move the region_obligations
processing code into InferCtxt
This commit is contained in:
parent
d73be851fb
commit
22cd041ba0
@ -55,6 +55,7 @@
|
||||
pub mod lattice;
|
||||
mod lub;
|
||||
pub mod region_inference;
|
||||
mod region_obligations;
|
||||
pub mod resolve;
|
||||
mod freshen;
|
||||
mod sub;
|
||||
@ -160,6 +161,13 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
// regionck to be sure that it has found *all* the region
|
||||
// obligations (otherwise, it's easy to fail to walk to a
|
||||
// particular node-id).
|
||||
//
|
||||
// Before running `resolve_regions_and_report_errors`, the creator
|
||||
// of the inference context is expected to invoke
|
||||
// `process_region_obligations` (defined in `self::region_obligations`)
|
||||
// for each body-id in this map, which will process the
|
||||
// obligations within. This is expected to be done 'late enough'
|
||||
// that all type inference variables have been bound and so forth.
|
||||
region_obligations: RefCell<NodeMap<Vec<RegionObligation<'tcx>>>>,
|
||||
}
|
||||
|
||||
@ -984,33 +992,6 @@ pub fn region_outlives_predicate(&self,
|
||||
})
|
||||
}
|
||||
|
||||
/// Registers that the given region obligation must be resolved
|
||||
/// from within the scope of `body_id`. These regions are enqueued
|
||||
/// and later processed by regionck, when full type information is
|
||||
/// available (see `region_obligations` field for more
|
||||
/// information).
|
||||
pub fn register_region_obligation(&self,
|
||||
body_id: ast::NodeId,
|
||||
obligation: RegionObligation<'tcx>)
|
||||
{
|
||||
self.region_obligations.borrow_mut().entry(body_id)
|
||||
.or_insert(vec![])
|
||||
.push(obligation);
|
||||
}
|
||||
|
||||
/// Get the region obligations that must be proven (during
|
||||
/// `regionck`) for the given `body_id` (removing them from the
|
||||
/// map as a side-effect).
|
||||
pub fn take_region_obligations(&self,
|
||||
body_id: ast::NodeId)
|
||||
-> Vec<RegionObligation<'tcx>>
|
||||
{
|
||||
match self.region_obligations.borrow_mut().remove(&body_id) {
|
||||
None => vec![],
|
||||
Some(vec) => vec,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
|
||||
self.type_variables
|
||||
.borrow_mut()
|
||||
|
@ -1,27 +1,210 @@
|
||||
//! Temporary holding spot for some code I want to factor out.
|
||||
//! Code that handles "type-outlives" constraints like `T: 'a`. This
|
||||
//! is based on the `outlives_components` function defined on the tcx,
|
||||
//! but it adds a bit of heuristics on top, in particular to deal with
|
||||
//! associated types and projections.
|
||||
//!
|
||||
//! When we process a given `T: 'a` obligation, we may produce two
|
||||
//! kinds of constraints for the region inferencer:
|
||||
//!
|
||||
//! - Relationships between inference variables and other regions.
|
||||
//! For example, if we have `&'?0 u32: 'a`, then we would produce
|
||||
//! a constraint that `'a <= '?0`.
|
||||
//! - "Verifys" that must be checked after inferencing is done.
|
||||
//! For example, if we know that, for some type parameter `T`,
|
||||
//! `T: 'a + 'b`, and we have a requirement that `T: '?1`,
|
||||
//! then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`.
|
||||
//! - Note the difference with the previous case: here, the region
|
||||
//! variable must be less than something else, so this doesn't
|
||||
//! affect how inference works (it finds the smallest region that
|
||||
//! will do); it's just a post-condition that we have to check.
|
||||
//!
|
||||
//! **The key point is that once this function is done, we have
|
||||
//! reduced all of our "type-region outlives" obligations into relationships
|
||||
//! between individual regions.**
|
||||
//!
|
||||
//! One key input to this function is the set of "region-bound pairs".
|
||||
//! These are basically the relationships between type parameters and
|
||||
//! regions that are in scope at the point where the outlives
|
||||
//! obligation was incurred. **When type-checking a function,
|
||||
//! particularly in the face of closures, this is not known until
|
||||
//! regionck runs!** This is because some of those bounds come
|
||||
//! from things we have yet to infer.
|
||||
//!
|
||||
//! Consider:
|
||||
//!
|
||||
//! ```
|
||||
//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T));
|
||||
//! fn foo<T>(x: T) {
|
||||
//! bar(x, |y| { ... })
|
||||
//! // ^ closure arg
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Here, the type of `y` may involve inference variables and the
|
||||
//! like, and it may also contain implied bounds that are needed to
|
||||
//! type-check the closure body (e.g., here it informs us that `T`
|
||||
//! outlives the late-bound region `'a`).
|
||||
//!
|
||||
//! > That said, in writing this, I have come to wonder: this
|
||||
//! inference dependency, I think, is only interesting for
|
||||
//! late-bound regions in the closure -- if the region appears free
|
||||
//! in the closure signature, then the relationship must be known to
|
||||
//! the caller (here, `foo`), and hence could be verified earlier
|
||||
//! up. Moreover, we infer late-bound regions quite early on right
|
||||
//! now, i.e., only when the expected signature is known. So we
|
||||
//! *may* be able to sidestep this. Regardless, once the NLL
|
||||
//! transition is complete, this concern will be gone. -nmatsakis
|
||||
|
||||
use rustc::traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations};
|
||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::infer::{self, GenericKind, InferCtxt, InferOk, VerifyBound};
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc::ty::outlives::Component;
|
||||
use infer::{self, GenericKind, InferCtxt, InferOk, RegionObligation, SubregionOrigin, VerifyBound};
|
||||
use traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations};
|
||||
use ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use ty::subst::Subst;
|
||||
use ty::outlives::Component;
|
||||
use syntax::ast;
|
||||
use syntax_pos::Span;
|
||||
|
||||
pub struct RegionckOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
// Context provided by the caller:
|
||||
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
|
||||
/// Registers that the given region obligation must be resolved
|
||||
/// from within the scope of `body_id`. These regions are enqueued
|
||||
/// and later processed by regionck, when full type information is
|
||||
/// available (see `region_obligations` field for more
|
||||
/// information).
|
||||
pub fn register_region_obligation(
|
||||
&self,
|
||||
body_id: ast::NodeId,
|
||||
obligation: RegionObligation<'tcx>,
|
||||
) {
|
||||
self.region_obligations
|
||||
.borrow_mut()
|
||||
.entry(body_id)
|
||||
.or_insert(vec![])
|
||||
.push(obligation);
|
||||
}
|
||||
|
||||
/// Process the region obligations that must be proven (during
|
||||
/// `regionck`) for the given `body_id`, given information about
|
||||
/// the region bounds in scope and so forth. This function must be
|
||||
/// invoked for all relevant body-ids before region inference is
|
||||
/// done (or else an assert will fire).
|
||||
///
|
||||
/// See the `region_obligations` field of `InferCtxt` for some
|
||||
/// comments about how this funtion fits into the overall expected
|
||||
/// flow of the the inferencer. The key point is that it is
|
||||
/// invoked after all type-inference variables have been bound --
|
||||
/// towards the end of regionck. This also ensures that the
|
||||
/// region-bound-pairs are available (see comments above regarding
|
||||
/// closures).
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `region_bound_pairs`: the set of region bounds implied by
|
||||
/// the parameters and where-clauses. In particular, each pair
|
||||
/// `('a, K)` in this list tells us that the bounds in scope
|
||||
/// indicate that `K: 'a`, where `K` is either a generic
|
||||
/// parameter like `T` or a projection like `T::Item`.
|
||||
/// - `implicit_region_bound`: if some, this is a region bound
|
||||
/// that is considered to hold for all type parameters (the
|
||||
/// function body).
|
||||
/// - `param_env` is the parameter environment for the enclosing function.
|
||||
/// - `body_id` is the body-id whose region obligations are being
|
||||
/// processed.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// This function may have to perform normalizations, and hence it
|
||||
/// returns an `InferOk` with subobligations that must be
|
||||
/// processed.
|
||||
pub fn process_registered_region_obligations(
|
||||
&self,
|
||||
region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
) -> InferOk<'tcx, ()> {
|
||||
let region_obligations = match self.region_obligations.borrow_mut().remove(&body_id) {
|
||||
None => vec![],
|
||||
Some(vec) => vec,
|
||||
};
|
||||
|
||||
let mut outlives = TypeOutlives::new(
|
||||
self,
|
||||
region_bound_pairs,
|
||||
implicit_region_bound,
|
||||
param_env,
|
||||
body_id,
|
||||
);
|
||||
|
||||
for RegionObligation {
|
||||
sup_type,
|
||||
sub_region,
|
||||
cause,
|
||||
} in region_obligations
|
||||
{
|
||||
let origin = SubregionOrigin::from_obligation_cause(
|
||||
&cause,
|
||||
|| infer::RelateParamBound(cause.span, sup_type),
|
||||
);
|
||||
|
||||
outlives.type_must_outlive(origin, sup_type, sub_region);
|
||||
}
|
||||
|
||||
InferOk {
|
||||
value: (),
|
||||
obligations: outlives.into_accrued_obligations(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a single ad-hoc region obligation that was not
|
||||
/// registered in advance.
|
||||
pub fn type_must_outlive(
|
||||
&self,
|
||||
region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
) -> InferOk<'tcx, ()> {
|
||||
let mut outlives = TypeOutlives::new(
|
||||
self,
|
||||
region_bound_pairs,
|
||||
implicit_region_bound,
|
||||
param_env,
|
||||
body_id,
|
||||
);
|
||||
outlives.type_must_outlive(origin, ty, region);
|
||||
InferOk {
|
||||
value: (),
|
||||
obligations: outlives.into_accrued_obligations(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ignore the region obligations for a given `body_id`, not bothering to
|
||||
/// prove them. This function should not really exist; it is used to accommodate some older
|
||||
/// code for the time being.
|
||||
pub fn ignore_region_obligations(&self, body_id: ast::NodeId) {
|
||||
self.region_obligations.borrow_mut().remove(&body_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =)
|
||||
struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
// See the comments on `process_registered_region_obligations` for the meaning
|
||||
// of these fields.
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
|
||||
// Obligations that we accrue as we go:
|
||||
/// These are sub-obligations that we accrue as we go; they result
|
||||
/// from any normalizations we had to do.
|
||||
obligations: PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> {
|
||||
pub fn new(
|
||||
impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> {
|
||||
fn new(
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
|
||||
implicit_region_bound: Option<ty::Region<'tcx>>,
|
||||
@ -38,6 +221,12 @@ pub fn new(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the obligations that accrued as a result of the
|
||||
/// `type_must_outlive` calls.
|
||||
fn into_accrued_obligations(self) -> PredicateObligations<'tcx> {
|
||||
self.obligations
|
||||
}
|
||||
|
||||
/// Adds constraints to inference such that `T: 'a` holds (or
|
||||
/// reports an error if it cannot).
|
||||
///
|
||||
@ -46,22 +235,7 @@ pub fn new(
|
||||
/// - `origin`, the reason we need this constraint
|
||||
/// - `ty`, the type `T`
|
||||
/// - `region`, the region `'a`
|
||||
pub fn type_must_outlive(
|
||||
mut self,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
region: ty::Region<'tcx>,
|
||||
) -> InferOk<'tcx, ()> {
|
||||
self.type_must_outlive_pushing_obligations(origin, ty, region);
|
||||
InferOk {
|
||||
value: (),
|
||||
obligations: self.obligations,
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal helper: ensure that `ty_must_outlive` and push obligations onto
|
||||
/// our internal vector.
|
||||
fn type_must_outlive_pushing_obligations(
|
||||
fn type_must_outlive(
|
||||
&mut self,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
@ -199,7 +373,7 @@ fn projection_must_outlive(
|
||||
debug!("projection_must_outlive: no declared bounds");
|
||||
|
||||
for component_ty in projection_ty.substs.types() {
|
||||
self.type_must_outlive_pushing_obligations(origin.clone(), component_ty, region);
|
||||
self.type_must_outlive(origin.clone(), component_ty, region);
|
||||
}
|
||||
|
||||
for r in projection_ty.substs.regions() {
|
@ -543,7 +543,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
// it, and it would take some refactoring to stop doing so.
|
||||
// (In particular, the needed methods currently live in
|
||||
// regionck.) -nmatsakis
|
||||
let _ = infcx.take_region_obligations(body_id);
|
||||
let _ = infcx.ignore_region_obligations(body_id);
|
||||
|
||||
infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions);
|
||||
let predicates = match infcx.fully_resolve(&predicates) {
|
||||
|
@ -137,7 +137,6 @@
|
||||
pub mod _match;
|
||||
pub mod writeback;
|
||||
mod regionck;
|
||||
mod regionck_outlives;
|
||||
pub mod coercion;
|
||||
pub mod demand;
|
||||
pub mod method;
|
||||
|
@ -90,9 +90,8 @@
|
||||
use middle::region;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, Ty, TypeFoldable};
|
||||
use rustc::infer::{self, GenericKind, SubregionOrigin};
|
||||
use rustc::infer::{self, InferOk, GenericKind};
|
||||
use rustc::ty::adjustment;
|
||||
use rustc::ty::outlives::Component;
|
||||
use rustc::ty::wf;
|
||||
@ -105,8 +104,6 @@
|
||||
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
|
||||
use rustc::hir::{self, PatKind};
|
||||
|
||||
use super::regionck_outlives::RegionckOutlives;
|
||||
|
||||
// a variation on try that just returns unit
|
||||
macro_rules! ignore_err {
|
||||
($e:expr) => (match $e { Ok(e) => e, Err(_) => return () })
|
||||
@ -360,28 +357,21 @@ fn visit_region_obligations(&mut self, node_id: ast::NodeId)
|
||||
// obligations. So make sure we process those.
|
||||
self.select_all_obligations_or_error();
|
||||
|
||||
// Make a copy of the region obligations vec because we'll need
|
||||
// to be able to borrow the fulfillment-cx below when projecting.
|
||||
let region_obligations = self.infcx.take_region_obligations(node_id);
|
||||
let InferOk { value: (), obligations } =
|
||||
self.infcx.process_registered_region_obligations(
|
||||
&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
self.body_id);
|
||||
|
||||
for r_o in ®ion_obligations {
|
||||
debug!("visit_region_obligations: r_o={:?} cause={:?}",
|
||||
r_o, r_o.cause);
|
||||
let sup_type = self.resolve_type(r_o.sup_type);
|
||||
let origin = self.code_to_origin(&r_o.cause, sup_type);
|
||||
self.type_must_outlive(origin, sup_type, r_o.sub_region);
|
||||
}
|
||||
|
||||
// Processing the region obligations should not cause the list to grow further:
|
||||
assert!(self.infcx.take_region_obligations(node_id).is_empty());
|
||||
}
|
||||
|
||||
fn code_to_origin(&self,
|
||||
cause: &traits::ObligationCause<'tcx>,
|
||||
sup_type: Ty<'tcx>)
|
||||
-> SubregionOrigin<'tcx> {
|
||||
SubregionOrigin::from_obligation_cause(cause,
|
||||
|| infer::RelateParamBound(cause.span, sup_type))
|
||||
// TODO -- It feels like we ought to loop here; these new
|
||||
// obligations, when selected, could cause the list of region
|
||||
// obligations to grow further. Fortunately, I believe that if
|
||||
// that happens it will at least lead to an ICE today, because
|
||||
// `resolve_regions_and_report_errors` (which runs after *all*
|
||||
// obligations have been selected) will assert that there are
|
||||
// no unsolved region obligations.
|
||||
self.register_predicates(obligations);
|
||||
}
|
||||
|
||||
/// This method populates the region map's `free_region_map`. It walks over the transformed
|
||||
@ -1147,12 +1137,14 @@ pub fn type_must_outlive(&self,
|
||||
ty: Ty<'tcx>,
|
||||
region: ty::Region<'tcx>)
|
||||
{
|
||||
let outlives = RegionckOutlives::new(&self.infcx,
|
||||
&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
self.body_id);
|
||||
self.register_infer_ok_obligations(outlives.type_must_outlive(origin, ty, region));
|
||||
let infer_ok = self.infcx.type_must_outlive(&self.region_bound_pairs,
|
||||
self.implicit_region_bound,
|
||||
self.param_env,
|
||||
self.body_id,
|
||||
origin,
|
||||
ty,
|
||||
region);
|
||||
self.register_infer_ok_obligations(infer_ok)
|
||||
}
|
||||
|
||||
/// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the
|
||||
|
Loading…
Reference in New Issue
Block a user