Auto merge of #46319 - nikomatsakis:nll-master-to-rust-master-2, r=pnkfelix

NLL: improve inference with flow results, represent regions with bitsets, and more

This PR begins with a number of edits to the NLL code and then includes a large number of smaller refactorings (these refactorings ought not to change behavior). There are a lot of commits here, but each is individually simple. The goal is to land everything up to but not including the changes to how we handle closures, which are conceptually more complex.

The NLL specific changes are as follows (in order of appearance):

**Modify the region inferencer's approach to free regions.** Previously, for each free region (lifetime parameter) `'a`, it would compute the set of other free regions that `'a` outlives (e.g., if we have `where 'a: 'b`, then this set would be `{'a, 'b}`). Then it would mark those free regions as "constants" and report an error if inference tried to extend `'a` to include any other region (e.g., `'c`) that is not in that outlives set. In this way, the value of `'a` would never grow beyond the maximum that could type check. The new approach is to allow `'a` to grow larger. Then, after the fact, we check over the value of `'a` and see what other free regions it is required to outlive, and we check that those outlives relationships are justified by the where clauses in scope etc.

**Modify constraint generation to consider maybe-init.** When we have a "drop-live" variable `x` (i.e., a variable that will be dropped but will not be otherwise used), we now consider whether `x` is "maybe initialized" at that point. If not, then we know the drop is a no-op, and we can allow its regions to be dead. Due to limitations in the fragment code, this currently only works at the level of entire variables.

**Change representation of regions to use a `BitMatrix`.** We used to use a `BTreeSet`, which was rather silly. We now use a MxN matrix of bits, where `M` is the number of variables and `N` is the number of possible elements in each set (size of the CFG + number of free regions).

The remaining commits (starting from
extract the `implied_bounds` code into a helper function ") are all "no-op" refactorings, I believe.

~~One concern I have is with the commit "with -Zverbose, print all details of closure substs"; this commit seems to include some "internal" stuff in the mir-dump files, such as internal interner numbers, that I fear may vary by platform. Annoying. I guess we will see.~~ (I removed this commit.)

As for reviewer, @arielb1 has been reviewing the PRs, and they are certainly welcome to review this one too. But I figured it'd maybe be good to have more people taking a look and being familiar with this code, so I'll "nominate" @pnkfelix .

r? @pnkfelix
This commit is contained in:
bors 2017-12-04 19:07:52 +00:00
commit 8503b3ff82
63 changed files with 3288 additions and 2114 deletions

View File

@ -18,7 +18,7 @@ pub use ty::IntVarValue;
pub use self::freshen::TypeFreshener;
use hir::def_id::DefId;
use middle::free_region::{FreeRegionMap, RegionRelations};
use middle::free_region::RegionRelations;
use middle::region;
use middle::lang_items;
use mir::tcx::PlaceTy;
@ -44,6 +44,7 @@ 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::outlives::env::OutlivesEnvironment;
use self::type_variable::TypeVariableOrigin;
use self::unify_key::ToType;
@ -58,15 +59,13 @@ pub mod lattice;
mod lub;
pub mod region_constraints;
mod lexical_region_resolve;
mod outlives;
pub mod outlives;
pub mod resolve;
mod freshen;
mod sub;
pub mod type_variable;
pub mod unify_key;
pub use self::outlives::env::OutlivesEnvironment;
#[must_use]
pub struct InferOk<'tcx, T> {
pub value: T,
@ -1157,7 +1156,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
pub fn resolve_regions_and_report_errors(&self,
region_context: DefId,
region_map: &region::ScopeTree,
free_regions: &FreeRegionMap<'tcx>) {
outlives_env: &OutlivesEnvironment<'tcx>) {
assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(),
"region_obligations not empty: {:#?}",
self.region_obligations.borrow());
@ -1165,7 +1164,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let region_rels = &RegionRelations::new(self.tcx,
region_context,
region_map,
free_regions);
outlives_env.free_region_map());
let (var_origins, data) = self.region_constraints.borrow_mut()
.take()
.expect("regions already resolved")

View File

@ -0,0 +1,218 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use infer::InferCtxt;
use syntax::ast;
use syntax::codemap::Span;
use traits::FulfillmentContext;
use ty::{self, Ty, TypeFoldable};
use ty::outlives::Component;
use ty::wf;
/// Outlives bounds are relationships between generic parameters,
/// whether they both be regions (`'a: 'b`) or whether types are
/// involved (`T: 'a`). These relationships can be extracted from the
/// full set of predicates we understand or also from types (in which
/// case they are called implied bounds). They are fed to the
/// `OutlivesEnv` which in turn is supplied to the region checker and
/// other parts of the inference system.
#[derive(Debug)]
pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
}
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// 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`.
///
/// # Parameters
///
/// - `param_env`, the where-clauses in scope
/// - `body_id`, the body-id to use when normalizing assoc types.
/// Note that this may cause outlives obligations to be injected
/// into the inference context with this body-id.
/// - `ty`, the type that we are supposed to assume is WF.
/// - `span`, a span to use when normalizing, hopefully not important,
/// might be useful if a `bug!` occurs.
pub fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
body_id: ast::NodeId,
ty: Ty<'tcx>,
span: Span,
) -> Vec<OutlivesBound<'tcx>> {
let tcx = self.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![];
let mut fulfill_cx = FulfillmentContext::new();
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, param_env, body_id, ty, span).unwrap_or(vec![]);
// NB: All of these predicates *ought* to be easily proven
// true. In fact, their correctness is (mostly) implied by
// other parts of the program. However, in #42552, we had
// an annoying scenario where:
//
// - Some `T::Foo` gets normalized, resulting in a
// variable `_1` and a `T: Trait<Foo=_1>` constraint
// (not sure why it couldn't immediately get
// solved). This result of `_1` got cached.
// - These obligations were dropped on the floor here,
// rather than being registered.
// - Then later we would get a request to normalize
// `T::Foo` which would result in `_1` being used from
// the cache, but hence without the `T: Trait<Foo=_1>`
// constraint. As a result, `_1` never gets resolved,
// and we get an ICE (in dropck).
//
// Therefore, we register any predicates involving
// inference variables. We restrict ourselves to those
// involving inference variables both for efficiency and
// to avoids duplicate errors that otherwise show up.
fulfill_cx.register_predicate_obligations(
self,
obligations
.iter()
.filter(|o| o.predicate.has_infer_types())
.cloned(),
);
// 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(..) |
ty::Predicate::ConstEvaluatable(..) => vec![],
ty::Predicate::WellFormed(subty) => {
wf_types.push(subty);
vec![]
}
ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() {
None => vec![],
Some(ty::OutlivesPredicate(r_a, r_b)) => {
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
}
},
ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() {
None => vec![],
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
let components = tcx.outlives_components(ty_a);
Self::implied_bounds_from_components(r_b, components)
}
},
}
}));
}
// Ensure that those obligations that we had to solve
// get solved *here*.
match fulfill_cx.select_all_or_error(self) {
Ok(()) => (),
Err(errors) => self.report_fulfillment_errors(&errors, None),
}
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(
sub_region: ty::Region<'tcx>,
sup_components: Vec<Component<'tcx>>,
) -> Vec<OutlivesBound<'tcx>> {
sup_components
.into_iter()
.flat_map(|component| {
match component {
Component::Region(r) =>
vec![OutlivesBound::RegionSubRegion(sub_region, r)],
Component::Param(p) =>
vec![OutlivesBound::RegionSubParam(sub_region, p)],
Component::Projection(p) =>
vec![OutlivesBound::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()
}
}
pub fn explicit_outlives_bounds<'tcx>(
param_env: ty::ParamEnv<'tcx>,
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
debug!("explicit_outlives_bounds()");
param_env
.caller_bounds
.into_iter()
.filter_map(move |predicate| match predicate {
ty::Predicate::Projection(..) |
ty::Predicate::Trait(..) |
ty::Predicate::Equate(..) |
ty::Predicate::Subtype(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) |
ty::Predicate::ConstEvaluatable(..) => None,
ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map(
|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a),
),
})
}

View File

@ -8,12 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::free_region::FreeRegionMap;
use infer::{InferCtxt, GenericKind};
use traits::FulfillmentContext;
use ty::{self, Ty, TypeFoldable};
use ty::outlives::Component;
use ty::wf;
use infer::{GenericKind, InferCtxt};
use infer::outlives::free_region_map::FreeRegionMap;
use infer::outlives::bounds::{self, OutlivesBound};
use ty::{self, Ty};
use syntax::ast;
use syntax_pos::Span;
@ -44,34 +42,17 @@ pub struct OutlivesEnvironment<'tcx> {
region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
}
/// 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, 'tcx: 'a> OutlivesEnvironment<'tcx> {
pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
let mut free_region_map = FreeRegionMap::new();
free_region_map.relate_free_regions_from_predicates(&param_env.caller_bounds);
OutlivesEnvironment {
let mut env = OutlivesEnvironment {
param_env,
free_region_map,
free_region_map: FreeRegionMap::new(),
region_bound_pairs: vec![],
}
};
env.add_outlives_bounds(None, bounds::explicit_outlives_bounds(param_env));
env
}
/// Borrows current value of the `free_region_map`.
@ -163,193 +144,55 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
for &ty in fn_sig_tys {
let ty = infcx.resolve_type_vars_if_possible(&ty);
debug!("add_implied_bounds: ty = {}", ty);
let implied_bounds = self.implied_bounds(infcx, body_id, ty, span);
let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span);
self.add_outlives_bounds(Some(infcx), implied_bounds)
}
}
// But also record other relationships, such as `T:'x`,
// that don't go into the free-region-map but which we use
// here.
for implication in implied_bounds {
debug!("add_implied_bounds: implication={:?}", implication);
match implication {
ImpliedBound::RegionSubRegion(
r_a @ &ty::ReEarlyBound(_),
&ty::ReVar(vid_b),
) |
ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx.add_given(r_a, vid_b);
}
ImpliedBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Param(param_b)));
}
ImpliedBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Projection(projection_b)));
}
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. 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);
}
/// Processes outlives bounds that are known to hold, whether from implied or other sources.
///
/// The `infcx` parameter is optional; if the implied bounds may
/// contain inference variables, it must be supplied, in which
/// case we will register "givens" on the inference context. (See
/// `RegionConstraintData`.)
fn add_outlives_bounds<I>(
&mut self,
infcx: Option<&InferCtxt<'a, 'gcx, 'tcx>>,
outlives_bounds: I,
) where
I: IntoIterator<Item = OutlivesBound<'tcx>>,
{
// Record relationships such as `T:'x` that don't go into the
// free-region-map but which we use here.
for outlives_bound in outlives_bounds {
debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) |
OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
}
OutlivesBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Param(param_b)));
}
OutlivesBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Projection(projection_b)));
}
OutlivesBound::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. 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);
}
}
}
}
/// 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,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
body_id: ast::NodeId,
ty: Ty<'tcx>,
span: Span,
) -> Vec<ImpliedBound<'tcx>> {
let tcx = infcx.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![];
let mut fulfill_cx = FulfillmentContext::new();
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(infcx, self.param_env, body_id, ty, span).unwrap_or(vec![]);
// NB: All of these predicates *ought* to be easily proven
// true. In fact, their correctness is (mostly) implied by
// other parts of the program. However, in #42552, we had
// an annoying scenario where:
//
// - Some `T::Foo` gets normalized, resulting in a
// variable `_1` and a `T: Trait<Foo=_1>` constraint
// (not sure why it couldn't immediately get
// solved). This result of `_1` got cached.
// - These obligations were dropped on the floor here,
// rather than being registered.
// - Then later we would get a request to normalize
// `T::Foo` which would result in `_1` being used from
// the cache, but hence without the `T: Trait<Foo=_1>`
// constraint. As a result, `_1` never gets resolved,
// and we get an ICE (in dropck).
//
// Therefore, we register any predicates involving
// inference variables. We restrict ourselves to those
// involving inference variables both for efficiency and
// to avoids duplicate errors that otherwise show up.
fulfill_cx.register_predicate_obligations(
infcx,
obligations
.iter()
.filter(|o| o.predicate.has_infer_types())
.cloned());
// 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(..) |
ty::Predicate::ConstEvaluatable(..) => vec![],
ty::Predicate::WellFormed(subty) => {
wf_types.push(subty);
vec![]
}
ty::Predicate::RegionOutlives(ref data) => {
match 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 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 = tcx.outlives_components(ty_a);
self.implied_bounds_from_components(r_b, components)
}
}
}
}
}));
}
// Ensure that those obligations that we had to solve
// get solved *here*.
match fulfill_cx.select_all_or_error(infcx) {
Ok(()) => (),
Err(errors) => infcx.report_fulfillment_errors(&errors, None),
}
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()
}
}

View File

@ -0,0 +1,102 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use ty::{self, Lift, TyCtxt, Region};
use rustc_data_structures::transitive_relation::TransitiveRelation;
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct FreeRegionMap<'tcx> {
// Stores the relation `a < b`, where `a` and `b` are regions.
//
// Invariant: only free regions like `'x` or `'static` are stored
// in this relation, not scopes.
relation: TransitiveRelation<Region<'tcx>>
}
impl<'tcx> FreeRegionMap<'tcx> {
pub fn new() -> Self {
FreeRegionMap { relation: TransitiveRelation::new() }
}
pub fn is_empty(&self) -> bool {
self.relation.is_empty()
}
// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
// (with the exception that `'static: 'x` is not notable)
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
if is_free_or_static(sub) && is_free(sup) {
self.relation.add(sub, sup)
}
}
/// Tests whether `r_a <= r_b`. Both must be free regions or
/// `'static`.
pub fn sub_free_regions<'a, 'gcx>(&self,
r_a: Region<'tcx>,
r_b: Region<'tcx>)
-> bool {
assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
if let ty::ReStatic = r_b {
true // `'a <= 'static` is just always true, and not stored in the relation explicitly
} else {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}
}
/// Compute the least-upper-bound of two free regions. In some
/// cases, this is more conservative than necessary, in order to
/// avoid making arbitrary choices. See
/// `TransitiveRelation::postdom_upper_bound` for more details.
pub fn lub_free_regions<'a, 'gcx>(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
r_a: Region<'tcx>,
r_b: Region<'tcx>)
-> Region<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
let result = if r_a == r_b { r_a } else {
match self.relation.postdom_upper_bound(&r_a, &r_b) {
None => tcx.mk_region(ty::ReStatic),
Some(r) => *r,
}
};
debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
result
}
}
fn is_free(r: Region) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false
}
}
fn is_free_or_static(r: Region) -> bool {
match *r {
ty::ReStatic => true,
_ => is_free(r),
}
}
impl_stable_hash_for!(struct FreeRegionMap<'tcx> {
relation
});
impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
type Lifted = FreeRegionMap<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<FreeRegionMap<'tcx>> {
self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx))
.map(|relation| FreeRegionMap { relation })
}
}

View File

@ -8,5 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Various code related to computing outlives relations.
pub mod env;
pub mod free_region_map;
pub mod bounds;
mod obligations;

View File

@ -604,7 +604,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> {
predicates
.into_iter()
.filter_map(|p| p.as_ref().to_opt_type_outlives())
.filter_map(|p| self.tcx().no_late_bound_regions(&p))
.filter_map(|p| p.no_late_bound_regions())
.filter(|p| p.0 == ty)
.map(|p| p.1)
.collect()

View File

@ -15,10 +15,10 @@
//! `TransitiveRelation` type and use that to decide when one free
//! region outlives another and so forth.
use infer::outlives::free_region_map::FreeRegionMap;
use hir::def_id::DefId;
use middle::region;
use ty::{self, Lift, TyCtxt, Region};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use ty::{self, TyCtxt, Region};
/// Combines a `region::ScopeTree` (which governs relationships between
/// scopes) and a `FreeRegionMap` (which governs relationships between
@ -63,28 +63,28 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
-> bool {
let result = sub_region == super_region || {
match (sub_region, super_region) {
(&ty::ReEmpty, _) |
(_, &ty::ReStatic) =>
(ty::ReEmpty, _) |
(_, ty::ReStatic) =>
true,
(&ty::ReScope(sub_scope), &ty::ReScope(super_scope)) =>
self.region_scope_tree.is_subscope_of(sub_scope, super_scope),
(ty::ReScope(sub_scope), ty::ReScope(super_scope)) =>
self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope),
(&ty::ReScope(sub_scope), &ty::ReEarlyBound(ref br)) => {
(ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => {
let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
self.region_scope_tree.is_subscope_of(sub_scope, fr_scope)
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}
(&ty::ReScope(sub_scope), &ty::ReFree(ref fr)) => {
(ty::ReScope(sub_scope), ty::ReFree(fr)) => {
let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
self.region_scope_tree.is_subscope_of(sub_scope, fr_scope)
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}
(&ty::ReEarlyBound(_), &ty::ReEarlyBound(_)) |
(&ty::ReFree(_), &ty::ReEarlyBound(_)) |
(&ty::ReEarlyBound(_), &ty::ReFree(_)) |
(&ty::ReFree(_), &ty::ReFree(_)) =>
self.free_regions.sub_free_regions(&sub_region, &super_region),
(ty::ReEarlyBound(_), ty::ReEarlyBound(_)) |
(ty::ReFree(_), ty::ReEarlyBound(_)) |
(ty::ReEarlyBound(_), ty::ReFree(_)) |
(ty::ReFree(_), ty::ReFree(_)) =>
self.free_regions.sub_free_regions(sub_region, super_region),
_ =>
false,
@ -103,7 +103,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
ty::ReStatic => true,
ty::ReEarlyBound(_) | ty::ReFree(_) => {
let re_static = self.tcx.mk_region(ty::ReStatic);
self.free_regions.relation.contains(&re_static, &super_region)
self.free_regions.sub_free_regions(&re_static, &super_region)
}
_ => false
}
@ -117,121 +117,3 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
}
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct FreeRegionMap<'tcx> {
// Stores the relation `a < b`, where `a` and `b` are regions.
//
// Invariant: only free regions like `'x` or `'static` are stored
// in this relation, not scopes.
relation: TransitiveRelation<Region<'tcx>>
}
impl<'tcx> FreeRegionMap<'tcx> {
pub fn new() -> Self {
FreeRegionMap { relation: TransitiveRelation::new() }
}
pub fn is_empty(&self) -> bool {
self.relation.is_empty()
}
pub fn relate_free_regions_from_predicates(&mut self,
predicates: &[ty::Predicate<'tcx>]) {
debug!("relate_free_regions_from_predicates(predicates={:?})", predicates);
for predicate in predicates {
match *predicate {
ty::Predicate::Projection(..) |
ty::Predicate::Trait(..) |
ty::Predicate::Equate(..) |
ty::Predicate::Subtype(..) |
ty::Predicate::WellFormed(..) |
ty::Predicate::ObjectSafe(..) |
ty::Predicate::ClosureKind(..) |
ty::Predicate::TypeOutlives(..) |
ty::Predicate::ConstEvaluatable(..) => {
// No region bounds here
}
ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
self.relate_regions(r_b, r_a);
}
}
}
}
/// Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
/// (with the exception that `'static: 'x` is not notable)
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) {
self.relation.add(sub, sup)
}
}
/// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b`
/// must be free regions from the function header.
pub fn sub_free_regions<'a, 'gcx>(&self,
r_a: Region<'tcx>,
r_b: Region<'tcx>)
-> bool {
debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
let result = r_a == r_b || self.relation.contains(&r_a, &r_b);
debug!("sub_free_regions: result={}", result);
result
}
/// Compute the least-upper-bound of two free regions. In some
/// cases, this is more conservative than necessary, in order to
/// avoid making arbitrary choices. See
/// `TransitiveRelation::postdom_upper_bound` for more details.
pub fn lub_free_regions<'a, 'gcx>(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
r_a: Region<'tcx>,
r_b: Region<'tcx>)
-> Region<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
let result = if r_a == r_b { r_a } else {
match self.relation.postdom_upper_bound(&r_a, &r_b) {
None => tcx.mk_region(ty::ReStatic),
Some(r) => *r,
}
};
debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
result
}
/// Returns all regions that are known to outlive `r_a`. For
/// example, in a function:
///
/// ```
/// fn foo<'a, 'b: 'a, 'c: 'b>() { .. }
/// ```
///
/// if `r_a` represents `'a`, this function would return `{'b, 'c}`.
pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> {
assert!(is_free(r_a) || *r_a == ty::ReStatic);
self.relation.greater_than(&r_a)
}
}
fn is_free(r: Region) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false
}
}
impl_stable_hash_for!(struct FreeRegionMap<'tcx> {
relation
});
impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
type Lifted = FreeRegionMap<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<FreeRegionMap<'tcx>> {
self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx))
.map(|relation| FreeRegionMap { relation })
}
}

View File

@ -400,14 +400,14 @@ fn process_predicate<'a, 'gcx, 'tcx>(
ty::Predicate::TypeOutlives(ref binder) => {
// Check if there are higher-ranked regions.
match selcx.tcx().no_late_bound_regions(binder) {
match binder.no_late_bound_regions() {
// If there are, inspect the underlying type further.
None => {
// Convert from `Binder<OutlivesPredicate<Ty, Region>>` to `Binder<Ty>`.
let binder = binder.map_bound_ref(|pred| pred.0);
// Check if the type has any bound regions.
match selcx.tcx().no_late_bound_regions(&binder) {
match binder.no_late_bound_regions() {
// If so, this obligation is an error (for now). Eventually we should be
// able to support additional cases here, like `for<'a> &'a str: 'a`.
None => {

View File

@ -17,9 +17,9 @@ pub use self::ObligationCauseCode::*;
use hir;
use hir::def_id::DefId;
use infer::outlives::env::OutlivesEnvironment;
use middle::const_val::ConstEvalErr;
use middle::region;
use middle::free_region::FreeRegionMap;
use ty::subst::Substs;
use ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable, ToPredicate};
use ty::error::{ExpectedFound, TypeError};
@ -554,9 +554,13 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
predicates);
let region_scope_tree = region::ScopeTree::default();
let free_regions = FreeRegionMap::new();
infcx.resolve_regions_and_report_errors(region_context, &region_scope_tree, &free_regions);
// We can use the `elaborated_env` here; the region code only
// cares about declarations like `'a: 'b`.
let outlives_env = OutlivesEnvironment::new(elaborated_env);
infcx.resolve_regions_and_report_errors(region_context, &region_scope_tree, &outlives_env);
let predicates = match infcx.fully_resolve(&predicates) {
Ok(predicates) => predicates,
Err(fixup_err) => {

View File

@ -1559,7 +1559,7 @@ impl<'cx, 'gcx, 'tcx> ProjectionCacheKey<'tcx> {
let infcx = selcx.infcx();
// We don't do cross-snapshot caching of obligations with escaping regions,
// so there's no cache key to use
infcx.tcx.no_late_bound_regions(&predicate)
predicate.no_late_bound_regions()
.map(|predicate| ProjectionCacheKey {
// We don't attempt to match up with a specific type-variable state
// from a specific call to `opt_normalize_projection_type` - if

View File

@ -1834,7 +1834,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// T: Trait
// so it seems ok if we (conservatively) fail to accept that `Unsize`
// obligation above. Should be possible to extend this in the future.
let source = match self.tcx().no_late_bound_regions(&obligation.self_ty()) {
let source = match obligation.self_ty().no_late_bound_regions() {
Some(t) => t,
None => {
// Don't add any candidates if there are bound regions.
@ -2784,7 +2784,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// assemble_candidates_for_unsizing should ensure there are no late bound
// regions here. See the comment there for more details.
let source = self.infcx.shallow_resolve(
tcx.no_late_bound_regions(&obligation.self_ty()).unwrap());
obligation.self_ty().no_late_bound_regions().unwrap());
let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1);
let target = self.infcx.shallow_resolve(target);

View File

@ -23,10 +23,10 @@ use hir::map as hir_map;
use hir::map::DefPathHash;
use lint::{self, Lint};
use ich::{StableHashingContext, NodeIdHashingMode};
use infer::outlives::free_region_map::FreeRegionMap;
use middle::const_val::ConstVal;
use middle::cstore::{CrateStore, LinkMeta};
use middle::cstore::EncodedMetadata;
use middle::free_region::FreeRegionMap;
use middle::lang_items;
use middle::resolve_lifetime::{self, ObjectLifetimeDefault};
use middle::stability;

View File

@ -40,6 +40,7 @@
//! and does not need to visit anything else.
use middle::const_val::ConstVal;
use hir::def_id::DefId;
use ty::{self, Binder, Ty, TyCtxt, TypeFlags};
use std::fmt;
@ -246,7 +247,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
match *r {
ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
ty::ReLateBound(debruijn, _) if debruijn.depth <= self.current_depth => {
/* ignore bound regions */
}
_ => (self.callback)(r),
@ -329,6 +330,14 @@ struct RegionReplacer<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// Replace all regions bound by the given `Binder` with the
/// results returned by the closure; the closure is expected to
/// return a free region (relative to this binder), and hence the
/// binder is removed in the return type. The closure is invoked
/// once for each unique `BoundRegion`; multiple references to the
/// same `BoundRegion` will reuse the previous result. A map is
/// returned at the end with each bound region and the free region
/// that replaced it.
pub fn replace_late_bound_regions<T,F>(self,
value: &Binder<T>,
mut f: F)
@ -341,6 +350,22 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
(result, replacer.map)
}
/// Replace any late-bound regions bound in `value` with
/// free variants attached to `all_outlive_scope`.
pub fn liberate_late_bound_regions<T>(
&self,
all_outlive_scope: DefId,
value: &ty::Binder<T>
) -> T
where T: TypeFoldable<'tcx> {
self.replace_late_bound_regions(value, |br| {
self.mk_region(ty::ReFree(ty::FreeRegion {
scope: all_outlive_scope,
bound_region: br
}))
}).0
}
/// Flattens two binding levels into one. So `for<'a> for<'b> Foo`
/// becomes `for<'a,'b> Foo`.
pub fn flatten_late_bound_regions<T>(self, bound2_value: &Binder<Binder<T>>)
@ -364,16 +389,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Binder(value)
}
pub fn no_late_bound_regions<T>(self, value: &Binder<T>) -> Option<T>
where T : TypeFoldable<'tcx>
{
if value.0.has_escaping_regions() {
None
} else {
Some(value.0.clone())
}
}
/// Returns a set of all late-bound regions that are constrained
/// by `value`, meaning that if we instantiate those LBR with
/// variables and equate `value` with something else, those

View File

@ -1921,6 +1921,12 @@ impl<'a, 'gcx, 'tcx> FieldDef {
}
}
/// Represents the various closure traits in the Rust language. This
/// will determine the type of the environment (`self`, in the
/// desuaring) argument that the closure expects.
///
/// You can get the environment type of a closure using
/// `tcx.closure_env_ty()`.
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub enum ClosureKind {
// Warning: Ordering is significant here! The ordering is chosen

View File

@ -680,6 +680,26 @@ impl<T> Binder<T> {
{
ty::Binder(f(self.0))
}
/// Unwraps and returns the value within, but only if it contains
/// no bound regions at all. (In other words, if this binder --
/// and indeed any enclosing binder -- doesn't bind anything at
/// all.) Otherwise, returns `None`.
///
/// (One could imagine having a method that just unwraps a single
/// binder, but permits late-bound regions bound by enclosing
/// binders, but that would require adjusting the debruijn
/// indices, and given the shallow binding structure we often use,
/// would not be that useful.)
pub fn no_late_bound_regions<'tcx>(self) -> Option<T>
where T : TypeFoldable<'tcx>
{
if self.skip_binder().has_escaping_regions() {
None
} else {
Some(self.skip_binder().clone())
}
}
}
/// Represents the projection of an associated type. In explicit UFCS

View File

@ -624,6 +624,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr
}
/// Given the `DefId` of a fn or closure, returns the `DefId` of
/// the innermost fn item that the closure is contained within.
/// This is a significant def-id because, when we do
/// type-checking, we type-check this fn item and all of its
/// (transitive) closures together. Therefore, when we fetch the
/// `typeck_tables_of` the closure, for example, we really wind up
/// fetching the `typeck_tables_of` the enclosing fn item.
pub fn closure_base_def_id(self, def_id: DefId) -> DefId {
let mut def_id = def_id;
while self.is_closure(def_id) {
@ -634,6 +641,33 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
def_id
}
/// Given the def-id and substs a closure, creates the type of
/// `self` argument that the closure expects. For example, for a
/// `Fn` closure, this would return a reference type `&T` where
/// `T=closure_ty`.
///
/// Returns `None` if this closure's kind has not yet been inferred.
/// This should only be possible during type checking.
///
/// Note that the return value is a late-bound region and hence
/// wrapped in a binder.
pub fn closure_env_ty(self,
closure_def_id: DefId,
closure_substs: ty::ClosureSubsts<'tcx>)
-> Option<ty::Binder<Ty<'tcx>>>
{
let closure_ty = self.mk_closure(closure_def_id, closure_substs);
let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
let closure_kind_ty = closure_substs.closure_kind_ty(closure_def_id, self);
let closure_kind = closure_kind_ty.to_opt_closure_kind()?;
let env_ty = match closure_kind {
ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty),
ty::ClosureKind::FnMut => self.mk_mut_ref(self.mk_region(env_region), closure_ty),
ty::ClosureKind::FnOnce => closure_ty,
};
Some(ty::Binder(env_ty))
}
/// Given the def-id of some item that has no type parameters, make
/// a suitable "empty substs" for it.
pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> &'tcx ty::Substs<'tcx> {

View File

@ -134,12 +134,13 @@ impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
}
}
/// Returns a vector of all things greater than `a`.
/// Thinking of `x R y` as an edge `x -> y` in a graph, this
/// returns all things reachable from `a`.
///
/// Really this probably ought to be `impl Iterator<Item=&T>`, but
/// I'm too lazy to make that work, and -- given the caching
/// strategy -- it'd be a touch tricky anyhow.
pub fn greater_than(&self, a: &T) -> Vec<&T> {
pub fn reachable_from(&self, a: &T) -> Vec<&T> {
match self.index(a) {
Some(a) => self.with_closure(|closure| {
closure.iter(a.0).map(|i| &self.elements[i]).collect()
@ -184,7 +185,14 @@ impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
/// b -> b1
/// ```
pub fn postdom_upper_bound(&self, a: &T, b: &T) -> Option<&T> {
let mut mubs = self.minimal_upper_bounds(a, b);
let mubs = self.minimal_upper_bounds(a, b);
self.mutual_immediate_postdominator(mubs)
}
/// Viewing the relation as a graph, computes the "mutual
/// immediate postdominator" of a set of points (if one
/// exists). See `postdom_upper_bound` for details.
pub fn mutual_immediate_postdominator<'a>(&'a self, mut mubs: Vec<&'a T>) -> Option<&'a T> {
loop {
match mubs.len() {
0 => return None,
@ -276,6 +284,8 @@ impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
// After step 3, we know that no element can reach any of
// its predecesssors (because of step 2) nor successors
// (because we just called `pare_down`)
//
// This same algorithm is used in `parents` below.
let mut candidates = closure.intersection(a.0, b.0); // (1)
pare_down(&mut candidates, closure); // (2)
@ -290,6 +300,59 @@ impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
.collect()
}
/// Given an element A, returns the maximal set {B} of elements B
/// such that
///
/// - A != B
/// - A R B is true
/// - for each i, j: B[i] R B[j] does not hold
///
/// The intuition is that this moves "one step up" through a lattice
/// (where the relation is encoding the `<=` relation for the lattice).
/// So e.g. if the relation is `->` and we have
///
/// ```
/// a -> b -> d -> f
/// | ^
/// +--> c -> e ---+
/// ```
///
/// then `parents(a)` returns `[b, c]`. The `postdom_parent` function
/// would further reduce this to just `f`.
pub fn parents(&self, a: &T) -> Vec<&T> {
let a = match self.index(a) {
Some(a) => a,
None => return vec![]
};
// Steal the algorithm for `minimal_upper_bounds` above, but
// with a slight tweak. In the case where `a R a`, we remove
// that from the set of candidates.
let ancestors = self.with_closure(|closure| {
let mut ancestors = closure.intersection(a.0, a.0);
// Remove anything that can reach `a`. If this is a
// reflexive relation, this will include `a` itself.
ancestors.retain(|&e| !closure.contains(e, a.0));
pare_down(&mut ancestors, closure); // (2)
ancestors.reverse(); // (3a)
pare_down(&mut ancestors, closure); // (3b)
ancestors
});
ancestors.into_iter()
.rev() // (4)
.map(|i| &self.elements[i])
.collect()
}
/// A "best" parent in some sense. See `parents` and
/// `postdom_upper_bound` for more details.
pub fn postdom_parent(&self, a: &T) -> Option<&T> {
self.mutual_immediate_postdominator(self.parents(a))
}
fn with_closure<OP, R>(&self, op: OP) -> R
where OP: FnOnce(&BitMatrix) -> R
{
@ -468,11 +531,17 @@ fn test_many_steps() {
}
#[test]
fn mubs_triange() {
fn mubs_triangle() {
// a -> tcx
// ^
// |
// b
let mut relation = TransitiveRelation::new();
relation.add("a", "tcx");
relation.add("b", "tcx");
assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"tcx"]);
assert_eq!(relation.parents(&"a"), vec![&"tcx"]);
assert_eq!(relation.parents(&"b"), vec![&"tcx"]);
}
#[test]
@ -498,6 +567,9 @@ fn mubs_best_choice1() {
relation.add("3", "2");
assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"2"]);
assert_eq!(relation.parents(&"0"), vec![&"2"]);
assert_eq!(relation.parents(&"2"), vec![&"1"]);
assert!(relation.parents(&"1").is_empty());
}
#[test]
@ -522,6 +594,9 @@ fn mubs_best_choice2() {
relation.add("3", "2");
assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1"]);
assert_eq!(relation.parents(&"0"), vec![&"1"]);
assert_eq!(relation.parents(&"1"), vec![&"2"]);
assert!(relation.parents(&"2").is_empty());
}
#[test]
@ -536,10 +611,15 @@ fn mubs_no_best_choice() {
relation.add("3", "2");
assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1", &"2"]);
assert_eq!(relation.parents(&"0"), vec![&"1", &"2"]);
assert_eq!(relation.parents(&"3"), vec![&"1", &"2"]);
}
#[test]
fn mubs_best_choice_scc() {
// in this case, 1 and 2 form a cycle; we pick arbitrarily (but
// consistently).
let mut relation = TransitiveRelation::new();
relation.add("0", "1");
relation.add("0", "2");
@ -551,6 +631,7 @@ fn mubs_best_choice_scc() {
relation.add("3", "2");
assert_eq!(relation.minimal_upper_bounds(&"0", &"3"), vec![&"1"]);
assert_eq!(relation.parents(&"0"), vec![&"1"]);
}
#[test]
@ -572,6 +653,8 @@ fn pdub_crisscross() {
assert_eq!(relation.minimal_upper_bounds(&"a", &"b"),
vec![&"a1", &"b1"]);
assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x"));
assert_eq!(relation.postdom_parent(&"a"), Some(&"x"));
assert_eq!(relation.postdom_parent(&"b"), Some(&"x"));
}
#[test]
@ -603,6 +686,9 @@ fn pdub_crisscross_more() {
assert_eq!(relation.minimal_upper_bounds(&"a1", &"b1"),
vec![&"a2", &"b2"]);
assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x"));
assert_eq!(relation.postdom_parent(&"a"), Some(&"x"));
assert_eq!(relation.postdom_parent(&"b"), Some(&"x"));
}
#[test]
@ -620,6 +706,11 @@ fn pdub_lub() {
assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"x"]);
assert_eq!(relation.postdom_upper_bound(&"a", &"b"), Some(&"x"));
assert_eq!(relation.postdom_parent(&"a"), Some(&"a1"));
assert_eq!(relation.postdom_parent(&"b"), Some(&"b1"));
assert_eq!(relation.postdom_parent(&"a1"), Some(&"x"));
assert_eq!(relation.postdom_parent(&"b1"), Some(&"x"));
}
#[test]
@ -721,3 +812,39 @@ fn mubs_scc_4() {
assert_eq!(relation.minimal_upper_bounds(&"a", &"b"), vec![&"c"]);
}
#[test]
fn parent() {
// An example that was misbehaving in the compiler.
//
// 4 -> 1 -> 3
// \ | /
// \ v /
// 2 -> 0
//
// plus a bunch of self-loops
//
// Here `->` represents `<=` and `0` is `'static`.
let pairs = vec![
(2, /*->*/ 0),
(2, /*->*/ 2),
(0, /*->*/ 0),
(0, /*->*/ 0),
(1, /*->*/ 0),
(1, /*->*/ 1),
(3, /*->*/ 0),
(3, /*->*/ 3),
(4, /*->*/ 0),
(4, /*->*/ 1),
(1, /*->*/ 3),
];
let mut relation = TransitiveRelation::new();
for (a, b) in pairs {
relation.add(a, b);
}
let p = relation.postdom_parent(&3);
assert_eq!(p, Some(&0));
}

View File

@ -17,7 +17,6 @@ use driver;
use rustc_lint;
use rustc_resolve::MakeGlobMap;
use rustc_trans;
use rustc::middle::free_region::FreeRegionMap;
use rustc::middle::region;
use rustc::middle::resolve_lifetime;
use rustc::ty::subst::{Kind, Subst};
@ -25,6 +24,7 @@ use rustc::traits::{ObligationCause, Reveal};
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::maps::OnDiskCache;
use rustc::infer::{self, InferOk, InferResult};
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc_metadata::cstore::CStore;
use rustc::hir::map as hir_map;
@ -162,14 +162,15 @@ fn test_env<F>(source_string: &str,
|tcx| {
tcx.infer_ctxt().enter(|infcx| {
let mut region_scope_tree = region::ScopeTree::default();
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
body(Env {
infcx: &infcx,
region_scope_tree: &mut region_scope_tree,
param_env: ty::ParamEnv::empty(Reveal::UserFacing),
param_env: param_env,
});
let free_regions = FreeRegionMap::new();
let outlives_env = OutlivesEnvironment::new(param_env);
let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID);
infcx.resolve_regions_and_report_errors(def_id, &region_scope_tree, &free_regions);
infcx.resolve_regions_and_report_errors(def_id, &region_scope_tree, &outlives_env);
assert_eq!(tcx.sess.err_count(), expected_err_count);
});
});

View File

@ -0,0 +1,337 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue};
use rustc::mir::visit::Visitor;
use rustc::mir::Place::Projection;
use rustc::mir::{Local, PlaceProjection, ProjectionElem};
use rustc::mir::visit::TyContext;
use rustc::infer::InferCtxt;
use rustc::traits::{self, ObligationCause};
use rustc::ty::{self, ClosureSubsts, Ty};
use rustc::ty::subst::Substs;
use rustc::ty::fold::TypeFoldable;
use rustc::util::common::ErrorReported;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use borrow_check::FlowInProgress;
use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::{HasMoveData, MoveData};
use super::LivenessResults;
use super::ToRegionVid;
use super::region_infer::RegionInferenceContext;
pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>,
param_env: ty::ParamEnv<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) {
let mut cg = ConstraintGeneration {
infcx,
regioncx,
mir,
liveness,
param_env,
flow_inits,
move_data,
};
for (bb, data) in mir.basic_blocks().iter_enumerated() {
cg.visit_basic_block_data(bb, data);
}
}
/// 'cg = the duration of the constraint generation process itself.
struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cg mut RegionInferenceContext<'tcx>,
mir: &'cg Mir<'tcx>,
liveness: &'cg LivenessResults,
param_env: ty::ParamEnv<'tcx>,
flow_inits: &'cg mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &'cg MoveData<'tcx>,
}
impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
self.add_liveness_constraints(bb);
self.super_basic_block_data(bb, data);
}
/// We sometimes have `substs` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_substs(&mut self, substs: &&'tcx Substs<'tcx>, location: Location) {
self.add_regular_live_constraint(*substs, location);
self.super_substs(substs);
}
/// We sometimes have `region` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) {
self.add_regular_live_constraint(*region, location);
self.super_region(region);
}
/// We sometimes have `ty` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_ty(&mut self, ty: &ty::Ty<'tcx>, ty_context: TyContext) {
match ty_context {
TyContext::ReturnTy(source_info) |
TyContext::LocalDecl { source_info, .. } => {
span_bug!(source_info.span,
"should not be visiting outside of the CFG: {:?}",
ty_context);
}
TyContext::Location(location) => {
self.add_regular_live_constraint(*ty, location);
}
}
self.super_ty(ty);
}
/// We sometimes have `closure_substs` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_closure_substs(&mut self, substs: &ClosureSubsts<'tcx>, location: Location) {
self.add_regular_live_constraint(*substs, location);
self.super_closure_substs(substs);
}
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
// Look for an rvalue like:
//
// & L
//
// where L is the path that is borrowed. In that case, we have
// to add the reborrow constraints (which don't fall out
// naturally from the type-checker).
if let Rvalue::Ref(region, _bk, ref borrowed_lv) = *rvalue {
self.add_reborrow_constraint(location, region, borrowed_lv);
}
self.super_rvalue(rvalue, location);
}
}
impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
/// Liveness constraints:
///
/// > If a variable V is live at point P, then all regions R in the type of V
/// > must include the point P.
fn add_liveness_constraints(&mut self, bb: BasicBlock) {
debug!("add_liveness_constraints(bb={:?})", bb);
self.liveness
.regular
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_regular_live_constraint(live_local_ty, location);
}
});
let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
self.liveness
.drop
.simulate_block(self.mir, bb, |location, live_locals| {
all_live_locals.push((location, live_locals.iter().collect()));
});
debug!(
"add_liveness_constraints: all_live_locals={:#?}",
all_live_locals
);
let terminator_index = self.mir.basic_blocks()[bb].statements.len();
self.flow_inits.reset_to_entry_of(bb);
while let Some((location, live_locals)) = all_live_locals.pop() {
for live_local in live_locals {
debug!(
"add_liveness_constraints: location={:?} live_local={:?}",
location,
live_local
);
self.flow_inits.each_state_bit(|mpi_init| {
debug!(
"add_liveness_constraints: location={:?} initialized={:?}",
location,
&self.flow_inits
.base_results
.operator()
.move_data()
.move_paths[mpi_init]
);
});
let mpi = self.move_data.rev_lookup.find_local(live_local);
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
debug!(
"add_liveness_constraints: mpi={:?} has initialized child {:?}",
self.move_data.move_paths[mpi],
self.move_data.move_paths[initialized_child]
);
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_drop_live_constraint(live_local_ty, location);
}
}
if location.statement_index == terminator_index {
debug!(
"add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
location
);
self.flow_inits.reconstruct_terminator_effect(location);
} else {
debug!(
"add_liveness_constraints: reconstruct_statement_effect from {:#?}",
location
);
self.flow_inits.reconstruct_statement_effect(location);
}
self.flow_inits.apply_local_effect();
}
}
/// Some variable with type `live_ty` is "regular live" at
/// `location` -- i.e., it may be used later. This means that all
/// regions appearing in the type `live_ty` must be live at
/// `location`.
fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
where
T: TypeFoldable<'tcx>,
{
debug!(
"add_regular_live_constraint(live_ty={:?}, location={:?})",
live_ty,
location
);
self.infcx
.tcx
.for_each_free_region(&live_ty, |live_region| {
let vid = live_region.to_region_vid();
self.regioncx.add_live_point(vid, location);
});
}
/// Some variable with type `live_ty` is "drop live" at `location`
/// -- i.e., it may be dropped later. This means that *some* of
/// the regions in its type must be live at `location`. The
/// precise set will depend on the dropck constraints, and in
/// particular this takes `#[may_dangle]` into account.
fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
debug!(
"add_drop_live_constraint(dropped_ty={:?}, location={:?})",
dropped_ty,
location
);
let tcx = self.infcx.tcx;
let mut types = vec![(dropped_ty, 0)];
let mut known = FxHashSet();
while let Some((ty, depth)) = types.pop() {
let span = DUMMY_SP; // FIXME
let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
Ok(result) => result,
Err(ErrorReported) => {
continue;
}
};
let ty::DtorckConstraint {
outlives,
dtorck_types,
} = result;
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for outlive in outlives {
self.add_regular_live_constraint(outlive, location);
}
// However, there may also be some types that
// `dtorck_constraint_for_ty` could not resolve (e.g.,
// associated types and parameters). We need to normalize
// associated types here and possibly recursively process.
for ty in dtorck_types {
let cause = ObligationCause::dummy();
// We know that our original `dropped_ty` is well-formed,
// so region obligations resulting from this normalization
// should always hold.
//
// Therefore we ignore them instead of trying to match
// them up with a location.
let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
match traits::fully_normalize_with_fulfillcx(
self.infcx, fulfillcx, cause, self.param_env, &ty
) {
Ok(ty) => match ty.sty {
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
self.add_regular_live_constraint(ty, location);
}
_ => if known.insert(ty) {
types.push((ty, depth + 1));
},
},
Err(errors) => {
self.infcx.report_fulfillment_errors(&errors, None);
}
}
}
}
}
fn add_reborrow_constraint(
&mut self,
location: Location,
borrow_region: ty::Region<'tcx>,
borrowed_place: &Place<'tcx>,
) {
if let Projection(ref proj) = *borrowed_place {
let PlaceProjection { ref base, ref elem } = **proj;
if let ProjectionElem::Deref = *elem {
let tcx = self.infcx.tcx;
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
let base_sty = &base_ty.sty;
if let ty::TyRef(base_region, ty::TypeAndMut { ty: _, mutbl }) = *base_sty {
match mutbl {
hir::Mutability::MutImmutable => {}
hir::Mutability::MutMutable => {
self.add_reborrow_constraint(location, borrow_region, base);
}
}
let span = self.mir.source_info(location).span;
self.regioncx.add_outlives(
span,
base_region.to_region_vid(),
borrow_region.to_region_vid(),
location.successor_within_block(),
);
}
}
}
}
}

View File

@ -17,34 +17,53 @@ use std::collections::BTreeSet;
use transform::MirSource;
use transform::type_check;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
use borrow_check::FlowInProgress;
use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::MoveData;
use util as mir_util;
use self::mir_util::PassWhere;
mod constraint_generation;
mod subtype_constraint_generation;
mod free_regions;
mod universal_regions;
use self::universal_regions::UniversalRegions;
pub(crate) mod region_infer;
use self::region_infer::RegionInferenceContext;
mod renumber;
/// Rewrites the regions in the MIR to use NLL variables, also
/// scraping out the set of free regions (e.g., region parameters)
/// declared on the function. That set will need to be given to
/// `compute_regions`.
pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
def_id: DefId,
mir: &mut Mir<'tcx>,
) -> UniversalRegions<'tcx> {
// Compute named region information.
let universal_regions = universal_regions::universal_regions(infcx, def_id);
// Replace all regions with fresh inference variables.
renumber::renumber_mir(infcx, &universal_regions, mir);
universal_regions
}
/// Computes the (non-lexical) regions from the input MIR.
///
/// This may result in errors being reported.
pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
def_id: DefId,
universal_regions: UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
param_env: ty::ParamEnv<'gcx>,
mir: &mut Mir<'tcx>,
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> RegionInferenceContext<'tcx> {
// Compute named region information.
let free_regions = &free_regions::free_regions(infcx, def_id);
// Replace all regions with fresh inference variables.
renumber::renumber_mir(infcx, free_regions, mir);
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
@ -52,8 +71,13 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// Create the region inference context, taking ownership of the region inference
// data that was contained in `infcx`.
let var_origins = infcx.take_region_var_origins();
let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir);
subtype_constraint_generation::generate(
&mut regioncx,
&universal_regions,
mir,
constraint_sets,
);
// Compute what is live where.
let liveness = &LivenessResults {
@ -75,7 +99,15 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
};
// Generate non-subtyping constraints.
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
constraint_generation::generate_constraints(
infcx,
&mut regioncx,
&mir,
param_env,
liveness,
flow_inits,
move_data,
);
// Solve the region constraints.
regioncx.solve(infcx, &mir);
@ -133,7 +165,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
match pass_where {
// Before the CFG, dump out the values for each region variable.
PassWhere::BeforeCFG => for region in regioncx.regions() {
writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?;
writeln!(out, "| {:?}: {}", region, regioncx.region_value_str(region))?;
},
// Before each basic block, dump out the values
@ -143,7 +175,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
writeln!(out, " | Live variables on entry to {:?}: {}", bb, s)?;
}
PassWhere::InCFG(location) => {
PassWhere::BeforeLocation(location) => {
let s = live_variable_set(
&regular_liveness_per_location[&location],
&drop_liveness_per_location[&location],
@ -151,7 +183,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
writeln!(out, " | Live variables at {:?}: {}", location, s)?;
}
PassWhere::AfterCFG => {}
PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
}
Ok(())
});

View File

@ -0,0 +1,553 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::universal_regions::UniversalRegions;
use rustc::infer::InferCtxt;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::region_constraints::VarOrigins;
use rustc::infer::outlives::free_region_map::FreeRegionMap;
use rustc::mir::{Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::bitvec::BitMatrix;
use rustc_data_structures::indexed_vec::Idx;
use std::collections::BTreeMap;
use std::fmt;
use syntax_pos::Span;
pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionVid`). The
/// definition contains information about where the region came
/// from as well as its final inferred value.
definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
/// The liveness constraints added to each region. For most
/// regions, these start out empty and steadily grow, though for
/// each universally quantified region R they start out containing
/// the entire CFG and `end(R)`.
///
/// In this `BitMatrix` representation, the rows are the region
/// variables and the columns are the free regions and MIR locations.
liveness_constraints: BitMatrix,
/// The final inferred values of the inference variables; `None`
/// until `solve` is invoked.
inferred_values: Option<BitMatrix>,
/// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>,
/// A map from each MIR Location to its column index in
/// `liveness_constraints`/`inferred_values`. (The first N columns are
/// the free regions.)
point_indices: BTreeMap<Location, usize>,
/// Number of universally quantified regions. This is used to
/// determine the meaning of the bits in `inferred_values` and
/// friends.
num_universal_regions: usize,
free_region_map: &'tcx FreeRegionMap<'tcx>,
}
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>>,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Constraint {
// NB. The ordering here is not significant for correctness, but
// it is for convenience. Before we dump the constraints in the
// debugging logs, we sort them, and we'd like the "super region"
// to be first, etc. (In particular, span should remain last.)
/// The region SUP must outlive SUB...
sup: RegionVid,
/// Region that must be outlived.
sub: RegionVid,
/// At this location.
point: Location,
/// Where did this constraint arise?
span: Span,
}
impl<'tcx> RegionInferenceContext<'tcx> {
/// Creates a new region inference context with a total of
/// `num_region_variables` valid inference variables; the first N
/// of those will be constant regions representing the free
/// regions defined in `universal_regions`.
pub fn new(
var_origins: VarOrigins,
universal_regions: &UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
) -> Self {
let num_region_variables = var_origins.len();
let num_universal_regions = universal_regions.indices.len();
let mut num_points = 0;
let mut point_indices = BTreeMap::new();
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
for statement_index in 0..block_data.statements.len() + 1 {
let location = Location {
block,
statement_index,
};
point_indices.insert(location, num_universal_regions + num_points);
num_points += 1;
}
}
// Create a RegionDefinition for each inference variable.
let definitions = var_origins
.into_iter()
.map(|origin| RegionDefinition::new(origin))
.collect();
let mut result = Self {
definitions,
liveness_constraints: BitMatrix::new(
num_region_variables,
num_universal_regions + num_points,
),
inferred_values: None,
constraints: Vec::new(),
point_indices,
num_universal_regions,
free_region_map: universal_regions.free_region_map,
};
result.init_universal_regions(universal_regions);
result
}
/// Initializes the region variables for each universally
/// quantified region (lifetime parameter). The first N variables
/// always correspond to the regions appearing in the function
/// signature (both named and anonymous) and where clauses. This
/// function iterates over those regions and initializes them with
/// minimum values.
///
/// For example:
///
/// fn foo<'a, 'b>(..) where 'a: 'b
///
/// would initialize two variables like so:
///
/// R0 = { CFG, R0 } // 'a
/// R1 = { CFG, R0, R1 } // 'b
///
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
/// and (b) any free regions that it outlives, which in this case
/// is just itself. R1 (`'b`) in contrast also outlives `'a` and
/// hence contains R0 and R1.
fn init_universal_regions(&mut self, universal_regions: &UniversalRegions<'tcx>) {
let UniversalRegions {
indices,
free_region_map: _,
} = universal_regions;
// For each universally quantified region X:
for (free_region, &variable) in indices {
// 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);
// Add all nodes in the CFG to liveness constraints
for (_location, point_index) in &self.point_indices {
self.liveness_constraints
.add(variable.index(), *point_index);
}
// Add `end(X)` into the set for X.
self.liveness_constraints
.add(variable.index(), variable.index());
}
}
/// Returns an iterator over all the region indices.
pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
self.definitions.indices()
}
/// Returns true if the region `r` contains the point `p`.
///
/// Panics if called before `solve()` executes,
pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool {
let inferred_values = self.inferred_values
.as_ref()
.expect("region values not yet inferred");
self.region_contains_point_in_matrix(inferred_values, r, p)
}
/// True if given region `r` contains the point `p`, when
/// evaluated in the set of region values `matrix`.
fn region_contains_point_in_matrix(
&self,
matrix: &BitMatrix,
r: RegionVid,
p: Location,
) -> bool {
let point_index = self.point_indices
.get(&p)
.expect("point index should be known");
matrix.contains(r.index(), *point_index)
}
/// True if given region `r` contains the `end(s)`, when
/// evaluated in the set of region values `matrix`.
fn region_contains_region_in_matrix(
&self,
matrix: &BitMatrix,
r: RegionVid,
s: RegionVid,
) -> bool {
matrix.contains(r.index(), s.index())
}
/// Returns access to the value of `r` for debugging purposes.
pub(super) fn region_value_str(&self, r: RegionVid) -> String {
let inferred_values = self.inferred_values
.as_ref()
.expect("region values not yet inferred");
let mut result = String::new();
result.push_str("{");
let mut sep = "";
for &point in self.point_indices.keys() {
if self.region_contains_point_in_matrix(inferred_values, r, point) {
result.push_str(&format!("{}{:?}", sep, point));
sep = ", ";
}
}
for fr in (0..self.num_universal_regions).map(RegionVid::new) {
if self.region_contains_region_in_matrix(inferred_values, r, fr) {
result.push_str(&format!("{}{:?}", sep, fr));
sep = ", ";
}
}
result.push_str("}");
result
}
/// Indicates that the region variable `v` is live at the point `point`.
pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool {
debug!("add_live_point({:?}, {:?})", v, point);
assert!(self.inferred_values.is_none(), "values already inferred");
let point_index = self.point_indices
.get(&point)
.expect("point index should be known");
self.liveness_constraints.add(v.index(), *point_index)
}
/// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
pub(super) fn add_outlives(
&mut self,
span: Span,
sup: RegionVid,
sub: RegionVid,
point: Location,
) {
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
assert!(self.inferred_values.is_none(), "values already inferred");
self.constraints.push(Constraint {
span,
sup,
sub,
point,
});
}
/// Perform region inference.
pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
assert!(self.inferred_values.is_none(), "values already inferred");
// Find the minimal regions that can solve the constraints. This is infallible.
self.propagate_constraints(mir);
// Now, see whether any of the constraints were too strong. In
// particular, we want to check for a case where a universally
// quantified region exceeded its bounds. Consider:
//
// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
//
// In this case, returning `x` requires `&'a u32 <: &'b u32`
// and hence we establish (transitively) a constraint that
// `'a: 'b`. The `propagate_constraints` code above will
// therefore add `end('a)` into the region for `'b` -- but we
// have no evidence that `'a` outlives `'b`, so we want to report
// an error.
// The universal regions are always found in a prefix of the
// full list.
let free_region_definitions = self.definitions
.iter_enumerated()
.take_while(|(_, fr_definition)| fr_definition.name.is_some());
for (fr, fr_definition) in free_region_definitions {
self.check_free_region(infcx, fr, fr_definition);
}
}
fn check_free_region(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
longer_fr: RegionVid,
longer_definition: &RegionDefinition<'tcx>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
let longer_name = longer_definition.name.unwrap();
let longer_value = inferred_values.iter(longer_fr.index());
// Find every region `shorter` such that `longer: shorter`
// (because `longer` includes `end(shorter)`).
for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) {
let shorter_fr = RegionVid::new(shorter_fr);
// `fr` includes `end(fr)`, that's not especially
// interesting.
if longer_fr == shorter_fr {
continue;
}
let shorter_definition = &self.definitions[shorter_fr];
let shorter_name = shorter_definition.name.unwrap();
// Check that `o <= fr`. If not, report an error.
if !self.free_region_map
.sub_free_regions(shorter_name, longer_name)
{
// FIXME: worst error msg ever
let blame_span = self.blame_span(longer_fr, shorter_fr);
infcx.tcx.sess.span_err(
blame_span,
&format!(
"free region `{}` does not outlive `{}`",
longer_name,
shorter_name
),
);
}
}
}
/// Propagate the region constraints: this will grow the values
/// 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>) {
let mut changed = true;
debug!("propagate_constraints()");
debug!("propagate_constraints: constraints={:#?}", {
let mut constraints: Vec<_> = self.constraints.iter().collect();
constraints.sort();
constraints
});
// The initial values for each region are derived from the liveness
// constraints we have accumulated.
let mut inferred_values = self.liveness_constraints.clone();
while changed {
changed = false;
debug!("propagate_constraints: --------------------");
for constraint in &self.constraints {
debug!("propagate_constraints: constraint={:?}", constraint);
// Grow the value as needed to accommodate the
// outlives constraint.
if self.copy(
&mut inferred_values,
mir,
constraint.sub,
constraint.sup,
constraint.point,
) {
debug!("propagate_constraints: sub={:?}", constraint.sub);
debug!("propagate_constraints: sup={:?}", constraint.sup);
changed = true;
}
}
debug!("\n");
}
self.inferred_values = Some(inferred_values);
}
fn copy(
&self,
inferred_values: &mut BitMatrix,
mir: &Mir<'tcx>,
from_region: RegionVid,
to_region: RegionVid,
start_point: Location,
) -> bool {
let mut changed = false;
let mut stack = vec![];
let mut visited = FxHashSet();
stack.push(start_point);
while let Some(p) = stack.pop() {
debug!(" copy: p={:?}", p);
if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
debug!(" not in from-region");
continue;
}
if !visited.insert(p) {
debug!(" already visited");
continue;
}
let point_index = self.point_indices.get(&p).unwrap();
changed |= inferred_values.add(to_region.index(), *point_index);
let block_data = &mir[p.block];
let successor_points = if p.statement_index < block_data.statements.len() {
vec![
Location {
statement_index: p.statement_index + 1,
..p
},
]
} else {
block_data
.terminator()
.successors()
.iter()
.map(|&basic_block| {
Location {
statement_index: 0,
block: basic_block,
}
})
.collect::<Vec<_>>()
};
if successor_points.is_empty() {
// If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_region`
// and make sure they are included in the `to_region`.
let universal_region_indices = inferred_values
.iter(from_region.index())
.take_while(|&i| i < self.num_universal_regions)
.collect::<Vec<_>>();
for fr in &universal_region_indices {
changed |= inferred_values.add(to_region.index(), *fr);
}
} else {
stack.extend(successor_points);
}
}
changed
}
/// Tries to finds a good span to blame for the fact that `fr1`
/// contains `fr2`.
fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
// Find everything that influenced final value of `fr`.
let influenced_fr1 = self.dependencies(fr1);
// Try to find some outlives constraint `'X: fr2` where `'X`
// influenced `fr1`. Blame that.
//
// NB, this is a pretty bad choice most of the time. In
// particular, the connection between `'X` and `fr1` may not
// be obvious to the user -- not to mention the naive notion
// of dependencies, which doesn't account for the locations of
// contraints at all. But it will do for now.
for constraint in &self.constraints {
if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
return constraint.span;
}
}
bug!(
"could not find any constraint to blame for {:?}: {:?}",
fr1,
fr2
);
}
/// Finds all regions whose values `'a` may depend on in some way.
/// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
/// and `dependencies('b)` will be in the final set.
///
/// Used during error reporting, extremely naive and inefficient.
fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
let mut result_set = IndexVec::from_elem(false, &self.definitions);
let mut changed = true;
result_set[r0] = true;
while changed {
changed = false;
for constraint in &self.constraints {
if result_set[constraint.sup] {
if !result_set[constraint.sub] {
result_set[constraint.sub] = true;
changed = true;
}
}
}
}
result_set
}
}
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_universal_regions`.
Self { origin, name: None }
}
}
impl fmt::Debug for Constraint {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
formatter,
"({:?}: {:?} @ {:?}) due to {:?}",
self.sup,
self.sub,
self.point,
self.span
)
}
}

View File

@ -16,18 +16,18 @@ use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use super::ToRegionVid;
use super::free_regions::FreeRegions;
use super::universal_regions::UniversalRegions;
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
free_regions: &FreeRegions<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
mir: &mut Mir<'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())
let free_region_inference_vars = (0..universal_regions.indices.len())
.map(RegionVid::new)
.map(|vid_expected| {
let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
@ -37,12 +37,12 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
.collect();
debug!("renumber_mir()");
debug!("renumber_mir: free_regions={:#?}", free_regions);
debug!("renumber_mir: universal_regions={:#?}", universal_regions);
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
let mut visitor = NLLVisitor {
infcx,
free_regions,
universal_regions,
free_region_inference_vars,
arg_count: mir.arg_count,
};
@ -51,7 +51,7 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
free_regions: &'a FreeRegions<'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
arg_count: usize,
}
@ -76,16 +76,16 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
/// Renumbers the regions appearing in `value`, but those regions
/// are expected to be free regions from the function signature.
fn renumber_free_regions<T>(&mut self, value: &T) -> T
fn renumber_universal_regions<T>(&mut self, value: &T) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("renumber_free_regions(value={:?})", value);
debug!("renumber_universal_regions(value={:?})", value);
self.infcx
.tcx
.fold_regions(value, &mut false, |region, _depth| {
let index = self.free_regions.indices[&region];
let index = self.universal_regions.indices[&region];
self.free_region_inference_vars[index]
})
}
@ -112,7 +112,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
let old_ty = *ty;
*ty = if is_arg {
self.renumber_free_regions(&old_ty)
self.renumber_universal_regions(&old_ty)
} else {
self.renumber_regions(ty_context, &old_ty)
};

View File

@ -15,7 +15,7 @@ use rustc::ty;
use transform::type_check::MirTypeckRegionConstraints;
use transform::type_check::OutlivesSet;
use super::free_regions::FreeRegions;
use super::universal_regions::UniversalRegions;
use super::region_infer::RegionInferenceContext;
/// When the MIR type-checker executes, it validates all the types in
@ -25,20 +25,20 @@ use super::region_infer::RegionInferenceContext;
/// them into the NLL `RegionInferenceContext`.
pub(super) fn generate<'tcx>(
regioncx: &mut RegionInferenceContext<'tcx>,
free_regions: &FreeRegions<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
constraints: &MirTypeckRegionConstraints<'tcx>,
) {
SubtypeConstraintGenerator {
regioncx,
free_regions,
universal_regions,
mir,
}.generate(constraints);
}
struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
regioncx: &'cx mut RegionInferenceContext<'tcx>,
free_regions: &'cx FreeRegions<'tcx>,
universal_regions: &'cx UniversalRegions<'tcx>,
mir: &'cx Mir<'tcx>,
}
@ -102,11 +102,11 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
// Every region that we see in the constraints came from the
// MIR or from the parameter environment. If the former, it
// will be a region variable. If the latter, it will be in
// the set of free regions *somewhere*.
// the set of universal regions *somewhere*.
if let ty::ReVar(vid) = r {
*vid
} else {
self.free_regions.indices[&r]
self.universal_regions.indices[&r]
}
}
}

View File

@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Code to extract the free regions declared on a function and the
//! relationships between them. For example:
//! Code to extract the universally quantified regions declared on a
//! function and the relationships between them. For example:
//!
//! ```
//! fn foo<'a, 'b, 'c: 'b>() { }
@ -24,29 +24,29 @@
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::middle::free_region::FreeRegionMap;
use rustc::infer::outlives::free_region_map::FreeRegionMap;
use rustc::ty::{self, RegionVid};
use rustc::ty::subst::Substs;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
#[derive(Debug)]
pub struct FreeRegions<'tcx> {
/// Given a free region defined on this function (either early- or
/// late-bound), this maps it to its internal region index. When
/// the region context is created, the first N variables will be
/// created based on these indices.
pub struct UniversalRegions<'tcx> {
/// Given a universally quantified region defined on this function
/// (either early- or late-bound), this maps it to its internal
/// region index. When the region context is created, the first N
/// variables will be created based on these indices.
pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
/// The map from the typeck tables telling us how to relate free regions.
/// The map from the typeck tables telling us how to relate universal regions.
pub free_region_map: &'tcx FreeRegionMap<'tcx>,
}
pub fn free_regions<'a, 'gcx, 'tcx>(
pub fn universal_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
item_def_id: DefId,
) -> FreeRegions<'tcx> {
debug!("free_regions(item_def_id={:?})", item_def_id);
) -> UniversalRegions<'tcx> {
debug!("universal_regions(item_def_id={:?})", item_def_id);
let mut indices = FxHashMap();
@ -76,15 +76,15 @@ pub fn free_regions<'a, 'gcx, 'tcx>(
}
});
debug!("free_regions: indices={:#?}", indices);
debug!("universal_regions: indices={:#?}", indices);
FreeRegions { indices, free_region_map: &tables.free_region_map }
UniversalRegions { indices, free_region_map: &tables.free_region_map }
}
fn insert_free_region<'tcx>(
free_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
universal_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
region: ty::Region<'tcx>,
) {
let next = RegionVid::new(free_regions.len());
free_regions.entry(region).or_insert(next);
let next = RegionVid::new(universal_regions.len());
universal_regions.entry(region).or_insert(next);
}

View File

@ -100,7 +100,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
// HACK(eddyb) Avoid having RustCall on closures,
// as it adds unnecessary (and wrong) auto-tupling.
abi = Abi::Rust;
Some((closure_self_ty(tcx, id, body_id), None))
Some((liberated_closure_env_ty(tcx, id, body_id), None))
}
ty::TyGenerator(..) => {
let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id);
@ -246,10 +246,10 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
///////////////////////////////////////////////////////////////////////////
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)
-> Ty<'tcx> {
fn liberated_closure_env_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)
-> Ty<'tcx> {
let closure_expr_hir_id = tcx.hir.node_to_hir_id(closure_expr_id);
let closure_ty = tcx.body_tables(body_id).node_id_to_type(closure_expr_hir_id);
@ -258,24 +258,8 @@ pub fn closure_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
_ => bug!("closure expr does not have closure type: {:?}", closure_ty)
};
let region = ty::ReFree(ty::FreeRegion {
scope: closure_def_id,
bound_region: ty::BoundRegion::BrEnv,
});
let region = tcx.mk_region(region);
match closure_substs.closure_kind_ty(closure_def_id, tcx).to_opt_closure_kind().unwrap() {
ty::ClosureKind::Fn =>
tcx.mk_ref(region,
ty::TypeAndMut { ty: closure_ty,
mutbl: hir::MutImmutable }),
ty::ClosureKind::FnMut =>
tcx.mk_ref(region,
ty::TypeAndMut { ty: closure_ty,
mutbl: hir::MutMutable }),
ty::ClosureKind::FnOnce =>
closure_ty
}
let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap();
tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty)
}
struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {

View File

@ -21,8 +21,8 @@ use rustc_data_structures::indexed_vec::{IndexVec};
use dataflow::{BitDenotation, BlockSets, DataflowOperator};
pub use dataflow::indexes::BorrowIndex;
use transform::nll::region_infer::RegionInferenceContext;
use transform::nll::ToRegionVid;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;
use syntax_pos::Span;
@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>,
nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
}
// temporarily allow some dead fields: `kind` and `region` will be
@ -69,7 +69,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>)
-> Self {
let mut visitor = GatherBorrows {
tcx,
@ -132,10 +132,6 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
&self.borrows[idx].location
}
pub fn nonlexical_regioncx(&self) -> Option<&'a RegionInferenceContext<'tcx>> {
self.nonlexical_regioncx
}
/// Returns the span for the "end point" given region. This will
/// return `None` if NLL is enabled, since that concept has no
/// meaning there. Otherwise, return region span if it exists and
@ -156,7 +152,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
fn kill_loans_out_of_scope_at_location(&self,
sets: &mut BlockSets<BorrowIndex>,
location: Location) {
if let Some(regioncx) = self.nonlexical_regioncx {
if let Some(ref regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
let borrow_region = borrow_data.region.to_region_vid();
if !regioncx.region_contains_point(borrow_region, location) {

View File

@ -263,6 +263,10 @@ impl<'tcx> MovePathLookup<'tcx> {
}
}
}
pub fn find_local(&self, local: Local) -> MovePathIndex {
self.locals[local]
}
}
#[derive(Debug)]

View File

@ -18,12 +18,15 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(catch_expr)]
#![feature(conservative_impl_trait)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(decl_macro)]
#![feature(i128_type)]
#![feature(inclusive_range_syntax)]
#![feature(match_default_bindings)]
#![feature(range_contains)]
#![feature(rustc_diagnostic_macros)]
#![feature(placement_in_syntax)]
#![feature(collection_placement)]

View File

@ -833,7 +833,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
let tcx = infcx.tcx;
let gcx = tcx.global_tcx();
let def_id = tcx.hir.local_def_id(ctor_id);
let sig = gcx.no_late_bound_regions(&gcx.fn_sig(def_id))
let sig = gcx.fn_sig(def_id).no_late_bound_regions()
.expect("LBR in ADT constructor signature");
let sig = gcx.erase_regions(&sig);
let param_env = gcx.param_env(def_id);

View File

@ -146,7 +146,7 @@ fn check_lang_item_type<'a, 'tcx, D>(
{
let did = tcx.require_lang_item(lang_item);
let poly_sig = tcx.fn_sig(did);
let sig = tcx.no_late_bound_regions(&poly_sig).unwrap();
let sig = poly_sig.no_late_bound_regions().unwrap();
let lhs_ty = lhs.ty(local_decls, tcx);
let rhs_ty = rhs.ty(local_decls, tcx);
let place_ty = place.ty(local_decls, tcx).to_ty(tcx);

View File

@ -43,7 +43,6 @@ pub mod instcombine;
pub mod copy_prop;
pub mod generator;
pub mod inline;
pub mod nll;
pub mod lower_128bit;
pub(crate) fn provide(providers: &mut Providers) {

View File

@ -1,241 +0,0 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::mir::{Location, Place, Mir, Rvalue};
use rustc::mir::visit::Visitor;
use rustc::mir::Place::Projection;
use rustc::mir::{PlaceProjection, ProjectionElem};
use rustc::infer::InferCtxt;
use rustc::traits::{self, ObligationCause};
use rustc::ty::{self, Ty};
use rustc::ty::fold::TypeFoldable;
use rustc::util::common::ErrorReported;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use super::LivenessResults;
use super::ToRegionVid;
use super::region_infer::RegionInferenceContext;
pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>,
param_env: ty::ParamEnv<'tcx>,
liveness: &LivenessResults,
) {
ConstraintGeneration {
infcx,
regioncx,
mir,
liveness,
param_env,
}.add_constraints();
}
struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cx mut RegionInferenceContext<'tcx>,
mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults,
param_env: ty::ParamEnv<'tcx>,
}
impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
fn add_constraints(&mut self) {
self.add_liveness_constraints();
self.add_borrow_constraints();
}
/// Liveness constraints:
///
/// > If a variable V is live at point P, then all regions R in the type of V
/// > must include the point P.
fn add_liveness_constraints(&mut self) {
debug!("add_liveness_constraints()");
for bb in self.mir.basic_blocks().indices() {
debug!("add_liveness_constraints: bb={:?}", bb);
self.liveness
.regular
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_regular_live_constraint(live_local_ty, location);
}
});
self.liveness
.drop
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_drop_live_constraint(live_local_ty, location);
}
});
}
}
/// Some variable with type `live_ty` is "regular live" at
/// `location` -- i.e., it may be used later. This means that all
/// regions appearing in the type `live_ty` must be live at
/// `location`.
fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
where
T: TypeFoldable<'tcx>,
{
debug!(
"add_regular_live_constraint(live_ty={:?}, location={:?})",
live_ty,
location
);
self.infcx
.tcx
.for_each_free_region(&live_ty, |live_region| {
let vid = live_region.to_region_vid();
self.regioncx.add_live_point(vid, location);
});
}
/// Some variable with type `live_ty` is "drop live" at `location`
/// -- i.e., it may be dropped later. This means that *some* of
/// the regions in its type must be live at `location`. The
/// precise set will depend on the dropck constraints, and in
/// particular this takes `#[may_dangle]` into account.
fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
debug!(
"add_drop_live_constraint(dropped_ty={:?}, location={:?})",
dropped_ty,
location
);
let tcx = self.infcx.tcx;
let mut types = vec![(dropped_ty, 0)];
let mut known = FxHashSet();
while let Some((ty, depth)) = types.pop() {
let span = DUMMY_SP; // FIXME
let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
Ok(result) => result,
Err(ErrorReported) => {
continue;
}
};
let ty::DtorckConstraint {
outlives,
dtorck_types,
} = result;
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for outlive in outlives {
if let Some(ty) = outlive.as_type() {
self.add_regular_live_constraint(ty, location);
} else if let Some(r) = outlive.as_region() {
self.add_regular_live_constraint(r, location);
} else {
bug!()
}
}
// However, there may also be some types that
// `dtorck_constraint_for_ty` could not resolve (e.g.,
// associated types and parameters). We need to normalize
// associated types here and possibly recursively process.
for ty in dtorck_types {
let cause = ObligationCause::dummy();
// We know that our original `dropped_ty` is well-formed,
// so region obligations resulting from this normalization
// should always hold.
//
// Therefore we ignore them instead of trying to match
// them up with a location.
let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
match traits::fully_normalize_with_fulfillcx(
self.infcx, fulfillcx, cause, self.param_env, &ty
) {
Ok(ty) => match ty.sty {
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
self.add_regular_live_constraint(ty, location);
}
_ => if known.insert(ty) {
types.push((ty, depth + 1));
},
},
Err(errors) => {
self.infcx.report_fulfillment_errors(&errors, None);
}
}
}
}
}
fn add_borrow_constraints(&mut self) {
self.visit_mir(self.mir);
}
fn add_reborrow_constraint(
&mut self,
location: Location,
borrow_region: ty::Region<'tcx>,
borrowed_place: &Place<'tcx>,
) {
if let Projection(ref proj) = *borrowed_place {
let PlaceProjection { ref base, ref elem } = **proj;
if let ProjectionElem::Deref = *elem {
let tcx = self.infcx.tcx;
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
let base_sty = &base_ty.sty;
if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty {
match mutbl {
hir::Mutability::MutImmutable => { },
hir::Mutability::MutMutable => {
self.add_reborrow_constraint(location, borrow_region, base);
},
}
let span = self.mir.source_info(location).span;
self.regioncx.add_outlives(span,
base_region.to_region_vid(),
borrow_region.to_region_vid(),
location.successor_within_block());
}
}
}
}
}
impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
// Look for an rvalue like:
//
// & L
//
// where L is the path that is borrowed. In that case, we have
// to add the reborrow constraints (which don't fall out
// naturally from the type-checker).
if let Rvalue::Ref(region, _bk, ref borrowed_place) = *rvalue {
self.add_reborrow_constraint(location, region, borrowed_place);
}
self.super_rvalue(rvalue, location);
}
}

View File

@ -1,443 +0,0 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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;
use rustc_data_structures::fx::FxHashSet;
use std::collections::BTreeSet;
use std::fmt;
use syntax_pos::Span;
pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionVid`). The
/// definition contains information about where the region came
/// from as well as its final inferred value.
definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
/// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>,
}
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>>,
/// If true, this is a constant region which cannot grow larger.
/// This is used for named regions as well as `'static`.
constant: bool,
/// The current value of this inference variable. This starts out
/// empty, but grows as we add constraints. The final value is
/// determined when `solve()` is executed.
value: Region,
}
/// The value of an individual region variable. Region variables
/// consist of a set of points in the CFG as well as a set of "free
/// regions", which are sometimes written as `end(R)`. These
/// correspond to the named lifetimes and refer to portions of the
/// caller's control-flow graph -- specifically some portion that can
/// be reached after we return.
#[derive(Clone, Default, PartialEq, Eq)]
struct Region {
points: BTreeSet<Location>,
free_regions: BTreeSet<RegionVid>,
}
impl fmt::Debug for Region {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
formatter
.debug_set()
.entries(&self.points)
.entries(&self.free_regions)
.finish()
}
}
impl Region {
fn add_point(&mut self, point: Location) -> bool {
self.points.insert(point)
}
fn add_free_region(&mut self, region: RegionVid) -> bool {
self.free_regions.insert(region)
}
fn contains_point(&self, point: Location) -> bool {
self.points.contains(&point)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Constraint {
// NB. The ordering here is not significant for correctness, but
// it is for convenience. Before we dump the constraints in the
// debugging logs, we sort them, and we'd like the "super region"
// to be first, etc. (In particular, span should remain last.)
/// The region SUP must outlive SUB...
sup: RegionVid,
/// Region that must be outlived.
sub: RegionVid,
/// At this location.
point: Location,
/// Where did this constraint arise?
span: Span,
}
impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
/// Creates a new region inference context with a total of
/// `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(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: definitions,
constraints: Vec::new(),
};
result.init_free_regions(free_regions, mir);
result
}
/// Initializes the region variables for each free region
/// (lifetime parameter). The first N variables always correspond
/// to the free regions appearing in the function signature (both
/// named and anonymous) and where clauses. This function iterates
/// over those regions and initializes them with minimum values.
///
/// For example:
///
/// fn foo<'a, 'b>(..) where 'a: 'b
///
/// would initialize two variables like so:
///
/// R0 = { CFG, R0 } // 'a
/// R1 = { CFG, R0, R1 } // 'b
///
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
/// and (b) any free regions that it outlives, which in this case
/// is just itself. R1 (`'b`) in contrast also outlives `'a` and
/// hence contains R0 and R1.
fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) {
let FreeRegions {
indices,
free_region_map,
} = free_regions;
// For each free region X:
for (free_region, &variable) in indices {
// 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);
self.definitions[variable].constant = true;
// Add all nodes in the CFG to `definition.value`.
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
let definition = &mut self.definitions[variable];
for statement_index in 0..block_data.statements.len() + 1 {
let location = Location {
block,
statement_index,
};
definition.value.add_point(location);
}
}
// Add `end(X)` into the set for X.
self.definitions[variable].value.add_free_region(variable);
// `'static` outlives all other free regions as well.
if let ty::ReStatic = free_region {
for &other_variable in indices.values() {
self.definitions[variable]
.value
.add_free_region(other_variable);
}
}
// Go through each region Y that outlives X (i.e., where
// Y: X is true). Add `end(X)` into the set for `Y`.
for superregion in free_region_map.regions_that_outlive(&free_region) {
let superregion_index = indices[superregion];
self.definitions[superregion_index]
.value
.add_free_region(variable);
}
debug!(
"init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
free_region,
variable,
self.definitions[variable].value
);
}
}
/// Returns an iterator over all the region indices.
pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
self.definitions.indices()
}
/// Returns true if the region `r` contains the point `p`.
///
/// Until `solve()` executes, this value is not particularly meaningful.
pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool {
self.definitions[r].value.contains_point(p)
}
/// Returns access to the value of `r` for debugging purposes.
pub(super) fn region_value(&self, r: RegionVid) -> &fmt::Debug {
&self.definitions[r].value
}
/// Indicates that the region variable `v` is live at the point `point`.
pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) {
debug!("add_live_point({:?}, {:?})", v, point);
let definition = &mut self.definitions[v];
if !definition.constant {
definition.value.add_point(point);
} else {
// Constants are used for free regions, which already
// contain all the points in the control-flow graph.
assert!(definition.value.contains_point(point));
}
}
/// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
pub(super) fn add_outlives(
&mut self,
span: Span,
sup: RegionVid,
sub: RegionVid,
point: Location,
) {
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
self.constraints.push(Constraint {
span,
sup,
sub,
point,
});
}
/// Perform region inference.
pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>) {
let errors = self.propagate_constraints(mir);
// worst error msg ever
for (fr1, span, fr2) in errors {
infcx.tcx.sess.span_err(
span,
&format!(
"free region `{}` does not outlive `{}`",
self.definitions[fr1].name.unwrap(),
self.definitions[fr2].name.unwrap()
),
);
}
}
/// Propagate the region constraints: this will grow the values
/// 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)> {
let mut changed = true;
let mut dfs = Dfs::new(mir);
let mut error_regions = FxHashSet();
let mut errors = vec![];
debug!("propagate_constraints()");
debug!("propagate_constraints: constraints={:#?}", {
let mut constraints: Vec<_> = self.constraints.iter().collect();
constraints.sort();
constraints
});
while changed {
changed = false;
for constraint in &self.constraints {
debug!("propagate_constraints: constraint={:?}", constraint);
let sub = &self.definitions[constraint.sub].value.clone();
let sup_def = &mut self.definitions[constraint.sup];
debug!("propagate_constraints: sub (before): {:?}", sub);
debug!("propagate_constraints: sup (before): {:?}", sup_def.value);
if !sup_def.constant {
// If this is not a constant, then grow the value as needed to
// accommodate the outlives constraint.
if dfs.copy(sub, &mut sup_def.value, constraint.point) {
changed = true;
}
debug!("propagate_constraints: sup (after) : {:?}", sup_def.value);
debug!("propagate_constraints: changed : {:?}", changed);
} else {
// If this is a constant, check whether it *would
// have* to grow in order for the constraint to be
// satisfied. If so, create an error.
let mut sup_value = sup_def.value.clone();
if dfs.copy(sub, &mut sup_value, constraint.point) {
// Constant values start out with the entire
// CFG, so it must be some new free region
// that was added. Find one.
let &new_region = sup_value
.free_regions
.difference(&sup_def.value.free_regions)
.next()
.unwrap();
debug!("propagate_constraints: new_region : {:?}", new_region);
if error_regions.insert(constraint.sup) {
errors.push((constraint.sup, constraint.span, new_region));
}
}
}
}
debug!("\n");
}
errors
}
}
struct Dfs<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
}
impl<'a, 'tcx> Dfs<'a, 'tcx> {
fn new(mir: &'a Mir<'tcx>) -> Self {
Self { mir }
}
fn copy(
&mut self,
from_region: &Region,
to_region: &mut Region,
start_point: Location,
) -> bool {
let mut changed = false;
let mut stack = vec![];
let mut visited = FxHashSet();
stack.push(start_point);
while let Some(p) = stack.pop() {
debug!(" dfs: p={:?}", p);
if !from_region.contains_point(p) {
debug!(" not in from-region");
continue;
}
if !visited.insert(p) {
debug!(" already visited");
continue;
}
changed |= to_region.add_point(p);
let block_data = &self.mir[p.block];
let successor_points = if p.statement_index < block_data.statements.len() {
vec![
Location {
statement_index: p.statement_index + 1,
..p
},
]
} else {
block_data
.terminator()
.successors()
.iter()
.map(|&basic_block| {
Location {
statement_index: 0,
block: basic_block,
}
})
.collect::<Vec<_>>()
};
if successor_points.is_empty() {
// If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_region`
// and make sure they are included in the `to_region`.
debug!(" dfs: free_regions={:?}", from_region.free_regions);
for &fr in &from_region.free_regions {
changed |= to_region.free_regions.insert(fr);
}
} else {
stack.extend(successor_points);
}
}
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(),
}
}
}
impl fmt::Debug for Constraint {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
formatter,
"({:?}: {:?} @ {:?}) due to {:?}",
self.sup,
self.sub,
self.point,
self.span
)
}
}

View File

@ -104,12 +104,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
}
}
fn visit_place(
&mut self,
place: &Place<'tcx>,
context: PlaceContext,
location: Location,
) {
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
self.sanitize_place(place, location, context);
}
@ -164,11 +159,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
}
}
fn sanitize_place(&mut self,
place: &Place<'tcx>,
location: Location,
context: PlaceContext)
-> PlaceTy<'tcx> {
fn sanitize_place(
&mut self,
place: &Place<'tcx>,
location: Location,
context: PlaceContext,
) -> PlaceTy<'tcx> {
debug!("sanitize_place: {:?}", place);
let place_ty = match *place {
Place::Local(index) => PlaceTy::Ty {
@ -178,9 +174,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
let sty = self.sanitize_type(place, sty);
let ty = self.tcx().type_of(def_id);
let ty = self.cx.normalize(&ty, location);
if let Err(terr) = self.cx
.eq_types(self.last_span, ty, sty, location.at_self())
{
if let Err(terr) = self.cx.eq_types(ty, sty, location.at_self()) {
span_mirbug!(
self,
place,
@ -212,9 +206,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
};
if let PlaceContext::Copy = context {
let ty = place_ty.to_ty(self.tcx());
if self.cx.infcx.type_moves_by_default(self.cx.param_env, ty, DUMMY_SP) {
span_mirbug!(self, place,
"attempted copy of non-Copy type ({:?})", ty);
if self.cx
.infcx
.type_moves_by_default(self.cx.param_env, ty, DUMMY_SP)
{
span_mirbug!(self, place, "attempted copy of non-Copy type ({:?})", ty);
}
}
place_ty
@ -230,7 +226,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place);
let tcx = self.tcx();
let base_ty = base.to_ty(tcx);
let span = self.last_span;
match *pi {
ProjectionElem::Deref => {
let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
@ -315,18 +310,16 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
ProjectionElem::Field(field, fty) => {
let fty = self.sanitize_type(place, fty);
match self.field_ty(place, base, field, location) {
Ok(ty) => {
if let Err(terr) = self.cx.eq_types(span, ty, fty, location.at_self()) {
span_mirbug!(
self,
place,
"bad field access ({:?}: {:?}): {:?}",
ty,
fty,
terr
);
}
}
Ok(ty) => if let Err(terr) = self.cx.eq_types(ty, fty, location.at_self()) {
span_mirbug!(
self,
place,
"bad field access ({:?}: {:?}): {:?}",
ty,
fty,
terr
);
},
Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!(
self,
place,
@ -361,9 +354,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
variant_index,
} => (&adt_def.variants[variant_index], substs),
PlaceTy::Ty { ty } => match ty.sty {
ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => {
(&adt_def.variants[0], substs)
}
ty::TyAdt(adt_def, substs) if !adt_def.is_enum() => (&adt_def.variants[0], substs),
ty::TyClosure(def_id, substs) => {
return match substs.upvar_tys(def_id, tcx).nth(field.index()) {
Some(ty) => Ok(ty),
@ -529,13 +520,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
})
}
fn eq_types(
&mut self,
_span: Span,
a: Ty<'tcx>,
b: Ty<'tcx>,
locations: Locations,
) -> UnitResult<'tcx> {
fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> {
self.fully_perform_op(locations, |this| {
this.infcx
.at(&this.misc(this.last_span), this.param_env)
@ -1031,13 +1016,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
fn aggregate_field_ty(
&mut self,
ak: &Box<AggregateKind<'tcx>>,
ak: &AggregateKind<'tcx>,
field_index: usize,
location: Location,
) -> Result<Ty<'tcx>, FieldAccessError> {
let tcx = self.tcx();
match **ak {
match *ak {
AggregateKind::Adt(def, variant_index, substs, active_field_index) => {
let variant = &def.variants[variant_index];
let adj_field_index = active_field_index.unwrap_or(field_index);
@ -1069,56 +1054,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
}
AggregateKind::Array(ty) => {
Ok(ty)
}
AggregateKind::Array(ty) => Ok(ty),
AggregateKind::Tuple => {
unreachable!("This should have been covered in check_rvalues");
}
}
}
fn check_rvalue(&mut self, mir: &Mir<'tcx>, rv: &Rvalue<'tcx>, location: Location) {
let tcx = self.tcx();
match rv {
fn check_rvalue(&mut self, mir: &Mir<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
match rvalue {
Rvalue::Aggregate(ak, ops) => {
match **ak {
// tuple rvalue field type is always the type of the op. Nothing to check here.
AggregateKind::Tuple => {}
_ => {
for (i, op) in ops.iter().enumerate() {
let field_ty = match self.aggregate_field_ty(ak, i, location) {
Ok(field_ty) => field_ty,
Err(FieldAccessError::OutOfRange { field_count }) => {
span_mirbug!(
self,
rv,
"accessed field #{} but variant only has {}",
i,
field_count
);
continue;
}
};
let op_ty = op.ty(mir, tcx);
if let Err(terr) = self.sub_types(
op_ty,
field_ty,
location.at_successor_within_block(),
)
{
span_mirbug!(
self,
rv,
"{:?} is not a subtype of {:?}: {:?}",
op_ty,
field_ty,
terr
);
}
}
}
}
self.check_aggregate_rvalue(mir, rvalue, ak, ops, location)
}
// FIXME: These other cases have to be implemented in future PRs
Rvalue::Use(..) |
@ -1134,6 +1080,52 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
fn check_aggregate_rvalue(
&mut self,
mir: &Mir<'tcx>,
rvalue: &Rvalue<'tcx>,
aggregate_kind: &AggregateKind<'tcx>,
operands: &[Operand<'tcx>],
location: Location,
) {
match aggregate_kind {
// tuple rvalue field type is always the type of the op. Nothing to check here.
AggregateKind::Tuple => return,
_ => {}
}
let tcx = self.tcx();
for (i, operand) in operands.iter().enumerate() {
let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
Ok(field_ty) => field_ty,
Err(FieldAccessError::OutOfRange { field_count }) => {
span_mirbug!(
self,
rvalue,
"accessed field #{} but variant only has {}",
i,
field_count
);
continue;
}
};
let operand_ty = operand.ty(mir, tcx);
if let Err(terr) =
self.sub_types(operand_ty, field_ty, location.at_successor_within_block())
{
span_mirbug!(
self,
rvalue,
"{:?} is not a subtype of {:?}: {:?}",
operand_ty,
field_ty,
terr
);
}
}
}
fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
self.last_span = mir.span;
debug!("run_on_mir: {:?}", mir.span);

View File

@ -15,7 +15,7 @@ pub mod patch;
mod alignment;
mod graphviz;
mod pretty;
pub(crate) mod pretty;
pub mod liveness;
pub use self::alignment::is_disaligned;

View File

@ -14,17 +14,17 @@ use rustc::mir::*;
use rustc::ty::TyCtxt;
use rustc::ty::item_path;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::{Idx};
use rustc_data_structures::indexed_vec::Idx;
use std::fmt::Display;
use std::fs;
use std::io::{self, Write};
use std::path::{PathBuf, Path};
use std::path::{Path, PathBuf};
use super::graphviz::write_mir_fn_graphviz;
use transform::MirSource;
const INDENT: &'static str = " ";
/// Alignment for lining up comments following MIR statements
const ALIGN: usize = 40;
pub(crate) const ALIGN: usize = 40;
/// An indication of where we are in the control flow graph. Used for printing
/// extra information in `dump_mir`
@ -38,8 +38,11 @@ pub enum PassWhere {
/// We are about to start dumping the given basic block.
BeforeBlock(BasicBlock),
/// We are just about to dumpt the given statement or terminator.
InCFG(Location),
/// We are just about to dump the given statement or terminator.
BeforeLocation(Location),
/// We just dumped the given statement or terminator.
AfterLocation(Location),
}
/// If the session is properly configured, dumps a human-readable
@ -56,87 +59,80 @@ pub enum PassWhere {
/// - `substring1&substring2,...` -- `&`-separated list of substrings
/// that can appear in the pass-name or the `item_path_str` for the given
/// node-id. If any one of the substrings match, the data is dumped out.
pub fn dump_mir<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<&Display>,
pass_name: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
extra_data: F)
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
pub fn dump_mir<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<&Display>,
pass_name: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
extra_data: F,
) where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
{
if !dump_enabled(tcx, pass_name, source) {
return;
}
let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
let node_path = item_path::with_forced_impl_filename_line(|| {
// see notes on #41697 below
tcx.item_path_str(source.def_id)
});
dump_matched_mir_node(tcx, pass_num, pass_name, &node_path,
disambiguator, source, mir, extra_data);
dump_matched_mir_node(
tcx,
pass_num,
pass_name,
&node_path,
disambiguator,
source,
mir,
extra_data,
);
}
pub fn dump_enabled<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_name: &str,
source: MirSource)
-> bool {
pub fn dump_enabled<'a, 'gcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_name: &str,
source: MirSource,
) -> bool {
let filters = match tcx.sess.opts.debugging_opts.dump_mir {
None => return false,
Some(ref filters) => filters,
};
let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
let node_path = item_path::with_forced_impl_filename_line(|| {
// see notes on #41697 below
tcx.item_path_str(source.def_id)
});
filters.split("&")
.any(|filter| {
filter == "all" ||
pass_name.contains(filter) ||
node_path.contains(filter)
})
filters.split("&").any(|filter| {
filter == "all" || pass_name.contains(filter) || node_path.contains(filter)
})
}
// #41697 -- we use `with_forced_impl_filename_line()` because
// `item_path_str()` would otherwise trigger `type_of`, and this can
// run while we are already attempting to evaluate `type_of`.
fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<&Display>,
pass_name: &str,
node_path: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
mut extra_data: F)
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<&Display>,
pass_name: &str,
node_path: &str,
disambiguator: &Display,
source: MirSource,
mir: &Mir<'tcx>,
mut extra_data: F,
) where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
{
let promotion_id = match source.promoted {
Some(id) => format!("-{:?}", id),
None => String::new()
};
let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
format!("")
} else {
match pass_num {
None => format!(".-------"),
Some(pass_num) => format!(".{}", pass_num),
}
};
let mut file_path = PathBuf::new();
if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
let p = Path::new(file_dir);
file_path.push(p);
};
let _ = fs::create_dir_all(&file_path);
let item_name = tcx.hir.def_path(source.def_id).to_filename_friendly_no_crate();
let file_name = format!("rustc.{}{}{}.{}.{}.mir",
item_name, promotion_id, pass_num, pass_name, disambiguator);
file_path.push(&file_name);
let _ = fs::File::create(&file_path).and_then(|mut file| {
let _: io::Result<()> = do catch {
let mut file = create_dump_file(
tcx,
"mir",
pass_num,
pass_name,
disambiguator,
source,
)?;
writeln!(file, "// MIR for `{}`", node_path)?;
writeln!(file, "// source = {:?}", source)?;
writeln!(file, "// pass_name = {}", pass_name)?;
@ -149,25 +145,108 @@ where
write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?;
extra_data(PassWhere::AfterCFG, &mut file)?;
Ok(())
});
};
if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
file_path.set_extension("dot");
let _ = fs::File::create(&file_path).and_then(|mut file| {
let _: io::Result<()> = do catch {
let mut file = create_dump_file(
tcx,
"dot",
pass_num,
pass_name,
disambiguator,
source,
)?;
write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?;
Ok(())
});
};
}
}
/// Returns the path to the filename where we should dump a given MIR.
/// Also used by other bits of code (e.g., NLL inference) that dump
/// graphviz data or other things.
fn dump_path(
tcx: TyCtxt<'_, '_, '_>,
extension: &str,
pass_num: Option<&Display>,
pass_name: &str,
disambiguator: &Display,
source: MirSource,
) -> PathBuf {
let promotion_id = match source.promoted {
Some(id) => format!("-{:?}", id),
None => String::new(),
};
let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
format!("")
} else {
match pass_num {
None => format!(".-------"),
Some(pass_num) => format!(".{}", pass_num),
}
};
let mut file_path = PathBuf::new();
if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
let p = Path::new(file_dir);
file_path.push(p);
};
let item_name = tcx.hir
.def_path(source.def_id)
.to_filename_friendly_no_crate();
let file_name = format!(
"rustc.{}{}{}.{}.{}.{}",
item_name,
promotion_id,
pass_num,
pass_name,
disambiguator,
extension,
);
file_path.push(&file_name);
file_path
}
/// Attempts to open a file where we should dump a given MIR or other
/// bit of MIR-related data. Used by `mir-dump`, but also by other
/// bits of code (e.g., NLL inference) that dump graphviz data or
/// other things, and hence takes the extension as an argument.
pub(crate) fn create_dump_file(
tcx: TyCtxt<'_, '_, '_>,
extension: &str,
pass_num: Option<&Display>,
pass_name: &str,
disambiguator: &Display,
source: MirSource,
) -> io::Result<fs::File> {
let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source);
if let Some(parent) = file_path.parent() {
fs::create_dir_all(parent)?;
}
fs::File::create(&file_path)
}
/// Write out a human-readable textual representation for the given MIR.
pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
single: Option<DefId>,
w: &mut Write)
-> io::Result<()>
{
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
pub fn write_mir_pretty<'a, 'gcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
single: Option<DefId>,
w: &mut Write,
) -> io::Result<()> {
writeln!(
w,
"// WARNING: This output format is intended for human consumers only"
)?;
writeln!(
w,
"// and is subject to change without notice. Knock yourself out."
)?;
let mut first = true;
for def_id in dump_mir_def_ids(tcx, single) {
@ -186,7 +265,7 @@ pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
writeln!(w, "")?;
let src = MirSource {
def_id,
promoted: Some(i)
promoted: Some(i),
};
write_mir_fn(tcx, src, mir, &mut |_, _| Ok(()), w)?;
}
@ -194,14 +273,15 @@ pub fn write_mir_pretty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
Ok(())
}
pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
extra_data: &mut F,
w: &mut Write)
-> io::Result<()>
pub fn write_mir_fn<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
extra_data: &mut F,
w: &mut Write,
) -> io::Result<()>
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
{
write_mir_intro(tcx, src, mir, w)?;
for block in mir.basic_blocks().indices() {
@ -217,14 +297,15 @@ where
}
/// Write out a human-readable textual representation for the given basic block.
pub fn write_basic_block<F>(tcx: TyCtxt,
block: BasicBlock,
mir: &Mir,
extra_data: &mut F,
w: &mut Write)
-> io::Result<()>
pub fn write_basic_block<F>(
tcx: TyCtxt,
block: BasicBlock,
mir: &Mir,
extra_data: &mut F,
w: &mut Write,
) -> io::Result<()>
where
F: FnMut(PassWhere, &mut Write) -> io::Result<()>
F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
{
let data = &mir[block];
@ -234,43 +315,61 @@ where
writeln!(w, "{0:1$}{2}", lbl, ALIGN, cleanup_text)?;
// List of statements in the middle.
let mut current_location = Location { block: block, statement_index: 0 };
let mut current_location = Location {
block: block,
statement_index: 0,
};
for statement in &data.statements {
extra_data(PassWhere::InCFG(current_location), w)?;
extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
writeln!(w, "{0:1$} // {2}",
indented_mir,
ALIGN,
comment(tcx, statement.source_info))?;
writeln!(
w,
"{:A$} // {:?}: {}",
indented_mir,
current_location,
comment(tcx, statement.source_info),
A = ALIGN,
)?;
extra_data(PassWhere::AfterLocation(current_location), w)?;
current_location.statement_index += 1;
}
// Terminator at the bottom.
extra_data(PassWhere::InCFG(current_location), w)?;
extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
writeln!(w, "{0:1$} // {2}",
indented_terminator,
ALIGN,
comment(tcx, data.terminator().source_info))?;
writeln!(
w,
"{:A$} // {:?}: {}",
indented_terminator,
current_location,
comment(tcx, data.terminator().source_info),
A = ALIGN,
)?;
extra_data(PassWhere::AfterLocation(current_location), w)?;
writeln!(w, "{}}}", INDENT)
}
fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
format!(
"scope {} at {}",
scope.index(),
tcx.sess.codemap().span_to_string(span)
)
}
/// Prints user-defined variables in a scope tree.
///
/// Returns the total number of variables printed.
fn write_scope_tree(tcx: TyCtxt,
mir: &Mir,
scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
w: &mut Write,
parent: VisibilityScope,
depth: usize)
-> io::Result<()> {
fn write_scope_tree(
tcx: TyCtxt,
mir: &Mir,
scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
w: &mut Write,
parent: VisibilityScope,
depth: usize,
) -> io::Result<()> {
let indent = depth * INDENT.len();
let children = match scope_tree.get(&parent) {
@ -300,17 +399,22 @@ fn write_scope_tree(tcx: TyCtxt,
};
let indent = indent + INDENT.len();
let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
INDENT,
indent,
mut_str,
local,
var.ty);
writeln!(w, "{0:1$} // \"{2}\" in {3}",
indented_var,
ALIGN,
name,
comment(tcx, source_info))?;
let indented_var = format!(
"{0:1$}let {2}{3:?}: {4:?};",
INDENT,
indent,
mut_str,
local,
var.ty
);
writeln!(
w,
"{0:1$} // \"{2}\" in {3}",
indented_var,
ALIGN,
name,
comment(tcx, source_info)
)?;
}
write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
@ -323,11 +427,12 @@ fn write_scope_tree(tcx: TyCtxt,
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
src: MirSource,
mir: &Mir,
w: &mut Write)
-> io::Result<()> {
pub fn write_mir_intro<'a, 'gcx, 'tcx>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
src: MirSource,
mir: &Mir,
w: &mut Write,
) -> io::Result<()> {
write_mir_sig(tcx, src, mir, w)?;
writeln!(w, " {{")?;
@ -335,9 +440,10 @@ pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
if let Some(parent) = scope_data.parent_scope {
scope_tree.entry(parent)
.or_insert(vec![])
.push(VisibilityScope::new(index));
scope_tree
.entry(parent)
.or_insert(vec![])
.push(VisibilityScope::new(index));
} else {
// Only the argument scope has no parent, because it's the root.
assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
@ -363,9 +469,7 @@ pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
Ok(())
}
fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
-> io::Result<()>
{
fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) -> io::Result<()> {
let id = tcx.hir.as_local_node_id(src.def_id).unwrap();
let body_owner_kind = tcx.hir.body_owner_kind(id);
match (body_owner_kind, src.promoted) {
@ -376,7 +480,8 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
(hir::BodyOwnerKind::Static(hir::MutMutable), _) => write!(w, "static mut")?,
}
item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere
item_path::with_forced_impl_filename_line(|| {
// see notes on #41697 elsewhere
write!(w, " {}", tcx.item_path_str(src.def_id))
})?;
@ -394,9 +499,7 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
write!(w, ") -> {}", mir.return_ty())
}
(hir::BodyOwnerKind::Const, _) |
(hir::BodyOwnerKind::Static(_), _) |
(_, Some(_)) => {
(hir::BodyOwnerKind::Const, _) | (hir::BodyOwnerKind::Static(_), _) | (_, Some(_)) => {
assert_eq!(mir.arg_count, 0);
write!(w, ": {} =", mir.return_ty())
}
@ -406,7 +509,13 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
// Compiler-introduced temporary types.
for temp in mir.temps_iter() {
writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
writeln!(
w,
"{}let mut {:?}: {};",
INDENT,
temp,
mir.local_decls[temp].ty
)?;
}
Ok(())

View File

@ -395,15 +395,9 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let tcx = ccx.tcx();
let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs);
let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
let env_ty = match substs.closure_kind(def_id, tcx) {
ty::ClosureKind::Fn => tcx.mk_imm_ref(tcx.mk_region(env_region), ty),
ty::ClosureKind::FnMut => tcx.mk_mut_ref(tcx.mk_region(env_region), ty),
ty::ClosureKind::FnOnce => ty,
};
let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
sig.map_bound(|sig| tcx.mk_fn_sig(
iter::once(env_ty).chain(sig.inputs().iter().cloned()),
iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
sig.output(),
sig.variadic,
sig.unsafety,

View File

@ -800,7 +800,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let pat_ty = self.instantiate_value_path(segments, opt_ty, def, pat.span, pat.id);
// Replace constructor type with constructed type for tuple struct patterns.
let pat_ty = pat_ty.fn_sig(tcx).output();
let pat_ty = tcx.no_late_bound_regions(&pat_ty).expect("expected fn type");
let pat_ty = pat_ty.no_late_bound_regions().expect("expected fn type");
self.demand_eqtype(pat.span, expected, pat_ty);

View File

@ -562,7 +562,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
body: &hir::Body,
bound_sig: ty::PolyFnSig<'tcx>,
) -> ClosureSignatures<'tcx> {
let liberated_sig = self.liberate_late_bound_regions(expr_def_id, &bound_sig);
let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, &bound_sig);
let liberated_sig = self.inh.normalize_associated_types_in(
body.value.span,
body.value.id,

View File

@ -270,7 +270,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let impl_fty = tcx.mk_fn_ptr(ty::Binder(impl_sig));
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
let trait_sig = inh.liberate_late_bound_regions(
let trait_sig = tcx.liberate_late_bound_regions(
impl_m.def_id,
&tcx.fn_sig(trait_m.def_id));
let trait_sig =

View File

@ -11,12 +11,12 @@
use check::regionck::RegionCtxt;
use hir::def_id::DefId;
use middle::free_region::FreeRegionMap;
use rustc::infer::{self, InferOk};
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::middle::region;
use rustc::ty::subst::{Subst, Substs};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::traits::{self, ObligationCause};
use rustc::traits::{self, Reveal, ObligationCause};
use util::common::ErrorReported;
use util::nodemap::FxHashSet;
@ -115,8 +115,18 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
}
let region_scope_tree = region::ScopeTree::default();
let free_regions = FreeRegionMap::new();
infcx.resolve_regions_and_report_errors(drop_impl_did, &region_scope_tree, &free_regions);
// NB. It seems a bit... suspicious to use an empty param-env
// here. The correct thing, I imagine, would be
// `OutlivesEnvironment::new(impl_param_env)`, which would
// allow region solving to take any `a: 'b` relations on the
// impl into account. But I could not create a test case where
// it did the wrong thing, so I chose to preserve existing
// behavior, since it ought to be simply more
// conservative. -nmatsakis
let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty(Reveal::UserFacing));
infcx.resolve_regions_and_report_errors(drop_impl_did, &region_scope_tree, &outlives_env);
Ok(())
})
}

View File

@ -389,7 +389,7 @@ pub fn check_platform_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let mut structural_to_nomimal = FxHashMap();
let sig = tcx.fn_sig(def_id);
let sig = tcx.no_late_bound_regions(&sig).unwrap();
let sig = sig.no_late_bound_regions().unwrap();
if intr.inputs.len() != sig.inputs().len() {
span_err!(tcx.sess, it.span, E0444,
"platform-specific intrinsic has invalid number of \

View File

@ -698,22 +698,6 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> {
let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value);
self.register_infer_ok_obligations(ok)
}
/// Replace any late-bound regions bound in `value` with
/// free variants attached to `all_outlive_scope`.
fn liberate_late_bound_regions<T>(&self,
all_outlive_scope: DefId,
value: &ty::Binder<T>)
-> T
where T: TypeFoldable<'tcx>
{
self.tcx.replace_late_bound_regions(value, |br| {
self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
scope: all_outlive_scope,
bound_region: br
}))
}).0
}
}
struct CheckItemTypesVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
@ -882,7 +866,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Compute the fty from point of view of inside fn.
let fn_sig =
inh.liberate_late_bound_regions(def_id, &fn_sig);
tcx.liberate_late_bound_regions(def_id, &fn_sig);
let fn_sig =
inh.normalize_associated_types_in(body.value.span,
body_id.node_id,

View File

@ -90,7 +90,8 @@ use middle::region;
use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty};
use rustc::infer::{self, OutlivesEnvironment};
use rustc::infer;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::ty::adjustment;
use rustc::ty::outlives::Component;
@ -553,7 +554,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
fn resolve_regions_and_report_errors(&self) {
self.fcx.resolve_regions_and_report_errors(self.subject_def_id,
&self.region_scope_tree,
self.outlives_environment.free_region_map());
&self.outlives_environment);
}
fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) {

View File

@ -451,7 +451,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
implied_bounds: &mut Vec<Ty<'tcx>>)
{
let sig = fcx.normalize_associated_types_in(span, &sig);
let sig = fcx.liberate_late_bound_regions(def_id, &sig);
let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig);
for input_ty in sig.inputs() {
fcx.register_wf_obligation(&input_ty, span, self.code.clone());
@ -484,12 +484,12 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
let sig = fcx.tcx.fn_sig(method.def_id);
let sig = fcx.normalize_associated_types_in(span, &sig);
let sig = fcx.liberate_late_bound_regions(method.def_id, &sig);
let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, &sig);
debug!("check_method_receiver: sig={:?}", sig);
let self_ty = fcx.normalize_associated_types_in(span, &self_ty);
let self_ty = fcx.liberate_late_bound_regions(
let self_ty = fcx.tcx.liberate_late_bound_regions(
method.def_id,
&ty::Binder(self_ty)
);
@ -498,7 +498,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty);
let self_arg_ty = fcx.liberate_late_bound_regions(
let self_arg_ty = fcx.tcx.liberate_late_bound_regions(
method.def_id,
&ty::Binder(self_arg_ty)
);

View File

@ -11,7 +11,7 @@
//! Check properties that are required by built-in traits and set
//! up data structures required by type-checking/translation.
use rustc::middle::free_region::FreeRegionMap;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::middle::region;
use rustc::middle::lang_items::UnsizeTraitLangItem;
@ -391,9 +391,12 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Finally, resolve all regions.
let region_scope_tree = region::ScopeTree::default();
let mut free_regions = FreeRegionMap::new();
free_regions.relate_free_regions_from_predicates(&param_env.caller_bounds);
infcx.resolve_regions_and_report_errors(impl_did, &region_scope_tree, &free_regions);
let outlives_env = OutlivesEnvironment::new(param_env);
infcx.resolve_regions_and_report_errors(
impl_did,
&region_scope_tree,
&outlives_env,
);
CoerceUnsizedInfo {
custom_kind: kind

View File

@ -202,7 +202,7 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> {
poly_trait_ref: ty::PolyTraitRef<'tcx>)
-> Ty<'tcx>
{
if let Some(trait_ref) = self.tcx().no_late_bound_regions(&poly_trait_ref) {
if let Some(trait_ref) = poly_trait_ref.no_late_bound_regions() {
self.tcx().mk_projection(item_def_id, trait_ref.substs)
} else {
// no late-bound regions, we can just ignore the binder

View File

@ -0,0 +1,37 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Test that we are able to normalize in the list of where-clauses,
// even if `'a: 'b` is required.
trait Project<'a, 'b> {
type Item;
}
impl<'a, 'b> Project<'a, 'b> for ()
where 'a: 'b
{
type Item = ();
}
// No error here, we have 'a: 'b. We used to report an error here
// though, see https://github.com/rust-lang/rust/issues/45937.
fn foo<'a: 'b, 'b>()
where <() as Project<'a, 'b>>::Item : Eq
{
}
// Here we get an error: we need `'a: 'b`.
fn bar<'a, 'b>() //~ ERROR cannot infer
where <() as Project<'a, 'b>>::Item : Eq
{
}
fn main() { }

View File

@ -26,9 +26,9 @@ fn main() {
// END RUST SOURCE
// START rustc.use_x.nll.0.mir
// | '_#0r: {bb0[0], bb0[1], '_#0r, '_#1r, '_#2r, '_#3r}
// | '_#0r: {bb0[0], bb0[1], '_#0r}
// | '_#1r: {bb0[0], bb0[1], '_#1r}
// | '_#2r: {bb0[0], bb0[1], '_#1r, '_#2r}
// | '_#2r: {bb0[0], bb0[1], '_#2r}
// | '_#3r: {bb0[0], bb0[1], '_#3r}
// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
// END rustc.use_x.nll.0.mir

View File

@ -31,7 +31,7 @@ fn main() {
// END RUST SOURCE
// START rustc.main.nll.0.mir
// | '_#1r: {bb2[1], bb3[0], bb3[1]}
// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]}
// | '_#2r: {bb2[1], bb3[0], bb3[1]}
// ...
// let _2: &'_#2r usize;

View File

@ -36,9 +36,9 @@ fn main() {
// END RUST SOURCE
// START rustc.main.nll.0.mir
// | '_#1r: {bb2[1], bb3[0], bb3[1]}
// | '_#1r: {bb2[0], bb2[1], bb3[0], bb3[1]}
// ...
// | '_#3r: {bb8[2], bb8[3], bb8[4]}
// | '_#3r: {bb8[1], bb8[2], bb8[3], bb8[4]}
// | '_#4r: {bb2[1], bb3[0], bb3[1], bb8[2], bb8[3], bb8[4]}
// ...
// let mut _2: &'_#4r usize;

View File

@ -32,7 +32,7 @@ fn main() {
// END RUST SOURCE
// START rustc.main.nll.0.mir
// | '_#1r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
// | '_#1r: {bb2[0], bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
// | '_#2r: {bb2[1], bb2[2], bb2[3], bb2[4], bb2[5], bb2[6], bb3[0], bb3[1]}
// | '_#3r: {bb2[5], bb2[6], bb3[0], bb3[1]}
// END rustc.main.nll.0.mir

View File

@ -0,0 +1,33 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
#![allow(warnings)]
struct Wrap<'p> { p: &'p mut i32 }
impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}
struct Foo<'p> { a: String, b: Wrap<'p> }
fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
let s = String::from("str");
let foo = Foo { a: s, b: wrap };
std::mem::drop(foo.b);
x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
// FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones
}

View File

@ -0,0 +1,11 @@
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5
|
27 | let wrap = Wrap { p: &mut x };
| ------ borrow of `x` occurs here
...
31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
error: aborting due to previous error

View File

@ -0,0 +1,28 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
#![allow(warnings)]
struct Wrap<'p> { p: &'p mut i32 }
impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}
fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
std::mem::drop(wrap);
x = 1; // OK, drop is inert
}

View File

@ -0,0 +1,32 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
#![allow(warnings)]
struct Wrap<'p> { p: &'p mut i32 }
impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}
struct Foo<'p> { a: String, b: Wrap<'p> }
fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
let s = String::from("str");
let foo = Foo { a: s, b: wrap };
std::mem::drop(foo.a);
x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
}

View File

@ -0,0 +1,11 @@
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/maybe-initialized-drop-with-fragment.rs:31:5
|
27 | let wrap = Wrap { p: &mut x };
| ------ borrow of `x` occurs here
...
31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
error: aborting due to previous error

View File

@ -0,0 +1,34 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
#![allow(warnings)]
struct Wrap<'p> { p: &'p mut i32 }
impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}
struct Foo<'p> { a: String, b: Wrap<'p> }
fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
let s = String::from("str");
let foo = Foo { a: s, b: wrap };
std::mem::drop(foo.a);
std::mem::drop(foo.b);
x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
// FIXME ^ This currently errors and it should not.
}

View File

@ -0,0 +1,11 @@
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:32:5
|
27 | let wrap = Wrap { p: &mut x };
| ------ borrow of `x` occurs here
...
32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
error: aborting due to previous error

View File

@ -0,0 +1,27 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
#![allow(warnings)]
struct Wrap<'p> { p: &'p mut i32 }
impl<'p> Drop for Wrap<'p> {
fn drop(&mut self) {
*self.p += 1;
}
}
fn main() {
let mut x = 0;
let wrap = Wrap { p: &mut x };
x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
}

View File

@ -0,0 +1,10 @@
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/maybe-initialized-drop.rs:26:5
|
25 | let wrap = Wrap { p: &mut x };
| ------ borrow of `x` occurs here
26 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
error: aborting due to previous error