mirror of
https://github.com/rust-lang/rust
synced 2024-09-15 22:50:55 +00:00
move implied_bounds
into regionck
This commit is contained in:
parent
ff9f2d2ae9
commit
a65d8d3d71
|
@ -18,7 +18,6 @@
|
|||
use hir::def_id::DefId;
|
||||
use middle::region::RegionMaps;
|
||||
use ty::{self, Lift, TyCtxt, Region};
|
||||
use ty::wf::ImpliedBound;
|
||||
use rustc_data_structures::transitive_relation::TransitiveRelation;
|
||||
|
||||
/// Combines a `RegionMaps` (which governs relationships between
|
||||
|
@ -136,23 +135,6 @@ pub fn is_empty(&self) -> bool {
|
|||
self.relation.is_empty()
|
||||
}
|
||||
|
||||
pub fn relate_free_regions_from_implied_bounds(&mut self,
|
||||
implied_bounds: &[ImpliedBound<'tcx>])
|
||||
{
|
||||
debug!("relate_free_regions_from_implied_bounds()");
|
||||
for implied_bound in implied_bounds {
|
||||
debug!("implied bound: {:?}", implied_bound);
|
||||
match *implied_bound {
|
||||
ImpliedBound::RegionSubRegion(a, b) => {
|
||||
self.relate_regions(a, b);
|
||||
}
|
||||
ImpliedBound::RegionSubParam(..) |
|
||||
ImpliedBound::RegionSubProjection(..) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn relate_free_regions_from_predicates(&mut self,
|
||||
predicates: &[ty::Predicate<'tcx>]) {
|
||||
debug!("relate_free_regions_from_predicates(predicates={:?})", predicates);
|
||||
|
@ -177,7 +159,7 @@ pub fn relate_free_regions_from_predicates(&mut self,
|
|||
|
||||
// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
|
||||
// (with the exception that `'static: 'x` is not notable)
|
||||
fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
|
||||
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
|
||||
if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) {
|
||||
self.relation.add(sub, sup)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
use hir::def_id::DefId;
|
||||
use infer::InferCtxt;
|
||||
use ty::outlives::Component;
|
||||
use ty::subst::Substs;
|
||||
use traits;
|
||||
use ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
|
||||
|
@ -107,133 +106,6 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
|||
wf.normalize()
|
||||
}
|
||||
|
||||
/// Implied bounds are region relationships that we deduce
|
||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||
/// function's argument types are well-formed immediately before
|
||||
/// calling that fn, and hence the *callee* can assume that its
|
||||
/// argument types are well-formed. This may imply certain relationships
|
||||
/// between generic parameters. For example:
|
||||
///
|
||||
/// fn foo<'a,T>(x: &'a T)
|
||||
///
|
||||
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
|
||||
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
|
||||
#[derive(Debug)]
|
||||
pub enum ImpliedBound<'tcx> {
|
||||
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
|
||||
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
|
||||
}
|
||||
|
||||
/// Compute the implied bounds that a callee/impl can assume based on
|
||||
/// the fact that caller/projector has ensured that `ty` is WF. See
|
||||
/// the `ImpliedBound` type for more details.
|
||||
pub fn implied_bounds<'a, 'gcx, 'tcx>(
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
ty: Ty<'tcx>,
|
||||
span: Span)
|
||||
-> Vec<ImpliedBound<'tcx>>
|
||||
{
|
||||
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||
// U: WF is required; in that case, we push U onto this stack and
|
||||
// process it next. Currently (at least) these resulting
|
||||
// predicates are always guaranteed to be a subset of the original
|
||||
// type, so we need not fear non-termination.
|
||||
let mut wf_types = vec![ty];
|
||||
|
||||
let mut implied_bounds = vec![];
|
||||
|
||||
while let Some(ty) = wf_types.pop() {
|
||||
// Compute the obligations for `ty` to be well-formed. If `ty` is
|
||||
// an unresolved inference variable, just substituted an empty set
|
||||
// -- because the return type here is going to be things we *add*
|
||||
// to the environment, it's always ok for this set to be smaller
|
||||
// than the ultimate set. (Note: normally there won't be
|
||||
// unresolved inference variables here anyway, but there might be
|
||||
// during typeck under some circumstances.)
|
||||
let obligations = obligations(infcx, param_env, body_id, ty, span).unwrap_or(vec![]);
|
||||
|
||||
// From the full set of obligations, just filter down to the
|
||||
// region relationships.
|
||||
implied_bounds.extend(
|
||||
obligations
|
||||
.into_iter()
|
||||
.flat_map(|obligation| {
|
||||
assert!(!obligation.has_escaping_regions());
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::Subtype(..) |
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::ObjectSafe(..) =>
|
||||
vec![],
|
||||
|
||||
ty::Predicate::WellFormed(subty) => {
|
||||
wf_types.push(subty);
|
||||
vec![]
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref data) =>
|
||||
match infcx.tcx.no_late_bound_regions(data) {
|
||||
None =>
|
||||
vec![],
|
||||
Some(ty::OutlivesPredicate(r_a, r_b)) =>
|
||||
vec![ImpliedBound::RegionSubRegion(r_b, r_a)],
|
||||
},
|
||||
|
||||
ty::Predicate::TypeOutlives(ref data) =>
|
||||
match infcx.tcx.no_late_bound_regions(data) {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
|
||||
let ty_a = infcx.resolve_type_vars_if_possible(&ty_a);
|
||||
let components = infcx.tcx.outlives_components(ty_a);
|
||||
implied_bounds_from_components(r_b, components)
|
||||
}
|
||||
},
|
||||
}}));
|
||||
}
|
||||
|
||||
implied_bounds
|
||||
}
|
||||
|
||||
/// When we have an implied bound that `T: 'a`, we can further break
|
||||
/// this down to determine what relationships would have to hold for
|
||||
/// `T: 'a` to hold. We get to assume that the caller has validated
|
||||
/// those relationships.
|
||||
fn implied_bounds_from_components<'tcx>(sub_region: ty::Region<'tcx>,
|
||||
sup_components: Vec<Component<'tcx>>)
|
||||
-> Vec<ImpliedBound<'tcx>>
|
||||
{
|
||||
sup_components
|
||||
.into_iter()
|
||||
.flat_map(|component| {
|
||||
match component {
|
||||
Component::Region(r) =>
|
||||
vec![ImpliedBound::RegionSubRegion(sub_region, r)],
|
||||
Component::Param(p) =>
|
||||
vec![ImpliedBound::RegionSubParam(sub_region, p)],
|
||||
Component::Projection(p) =>
|
||||
vec![ImpliedBound::RegionSubProjection(sub_region, p)],
|
||||
Component::EscapingProjection(_) =>
|
||||
// If the projection has escaping regions, don't
|
||||
// try to infer any implied bounds even for its
|
||||
// free components. This is conservative, because
|
||||
// the caller will still have to prove that those
|
||||
// free components outlive `sub_region`. But the
|
||||
// idea is that the WAY that the caller proves
|
||||
// that may change in the future and we want to
|
||||
// give ourselves room to get smarter here.
|
||||
vec![],
|
||||
Component::UnresolvedInferenceVariable(..) =>
|
||||
vec![],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct WfPredicates<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
|
|
|
@ -94,7 +94,8 @@
|
|||
use rustc::ty::{self, Ty, TypeFoldable};
|
||||
use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound};
|
||||
use rustc::ty::adjustment;
|
||||
use rustc::ty::wf::ImpliedBound;
|
||||
use rustc::ty::outlives::Component;
|
||||
use rustc::ty::wf;
|
||||
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
|
@ -196,6 +197,24 @@ pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
|||
|
||||
}
|
||||
|
||||
/// Implied bounds are region relationships that we deduce
|
||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||
/// function's argument types are well-formed immediately before
|
||||
/// calling that fn, and hence the *callee* can assume that its
|
||||
/// argument types are well-formed. This may imply certain relationships
|
||||
/// between generic parameters. For example:
|
||||
///
|
||||
/// fn foo<'a,T>(x: &'a T)
|
||||
///
|
||||
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
|
||||
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
|
||||
#[derive(Debug)]
|
||||
enum ImpliedBound<'tcx> {
|
||||
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
|
||||
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Deref for RegionCtxt<'a, 'gcx, 'tcx> {
|
||||
type Target = FnCtxt<'a, 'gcx, 'tcx>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -386,11 +405,7 @@ fn relate_free_regions(&mut self,
|
|||
for &ty in fn_sig_tys {
|
||||
let ty = self.resolve_type(ty);
|
||||
debug!("relate_free_regions(t={:?})", ty);
|
||||
let implied_bounds =
|
||||
ty::wf::implied_bounds(self, self.fcx.param_env, body_id, ty, span);
|
||||
|
||||
// Record any relations between free regions that we observe into the free-region-map.
|
||||
self.free_region_map.relate_free_regions_from_implied_bounds(&implied_bounds);
|
||||
let implied_bounds = self.implied_bounds(body_id, ty, span);
|
||||
|
||||
// But also record other relationships, such as `T:'x`,
|
||||
// that don't go into the free-region-map but which we use
|
||||
|
@ -410,16 +425,18 @@ fn relate_free_regions(&mut self,
|
|||
ImpliedBound::RegionSubProjection(r_a, projection_b) => {
|
||||
self.region_bound_pairs.push((r_a, GenericKind::Projection(projection_b)));
|
||||
}
|
||||
ImpliedBound::RegionSubRegion(..) => {
|
||||
ImpliedBound::RegionSubRegion(r_a, r_b) => {
|
||||
// In principle, we could record (and take
|
||||
// advantage of) every relationship here, but
|
||||
// we are also free not to -- it simply means
|
||||
// strictly less that we can successfully type
|
||||
// check. (It may also be that we should
|
||||
// revise our inference system to be more
|
||||
// general and to make use of *every*
|
||||
// relationship that arises here, but
|
||||
// presently we do not.)
|
||||
// check. Right now we only look for things
|
||||
// relationships between free regions. (It may
|
||||
// also be that we should revise our inference
|
||||
// system to be more general and to make use
|
||||
// of *every* relationship that arises here,
|
||||
// but presently we do not.)
|
||||
self.free_region_map.relate_regions(r_a, r_b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -428,6 +445,112 @@ fn relate_free_regions(&mut self,
|
|||
debug!("<< relate_free_regions");
|
||||
}
|
||||
|
||||
/// Compute the implied bounds that a callee/impl can assume based on
|
||||
/// the fact that caller/projector has ensured that `ty` is WF. See
|
||||
/// the `ImpliedBound` type for more details.
|
||||
fn implied_bounds(&mut self, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span)
|
||||
-> Vec<ImpliedBound<'tcx>> {
|
||||
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||
// U: WF is required; in that case, we push U onto this stack and
|
||||
// process it next. Currently (at least) these resulting
|
||||
// predicates are always guaranteed to be a subset of the original
|
||||
// type, so we need not fear non-termination.
|
||||
let mut wf_types = vec![ty];
|
||||
|
||||
let mut implied_bounds = vec![];
|
||||
|
||||
while let Some(ty) = wf_types.pop() {
|
||||
// Compute the obligations for `ty` to be well-formed. If `ty` is
|
||||
// an unresolved inference variable, just substituted an empty set
|
||||
// -- because the return type here is going to be things we *add*
|
||||
// to the environment, it's always ok for this set to be smaller
|
||||
// than the ultimate set. (Note: normally there won't be
|
||||
// unresolved inference variables here anyway, but there might be
|
||||
// during typeck under some circumstances.)
|
||||
let obligations =
|
||||
wf::obligations(self, self.fcx.param_env, body_id, ty, span)
|
||||
.unwrap_or(vec![]);
|
||||
|
||||
// From the full set of obligations, just filter down to the
|
||||
// region relationships.
|
||||
implied_bounds.extend(
|
||||
obligations
|
||||
.into_iter()
|
||||
.flat_map(|obligation| {
|
||||
assert!(!obligation.has_escaping_regions());
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::Subtype(..) |
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::ObjectSafe(..) =>
|
||||
vec![],
|
||||
|
||||
ty::Predicate::WellFormed(subty) => {
|
||||
wf_types.push(subty);
|
||||
vec![]
|
||||
}
|
||||
|
||||
ty::Predicate::RegionOutlives(ref data) =>
|
||||
match self.tcx.no_late_bound_regions(data) {
|
||||
None =>
|
||||
vec![],
|
||||
Some(ty::OutlivesPredicate(r_a, r_b)) =>
|
||||
vec![ImpliedBound::RegionSubRegion(r_b, r_a)],
|
||||
},
|
||||
|
||||
ty::Predicate::TypeOutlives(ref data) =>
|
||||
match self.tcx.no_late_bound_regions(data) {
|
||||
None => vec![],
|
||||
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
|
||||
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
|
||||
let components = self.tcx.outlives_components(ty_a);
|
||||
self.implied_bounds_from_components(r_b, components)
|
||||
}
|
||||
},
|
||||
}}));
|
||||
}
|
||||
|
||||
implied_bounds
|
||||
}
|
||||
|
||||
/// When we have an implied bound that `T: 'a`, we can further break
|
||||
/// this down to determine what relationships would have to hold for
|
||||
/// `T: 'a` to hold. We get to assume that the caller has validated
|
||||
/// those relationships.
|
||||
fn implied_bounds_from_components(&self,
|
||||
sub_region: ty::Region<'tcx>,
|
||||
sup_components: Vec<Component<'tcx>>)
|
||||
-> Vec<ImpliedBound<'tcx>>
|
||||
{
|
||||
sup_components
|
||||
.into_iter()
|
||||
.flat_map(|component| {
|
||||
match component {
|
||||
Component::Region(r) =>
|
||||
vec![ImpliedBound::RegionSubRegion(sub_region, r)],
|
||||
Component::Param(p) =>
|
||||
vec![ImpliedBound::RegionSubParam(sub_region, p)],
|
||||
Component::Projection(p) =>
|
||||
vec![ImpliedBound::RegionSubProjection(sub_region, p)],
|
||||
Component::EscapingProjection(_) =>
|
||||
// If the projection has escaping regions, don't
|
||||
// try to infer any implied bounds even for its
|
||||
// free components. This is conservative, because
|
||||
// the caller will still have to prove that those
|
||||
// free components outlive `sub_region`. But the
|
||||
// idea is that the WAY that the caller proves
|
||||
// that may change in the future and we want to
|
||||
// give ourselves room to get smarter here.
|
||||
vec![],
|
||||
Component::UnresolvedInferenceVariable(..) =>
|
||||
vec![],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn resolve_regions_and_report_errors(&self) {
|
||||
self.fcx.resolve_regions_and_report_errors(self.subject_def_id,
|
||||
&self.region_maps,
|
||||
|
@ -1353,25 +1476,25 @@ pub fn type_must_outlive(&self,
|
|||
|
||||
fn components_must_outlive(&self,
|
||||
origin: infer::SubregionOrigin<'tcx>,
|
||||
components: Vec<ty::outlives::Component<'tcx>>,
|
||||
components: Vec<Component<'tcx>>,
|
||||
region: ty::Region<'tcx>)
|
||||
{
|
||||
for component in components {
|
||||
let origin = origin.clone();
|
||||
match component {
|
||||
ty::outlives::Component::Region(region1) => {
|
||||
Component::Region(region1) => {
|
||||
self.sub_regions(origin, region, region1);
|
||||
}
|
||||
ty::outlives::Component::Param(param_ty) => {
|
||||
Component::Param(param_ty) => {
|
||||
self.param_ty_must_outlive(origin, region, param_ty);
|
||||
}
|
||||
ty::outlives::Component::Projection(projection_ty) => {
|
||||
Component::Projection(projection_ty) => {
|
||||
self.projection_must_outlive(origin, region, projection_ty);
|
||||
}
|
||||
ty::outlives::Component::EscapingProjection(subcomponents) => {
|
||||
Component::EscapingProjection(subcomponents) => {
|
||||
self.components_must_outlive(origin, subcomponents, region);
|
||||
}
|
||||
ty::outlives::Component::UnresolvedInferenceVariable(v) => {
|
||||
Component::UnresolvedInferenceVariable(v) => {
|
||||
// ignore this, we presume it will yield an error
|
||||
// later, since if a type variable is not resolved by
|
||||
// this point it never will be
|
||||
|
|
Loading…
Reference in a new issue