diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 05b131dc355c2..96a980a15457e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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: ®ion::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") diff --git a/src/librustc/infer/outlives/bounds.rs b/src/librustc/infer/outlives/bounds.rs new file mode 100644 index 0000000000000..8a562471ac5d0 --- /dev/null +++ b/src/librustc/infer/outlives/bounds.rs @@ -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 or the MIT license +// , 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> { + 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` 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` + // 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>, + ) -> Vec> { + 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> + '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), + ), + }) +} diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 2099e923e0959..d47507592f80d 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -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(¶m_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); - - // 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); - } - } - } + let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty, span); + self.add_outlives_bounds(Some(infcx), implied_bounds) } } - /// 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( + /// 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( &mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - ty: Ty<'tcx>, - span: Span, - ) -> Vec> { - 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` 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` - // 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) - } - } - } + infcx: Option<&InferCtxt<'a, 'gcx, 'tcx>>, + outlives_bounds: I, + ) where + I: IntoIterator>, + { + // 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); } - })); - } - - // 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>, - ) -> Vec> { - 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![], + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs + .push((r_a, GenericKind::Param(param_b))); } - }) - .collect() + 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); + } + } + } } } diff --git a/src/librustc/infer/outlives/free_region_map.rs b/src/librustc/infer/outlives/free_region_map.rs new file mode 100644 index 0000000000000..2127c4714aef0 --- /dev/null +++ b/src/librustc/infer/outlives/free_region_map.rs @@ -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 or the MIT license +// , 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> +} + +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> { + self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx)) + .map(|relation| FreeRegionMap { relation }) + } +} diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index 0976c5f1f2fc4..6aafebe79c671 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -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; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 3c3aba372fbd0..07eacde0aab88 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -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() diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 89c3f1668474a..ca6a5dd7f5b0b 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -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> -} - -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> { - self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx)) - .map(|relation| FreeRegionMap { relation }) - } -} diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index f56c3853de0ab..93e33836818ce 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -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>` to `Binder`. 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 => { diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 8d1bba4332276..d6f8a5f9cc6a1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -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, ®ion_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, ®ion_scope_tree, &outlives_env); + let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, Err(fixup_err) => { diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 0cc755dc42727..429771cca9844 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -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 diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 4bc3e2dd4d8d4..91e6c4270b32a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -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); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 0ab769d4fe307..ce05acb01b001 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -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; diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index bee119992230f..069dc0275cbb1 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -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(self, value: &Binder, 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( + &self, + all_outlive_scope: DefId, + value: &ty::Binder + ) -> 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(self, bound2_value: &Binder>) @@ -364,16 +389,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Binder(value) } - pub fn no_late_bound_regions(self, value: &Binder) -> Option - 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 diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 07573a48c03ee..afe999cede70d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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 diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index caf78309cc238..c115b573a1c06 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -680,6 +680,26 @@ impl Binder { { 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 + 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 diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 23dd3f1bc2bba..129badc46d8c1 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -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>> + { + 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> { diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 933e08811ce5d..ba7ab0c07c66a 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -134,12 +134,13 @@ impl TransitiveRelation { } } - /// 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`, 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 TransitiveRelation { /// 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 TransitiveRelation { // 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 TransitiveRelation { .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(&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)); +} diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 78ce959e5c94e..0818b929ee7ad 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -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(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, ®ion_scope_tree, &free_regions); + infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &outlives_env); assert_eq!(tcx.sess.err_count(), expected_err_count); }); }); diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check/mod.rs similarity index 60% rename from src/librustc_mir/borrow_check.rs rename to src/librustc_mir/borrow_check/mod.rs index ee48d1d369d53..446aba3d3d72c 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -11,34 +11,34 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. use rustc::hir; -use rustc::hir::def_id::{DefId}; -use rustc::infer::{InferCtxt}; -use rustc::ty::{self, TyCtxt, ParamEnv}; +use rustc::hir::def_id::DefId; +use rustc::infer::InferCtxt; +use rustc::ty::{self, ParamEnv, TyCtxt}; use rustc::ty::maps::Providers; -use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Place, Local}; +use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place}; use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; -use transform::nll; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_set::{self, IdxSetBuf}; -use rustc_data_structures::indexed_vec::{Idx}; +use rustc_data_structures::indexed_vec::Idx; -use syntax::ast::{self}; +use syntax::ast; use syntax_pos::Span; -use dataflow::{do_dataflow}; -use dataflow::{MoveDataParamEnv}; +use dataflow::do_dataflow; +use dataflow::MoveDataParamEnv; use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; -use dataflow::{MovingOutStatements, EverInitializedLvals}; -use dataflow::{Borrows, BorrowData, BorrowIndex}; -use dataflow::move_paths::{MoveError, IllegalMoveOriginKind}; -use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult, MoveOutIndex}; +use dataflow::{EverInitializedLvals, MovingOutStatements}; +use dataflow::{BorrowData, BorrowIndex, Borrows}; +use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; +use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex}; use util::borrowck_errors::{BorrowckErrors, Origin}; use self::MutateMode::{JustWrite, WriteAndRead}; +pub(crate) mod nll; pub fn provide(providers: &mut Providers) { *providers = Providers { @@ -52,9 +52,8 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); if { - !tcx.has_attr(def_id, "rustc_mir_borrowck") && - !tcx.sess.opts.borrowck_mode.use_mir() && - !tcx.sess.opts.debugging_opts.nll + !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir() + && !tcx.sess.opts.debugging_opts.nll } { return; } @@ -66,35 +65,58 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { debug!("mir_borrowck done"); } -fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, - input_mir: &Mir<'gcx>, - def_id: DefId) -{ +fn do_mir_borrowck<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + input_mir: &Mir<'gcx>, + def_id: DefId, +) { let tcx = infcx.tcx; let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - let id = tcx.hir.as_local_node_id(def_id) + let id = tcx.hir + .as_local_node_id(def_id) .expect("do_mir_borrowck: non-local DefId"); - let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx) { + // Make our own copy of the MIR. This copy will be modified (in place) to + // contain non-lexical lifetimes. It will have a lifetime tied + // to the inference context. + let mut mir: Mir<'tcx> = input_mir.clone(); + let free_regions = if !tcx.sess.opts.debugging_opts.nll { + None + } else { + let mir = &mut mir; + + // Replace all regions with fresh inference variables. + Some(nll::replace_regions_in_mir(infcx, def_id, mir)) + }; + let mir = &mir; + + let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { Ok(move_data) => move_data, Err((move_data, move_errors)) => { for move_error in move_errors { let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { - MoveError::UnionMove { .. } => - unimplemented!("dont know how to report union move errors yet."), - MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind), + MoveError::UnionMove { .. } => { + unimplemented!("dont know how to report union move errors yet.") + } + MoveError::IllegalMove { + cannot_move_out_of: o, + } => (o.span, o.kind), }; let origin = Origin::Mir; let mut err = match kind { - IllegalMoveOriginKind::Static => - tcx.cannot_move_out_of(span, "static item", origin), - IllegalMoveOriginKind::BorrowedContent => - tcx.cannot_move_out_of(span, "borrowed content", origin), - IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => - tcx.cannot_move_out_of_interior_of_drop(span, ty, origin), - IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => - tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), + IllegalMoveOriginKind::Static => { + tcx.cannot_move_out_of(span, "static item", origin) + } + IllegalMoveOriginKind::BorrowedContent => { + tcx.cannot_move_out_of(span, "borrowed content", origin) + } + IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { + tcx.cannot_move_out_of_interior_of_drop(span, ty, origin) + } + IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => { + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin) + } }; err.emit(); } @@ -102,36 +124,64 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } }; - // Make our own copy of the MIR. This copy will be modified (in place) to - // contain non-lexical lifetimes. It will have a lifetime tied - // to the inference context. - let mut mir: Mir<'tcx> = input_mir.clone(); - let mir = &mut mir; + let mdpe = MoveDataParamEnv { + move_data: move_data, + param_env: param_env, + }; + let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); + let mut flow_inits = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MaybeInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i], + )); + let flow_uninits = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MaybeUninitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().move_paths[i], + )); + let flow_move_outs = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + MovingOutStatements::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().moves[i], + )); + let flow_ever_inits = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + EverInitializedLvals::new(tcx, mir, &mdpe), + |bd, i| &bd.move_data().inits[i], + )); // If we are in non-lexical mode, compute the non-lexical lifetimes. - let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { - None + let opt_regioncx = if let Some(free_regions) = free_regions { + Some(nll::compute_regions( + infcx, + def_id, + free_regions, + mir, + param_env, + &mut flow_inits, + &mdpe.move_data, + )) } else { - Some(nll::compute_regions(infcx, def_id, param_env, mir)) + assert!(!tcx.sess.opts.debugging_opts.nll); + None }; - - let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; - let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir, opt_regioncx.as_ref()), - |bd, i| bd.location(i)); - let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeInitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MaybeUninitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().move_paths[i]); - let flow_move_outs = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - MovingOutStatements::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().moves[i]); - let flow_ever_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - EverInitializedLvals::new(tcx, mir, &mdpe), - |bd, i| &bd.move_data().inits[i]); + let flow_inits = flow_inits; // remove mut let mut mbcx = MirBorrowckCtxt { tcx: tcx, @@ -142,11 +192,23 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, storage_dead_or_drop_error_reported: FxHashSet(), }; - let mut state = InProgress::new(flow_borrows, - flow_inits, - flow_uninits, - flow_move_outs, - flow_ever_inits); + let flow_borrows = FlowInProgress::new(do_dataflow( + tcx, + mir, + id, + &attributes, + &dead_unwinds, + Borrows::new(tcx, mir, opt_regioncx), + |bd, i| bd.location(i), + )); + + let mut state = InProgress::new( + flow_borrows, + flow_inits, + flow_uninits, + flow_move_outs, + flow_ever_inits, + ); mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer } @@ -173,7 +235,10 @@ pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> { ever_inits: FlowInProgress>, } -struct FlowInProgress where BD: BitDenotation { +struct FlowInProgress +where + BD: BitDenotation, +{ base_results: DataflowResults, curr_state: IdxSetBuf, stmt_gen: IdxSetBuf, @@ -188,59 +253,76 @@ struct FlowInProgress where BD: BitDenotation { impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> { type FlowState = InProgress<'cx, 'gcx, 'tcx>; - fn mir(&self) -> &'cx Mir<'tcx> { self.mir } + fn mir(&self) -> &'cx Mir<'tcx> { + self.mir + } fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reset_to_entry_of(bb), - |i| i.reset_to_entry_of(bb), - |u| u.reset_to_entry_of(bb), - |m| m.reset_to_entry_of(bb), - |e| e.reset_to_entry_of(bb)); + flow_state.each_flow( + |b| b.reset_to_entry_of(bb), + |i| i.reset_to_entry_of(bb), + |u| u.reset_to_entry_of(bb), + |m| m.reset_to_entry_of(bb), + |e| e.reset_to_entry_of(bb), + ); } - fn reconstruct_statement_effect(&mut self, - location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reconstruct_statement_effect(location), - |i| i.reconstruct_statement_effect(location), - |u| u.reconstruct_statement_effect(location), - |m| m.reconstruct_statement_effect(location), - |e| e.reconstruct_statement_effect(location)); + fn reconstruct_statement_effect( + &mut self, + location: Location, + flow_state: &mut Self::FlowState, + ) { + flow_state.each_flow( + |b| b.reconstruct_statement_effect(location), + |i| i.reconstruct_statement_effect(location), + |u| u.reconstruct_statement_effect(location), + |m| m.reconstruct_statement_effect(location), + |e| e.reconstruct_statement_effect(location), + ); } - fn apply_local_effect(&mut self, - _location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.apply_local_effect(), - |i| i.apply_local_effect(), - |u| u.apply_local_effect(), - |m| m.apply_local_effect(), - |e| e.apply_local_effect()); + fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) { + flow_state.each_flow( + |b| b.apply_local_effect(), + |i| i.apply_local_effect(), + |u| u.apply_local_effect(), + |m| m.apply_local_effect(), + |e| e.apply_local_effect(), + ); } - fn reconstruct_terminator_effect(&mut self, - location: Location, - flow_state: &mut Self::FlowState) { - flow_state.each_flow(|b| b.reconstruct_terminator_effect(location), - |i| i.reconstruct_terminator_effect(location), - |u| u.reconstruct_terminator_effect(location), - |m| m.reconstruct_terminator_effect(location), - |e| e.reconstruct_terminator_effect(location)); + fn reconstruct_terminator_effect( + &mut self, + location: Location, + flow_state: &mut Self::FlowState, + ) { + flow_state.each_flow( + |b| b.reconstruct_terminator_effect(location), + |i| i.reconstruct_terminator_effect(location), + |u| u.reconstruct_terminator_effect(location), + |m| m.reconstruct_terminator_effect(location), + |e| e.reconstruct_terminator_effect(location), + ); } - fn visit_block_entry(&mut self, - bb: BasicBlock, - flow_state: &Self::FlowState) { + fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) { let summary = flow_state.summary(); debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary); } - fn visit_statement_entry(&mut self, - location: Location, - stmt: &Statement<'tcx>, - flow_state: &Self::FlowState) { + fn visit_statement_entry( + &mut self, + location: Location, + stmt: &Statement<'tcx>, + flow_state: &Self::FlowState, + ) { let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary); + debug!( + "MirBorrowckCtxt::process_statement({:?}, {:?}): {}", + location, + stmt, + summary + ); let span = stmt.source_info.span; match stmt.kind { StatementKind::Assign(ref lhs, ref rhs) => { @@ -249,38 +331,64 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // to shallow requires to dataflow: "if this is an // assignment `place = `, then any loan for some // path P of which `place` is a prefix is killed." - self.mutate_place(ContextKind::AssignLhs.new(location), - (lhs, span), Deep, JustWrite, flow_state); - - self.consume_rvalue(ContextKind::AssignRhs.new(location), - (rhs, span), location, flow_state); + self.mutate_place( + ContextKind::AssignLhs.new(location), + (lhs, span), + Deep, + JustWrite, + flow_state, + ); + + self.consume_rvalue( + ContextKind::AssignRhs.new(location), + (rhs, span), + location, + flow_state, + ); } - StatementKind::SetDiscriminant { ref place, variant_index: _ } => { - self.mutate_place(ContextKind::SetDiscrim.new(location), - (place, span), - Shallow(Some(ArtificialField::Discriminant)), - JustWrite, - flow_state); + StatementKind::SetDiscriminant { + ref place, + variant_index: _, + } => { + self.mutate_place( + ContextKind::SetDiscrim.new(location), + (place, span), + Shallow(Some(ArtificialField::Discriminant)), + JustWrite, + flow_state, + ); } - StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { + StatementKind::InlineAsm { + ref asm, + ref outputs, + ref inputs, + } => { let context = ContextKind::InlineAsm.new(location); for (o, output) in asm.outputs.iter().zip(outputs) { if o.is_indirect { // FIXME(eddyb) indirect inline asm outputs should // be encoeded through MIR place derefs instead. - self.access_place(context, - (output, span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state); - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (output, span), flow_state); + self.access_place( + context, + (output, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (output, span), + flow_state, + ); } else { - self.mutate_place(context, - (output, span), - Deep, - if o.is_rw { WriteAndRead } else { JustWrite }, - flow_state); + self.mutate_place( + context, + (output, span), + Deep, + if o.is_rw { WriteAndRead } else { JustWrite }, + flow_state, + ); } } for input in inputs { @@ -291,114 +399,158 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx // ignored when consuming results (update to // flow_state already handled). } - StatementKind::Nop | - StatementKind::Validate(..) | - StatementKind::StorageLive(..) => { + StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => { // `Nop`, `Validate`, and `StorageLive` are irrelevant // to borrow check. } StatementKind::StorageDead(local) => { - self.access_place(ContextKind::StorageDead.new(location), + self.access_place( + ContextKind::StorageDead.new(location), (&Place::Local(local), span), (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, - flow_state); + flow_state, + ); } } } - fn visit_terminator_entry(&mut self, - location: Location, - term: &Terminator<'tcx>, - flow_state: &Self::FlowState) { + fn visit_terminator_entry( + &mut self, + location: Location, + term: &Terminator<'tcx>, + flow_state: &Self::FlowState, + ) { let loc = location; let summary = flow_state.summary(); - debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location, term, summary); + debug!( + "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", + location, + term, + summary + ); let span = term.source_info.span; match term.kind { - TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { - self.consume_operand(ContextKind::SwitchInt.new(loc), - (discr, span), flow_state); + TerminatorKind::SwitchInt { + ref discr, + switch_ty: _, + values: _, + targets: _, + } => { + self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state); } - TerminatorKind::Drop { location: ref drop_place, target: _, unwind: _ } => { - self.access_place(ContextKind::Drop.new(loc), - (drop_place, span), - (Deep, Write(WriteKind::StorageDeadOrDrop)), - LocalMutationIsAllowed::Yes, - flow_state); + TerminatorKind::Drop { + location: ref drop_place, + target: _, + unwind: _, + } => { + self.access_place( + ContextKind::Drop.new(loc), + (drop_place, span), + (Deep, Write(WriteKind::StorageDeadOrDrop)), + LocalMutationIsAllowed::Yes, + flow_state, + ); } - TerminatorKind::DropAndReplace { location: ref drop_place, - value: ref new_value, - target: _, - unwind: _ } => { - self.mutate_place(ContextKind::DropAndReplace.new(loc), - (drop_place, span), - Deep, - JustWrite, - flow_state); - self.consume_operand(ContextKind::DropAndReplace.new(loc), - (new_value, span), flow_state); + TerminatorKind::DropAndReplace { + location: ref drop_place, + value: ref new_value, + target: _, + unwind: _, + } => { + self.mutate_place( + ContextKind::DropAndReplace.new(loc), + (drop_place, span), + Deep, + JustWrite, + flow_state, + ); + self.consume_operand( + ContextKind::DropAndReplace.new(loc), + (new_value, span), + flow_state, + ); } - TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - self.consume_operand(ContextKind::CallOperator.new(loc), - (func, span), flow_state); + TerminatorKind::Call { + ref func, + ref args, + ref destination, + cleanup: _, + } => { + self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state); for arg in args { - self.consume_operand(ContextKind::CallOperand.new(loc), - (arg, span), flow_state); + self.consume_operand( + ContextKind::CallOperand.new(loc), + (arg, span), + flow_state, + ); } - if let Some((ref dest, _/*bb*/)) = *destination { - self.mutate_place(ContextKind::CallDest.new(loc), - (dest, span), - Deep, - JustWrite, - flow_state); + if let Some((ref dest, _ /*bb*/)) = *destination { + self.mutate_place( + ContextKind::CallDest.new(loc), + (dest, span), + Deep, + JustWrite, + flow_state, + ); } } - TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { - self.consume_operand(ContextKind::Assert.new(loc), - (cond, span), flow_state); + TerminatorKind::Assert { + ref cond, + expected: _, + ref msg, + target: _, + cleanup: _, + } => { + self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state); match *msg { AssertMessage::BoundsCheck { ref len, ref index } => { - self.consume_operand(ContextKind::Assert.new(loc), - (len, span), flow_state); - self.consume_operand(ContextKind::Assert.new(loc), - (index, span), flow_state); + self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state); + self.consume_operand( + ContextKind::Assert.new(loc), + (index, span), + flow_state, + ); } - AssertMessage::Math(_/*const_math_err*/) => {} + AssertMessage::Math(_ /*const_math_err*/) => {} AssertMessage::GeneratorResumedAfterReturn => {} AssertMessage::GeneratorResumedAfterPanic => {} } } - TerminatorKind::Yield { ref value, resume: _, drop: _} => { - self.consume_operand(ContextKind::Yield.new(loc), - (value, span), flow_state); + TerminatorKind::Yield { + ref value, + resume: _, + drop: _, + } => { + self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state); } - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop => { + TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => { // Returning from the function implicitly kills storage for all locals and statics. // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), // so this "extra check" serves as a kind of backup. let domain = flow_state.borrows.base_results.operator(); let data = domain.borrows(); - flow_state.borrows.with_elems_outgoing(|borrows| for i in borrows { - let borrow = &data[i]; - - if self.place_is_invalidated_at_exit(&borrow.place) { - debug!("borrow conflicts at exit {:?}", borrow); - let borrow_span = self.mir.source_info(borrow.location).span; - // FIXME: should be talking about the region lifetime instead - // of just a span here. - let end_span = domain.opt_region_end_span(&borrow.region); - - self.report_borrowed_value_does_not_live_long_enough( - ContextKind::StorageDead.new(loc), - (&borrow.place, borrow_span), - end_span) + flow_state.borrows.with_elems_outgoing(|borrows| { + for i in borrows { + let borrow = &data[i]; + + if self.place_is_invalidated_at_exit(&borrow.place) { + debug!("borrow conflicts at exit {:?}", borrow); + let borrow_span = self.mir.source_info(borrow.location).span; + // FIXME: should be talking about the region lifetime instead + // of just a span here. + let end_span = domain.opt_region_end_span(&borrow.region); + + self.report_borrowed_value_does_not_live_long_enough( + ContextKind::StorageDead.new(loc), + (&borrow.place, borrow_span), + end_span, + ) + } } }); } @@ -412,12 +564,18 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum MutateMode { JustWrite, WriteAndRead } +enum MutateMode { + JustWrite, + WriteAndRead, +} #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum Control { Continue, Break } +enum Control { + Continue, + Break, +} -use self::ShallowOrDeep::{Shallow, Deep}; +use self::ShallowOrDeep::{Deep, Shallow}; use self::ReadOrWrite::{Read, Write}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -484,7 +642,7 @@ enum WriteKind { #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum LocalMutationIsAllowed { Yes, - No + No, } #[derive(Copy, Clone)] @@ -498,19 +656,19 @@ enum InitializationRequiringAction { impl InitializationRequiringAction { fn as_noun(self) -> &'static str { match self { - InitializationRequiringAction::Update => "update", - InitializationRequiringAction::Borrow => "borrow", - InitializationRequiringAction::Use => "use", - InitializationRequiringAction::Assignment => "assign" + InitializationRequiringAction::Update => "update", + InitializationRequiringAction::Borrow => "borrow", + InitializationRequiringAction::Use => "use", + InitializationRequiringAction::Assignment => "assign", } } fn as_verb_in_past_tense(self) -> &'static str { match self { - InitializationRequiringAction::Update => "updated", - InitializationRequiringAction::Borrow => "borrowed", - InitializationRequiringAction::Use => "used", - InitializationRequiringAction::Assignment => "assigned" + InitializationRequiringAction::Update => "updated", + InitializationRequiringAction::Borrow => "borrowed", + InitializationRequiringAction::Use => "used", + InitializationRequiringAction::Assignment => "assigned", } } } @@ -522,17 +680,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// access. /// /// Returns true if an error is reported, false otherwise. - fn access_place(&mut self, - context: Context, - place_span: (&Place<'tcx>, Span), - kind: (ShallowOrDeep, ReadOrWrite), - is_local_mutation_allowed: LocalMutationIsAllowed, - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn access_place( + &mut self, + context: Context, + place_span: (&Place<'tcx>, Span), + kind: (ShallowOrDeep, ReadOrWrite), + is_local_mutation_allowed: LocalMutationIsAllowed, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { let (sd, rw) = kind; let storage_dead_or_drop_local = match (place_span.0, rw) { (&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local), - _ => None + _ => None, }; // Check if error has already been reported to stop duplicate reporting. @@ -543,70 +703,84 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } // Check permissions - let mut error_reported = self.check_access_permissions(place_span, - rw, - is_local_mutation_allowed); + let mut error_reported = + self.check_access_permissions(place_span, rw, is_local_mutation_allowed); self.each_borrow_involving_path( - context, (sd, place_span.0), flow_state, |this, _index, borrow, common_prefix| { - match (rw, borrow.kind) { - (Read(_), BorrowKind::Shared) => { - Control::Continue - } - (Read(kind), BorrowKind::Unique) | - (Read(kind), BorrowKind::Mut) => { - match kind { - ReadKind::Copy => { - error_reported = true; - this.report_use_while_mutably_borrowed( - context, place_span, borrow) - }, - ReadKind::Borrow(bk) => { - let end_issued_loan_span = - flow_state.borrows.base_results.operator().opt_region_end_span( - &borrow.region); - error_reported = true; - this.report_conflicting_borrow( - context, common_prefix, place_span, bk, - &borrow, end_issued_loan_span) - } + context, + (sd, place_span.0), + flow_state, + |this, _index, borrow, common_prefix| match (rw, borrow.kind) { + (Read(_), BorrowKind::Shared) => Control::Continue, + (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => { + match kind { + ReadKind::Copy => { + error_reported = true; + this.report_use_while_mutably_borrowed(context, place_span, borrow) + } + ReadKind::Borrow(bk) => { + let end_issued_loan_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_conflicting_borrow( + context, + common_prefix, + place_span, + bk, + &borrow, + end_issued_loan_span, + ) } - Control::Break } - (Write(kind), _) => { - match kind { - WriteKind::MutableBorrow(bk) => { - let end_issued_loan_span = - flow_state.borrows.base_results.operator().opt_region_end_span( - &borrow.region); - error_reported = true; - this.report_conflicting_borrow( - context, common_prefix, place_span, bk, - &borrow, end_issued_loan_span) - } - WriteKind::StorageDeadOrDrop => { - let end_span = - flow_state.borrows.base_results.operator().opt_region_end_span( - &borrow.region); - error_reported = true; - this.report_borrowed_value_does_not_live_long_enough( - context, place_span, end_span) - }, - WriteKind::Mutate => { - error_reported = true; - this.report_illegal_mutation_of_borrowed( - context, place_span, borrow) - }, - WriteKind::Move => { - error_reported = true; - this.report_move_out_while_borrowed( - context, place_span, &borrow) - }, + Control::Break + } + (Write(kind), _) => { + match kind { + WriteKind::MutableBorrow(bk) => { + let end_issued_loan_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_conflicting_borrow( + context, + common_prefix, + place_span, + bk, + &borrow, + end_issued_loan_span, + ) + } + WriteKind::StorageDeadOrDrop => { + let end_span = flow_state + .borrows + .base_results + .operator() + .opt_region_end_span(&borrow.region); + error_reported = true; + this.report_borrowed_value_does_not_live_long_enough( + context, + place_span, + end_span, + ) + } + WriteKind::Mutate => { + error_reported = true; + this.report_illegal_mutation_of_borrowed(context, place_span, borrow) + } + WriteKind::Move => { + error_reported = true; + this.report_move_out_while_borrowed(context, place_span, &borrow) } - Control::Break } + Control::Break } - }); + }, + ); if error_reported { if let Some(local) = storage_dead_or_drop_local { @@ -615,75 +789,97 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn mutate_place(&mut self, - context: Context, - place_span: (&Place<'tcx>, Span), - kind: ShallowOrDeep, - mode: MutateMode, - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn mutate_place( + &mut self, + context: Context, + place_span: (&Place<'tcx>, Span), + kind: ShallowOrDeep, + mode: MutateMode, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd. match mode { MutateMode::WriteAndRead => { - self.check_if_path_is_moved(context, InitializationRequiringAction::Update, - place_span, flow_state); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Update, + place_span, + flow_state, + ); } MutateMode::JustWrite => { self.check_if_assigned_path_is_moved(context, place_span, flow_state); } } - self.access_place(context, - place_span, - (kind, Write(WriteKind::Mutate)), - LocalMutationIsAllowed::Yes, - flow_state); + self.access_place( + context, + place_span, + (kind, Write(WriteKind::Mutate)), + LocalMutationIsAllowed::Yes, + flow_state, + ); // check for reassignments to immutable local variables self.check_if_reassignment_to_immutable_state(context, place_span, flow_state); } - fn consume_rvalue(&mut self, - context: Context, - (rvalue, span): (&Rvalue<'tcx>, Span), - _location: Location, - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn consume_rvalue( + &mut self, + context: Context, + (rvalue, span): (&Rvalue<'tcx>, Span), + _location: Location, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { match *rvalue { - Rvalue::Ref(_/*rgn*/, bk, ref place) => { + Rvalue::Ref(_ /*rgn*/, bk, ref place) => { let access_kind = match bk { BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))), - BorrowKind::Unique | - BorrowKind::Mut => (Deep, Write(WriteKind::MutableBorrow(bk))), + BorrowKind::Unique | BorrowKind::Mut => { + (Deep, Write(WriteKind::MutableBorrow(bk))) + } }; - self.access_place(context, - (place, span), - access_kind, - LocalMutationIsAllowed::No, - flow_state); - self.check_if_path_is_moved(context, InitializationRequiringAction::Borrow, - (place, span), flow_state); + self.access_place( + context, + (place, span), + access_kind, + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Borrow, + (place, span), + flow_state, + ); } Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | - Rvalue::UnaryOp(_/*un_op*/, ref operand) | - Rvalue::Cast(_/*cast_kind*/, ref operand, _/*ty*/) => { + Rvalue::UnaryOp(_ /*un_op*/, ref operand) | + Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => { self.consume_operand(context, (operand, span), flow_state) } - Rvalue::Len(ref place) | - Rvalue::Discriminant(ref place) => { + Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => { let af = match *rvalue { Rvalue::Len(..) => ArtificialField::ArrayLength, Rvalue::Discriminant(..) => ArtificialField::Discriminant, _ => unreachable!(), }; - self.access_place(context, - (place, span), - (Shallow(Some(af)), Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state); - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (place, span), flow_state); + self.access_place( + context, + (place, span), + (Shallow(Some(af)), Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); } Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) | @@ -700,43 +896,55 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `NullOp::Box`? } - Rvalue::Aggregate(ref _aggregate_kind, ref operands) => { - for operand in operands { - self.consume_operand(context, (operand, span), flow_state); - } - } + Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands { + self.consume_operand(context, (operand, span), flow_state); + }, } } - fn consume_operand(&mut self, - context: Context, - (operand, span): (&Operand<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn consume_operand( + &mut self, + context: Context, + (operand, span): (&Operand<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { match *operand { Operand::Copy(ref place) => { // copy of place: check if this is "copy of frozen path" // (FIXME: see check_loans.rs) - self.access_place(context, - (place, span), - (Deep, Read(ReadKind::Copy)), - LocalMutationIsAllowed::No, - flow_state); + self.access_place( + context, + (place, span), + (Deep, Read(ReadKind::Copy)), + LocalMutationIsAllowed::No, + flow_state, + ); // Finally, check if path was already moved. - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (place, span), flow_state); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); } Operand::Move(ref place) => { // move of place: check if this is move of already borrowed path - self.access_place(context, - (place, span), - (Deep, Write(WriteKind::Move)), - LocalMutationIsAllowed::Yes, - flow_state); + self.access_place( + context, + (place, span), + (Deep, Write(WriteKind::Move)), + LocalMutationIsAllowed::Yes, + flow_state, + ); // Finally, check if path was already moved. - self.check_if_path_is_moved(context, InitializationRequiringAction::Use, - (place, span), flow_state); + self.check_if_path_is_moved( + context, + InitializationRequiringAction::Use, + (place, span), + flow_state, + ); } Operand::Constant(_) => {} } @@ -755,9 +963,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Static(statik) => { // Thread-locals might be dropped after the function exits, but // "true" statics will never be. - let is_thread_local = self.tcx.get_attrs(statik.def_id).iter().any(|attr| { - attr.check_name("thread_local") - }); + let is_thread_local = self.tcx + .get_attrs(statik.def_id) + .iter() + .any(|attr| attr.check_name("thread_local")); (true, is_thread_local) } @@ -766,12 +975,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // have a destructor it would've been called already. (false, true) } - Place::Projection(..) => bug!("root of {:?} is a projection ({:?})?", - place, root_place) + Place::Projection(..) => { + bug!("root of {:?} is a projection ({:?})?", place, root_place) + } }; if !will_be_dropped { - debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place); + debug!( + "place_is_invalidated_at_exit({:?}) - won't be dropped", + place + ); return false; } @@ -783,15 +996,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { PrefixSet::Shallow }; - self.prefixes(place, prefix_set).any(|prefix| prefix == root_place) + self.prefixes(place, prefix_set) + .any(|prefix| prefix == root_place) } } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn check_if_reassignment_to_immutable_state(&mut self, - context: Context, - (place, span): (&Place<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn check_if_reassignment_to_immutable_state( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { let move_data = self.move_data; // determine if this path has a non-mut owner (and thus needs checking). @@ -804,33 +1020,37 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } match self.move_path_closest_to(place) { - Ok(mpi) => { - for ii in &move_data.init_path_map[mpi] { - if flow_state.ever_inits.curr_state.contains(ii) { - let first_assign_span = self.move_data.inits[*ii].span; - self.report_illegal_reassignment( - context, (place, span), first_assign_span); - break; - } + Ok(mpi) => for ii in &move_data.init_path_map[mpi] { + if flow_state.ever_inits.curr_state.contains(ii) { + let first_assign_span = self.move_data.inits[*ii].span; + self.report_illegal_reassignment(context, (place, span), first_assign_span); + break; } }, Err(NoMovePathFound::ReachedStatic) => { let item_msg = match self.describe_place(place) { Some(name) => format!("immutable static item `{}`", name), - None => "immutable static item".to_owned() + None => "immutable static item".to_owned(), }; - self.tcx.sess.delay_span_bug(span, - &format!("cannot assign to {}, should have been caught by \ - `check_access_permissions()`", item_msg)); - }, + self.tcx.sess.delay_span_bug( + span, + &format!( + "cannot assign to {}, should have been caught by \ + `check_access_permissions()`", + item_msg + ), + ); + } } } - fn check_if_path_is_moved(&mut self, - context: Context, - desired_action: InitializationRequiringAction, - place_span: (&Place<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn check_if_path_is_moved( + &mut self, + context: Context, + desired_action: InitializationRequiringAction, + place_span: (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { // FIXME: analogous code in check_loans first maps `place` to // its base_path ... but is that what we want here? let place = self.base_path(place_span.0); @@ -878,22 +1098,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { match self.move_path_closest_to(place) { Ok(mpi) => { if maybe_uninits.curr_state.contains(&mpi) { - self.report_use_of_moved_or_uninitialized(context, desired_action, - place_span, mpi, - curr_move_outs); + self.report_use_of_moved_or_uninitialized( + context, + desired_action, + place_span, + mpi, + curr_move_outs, + ); return; // don't bother finding other problems. } } Err(NoMovePathFound::ReachedStatic) => { // Okay: we do not build MoveData for static variables - } - - // Only query longest prefix with a MovePath, not further - // ancestors; dataflow recurs on children when parents - // move (to support partial (re)inits). - // - // (I.e. querying parents breaks scenario 8; but may want - // to do such a query based on partial-init feature-gate.) + } // Only query longest prefix with a MovePath, not further + // ancestors; dataflow recurs on children when parents + // move (to support partial (re)inits). + // + // (I.e. querying parents breaks scenario 8; but may want + // to do such a query based on partial-init feature-gate.) } // A move of any shallow suffix of `place` also interferes @@ -906,9 +1128,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { debug!("check_if_path_is_moved part2 place: {:?}", place); if let Some(mpi) = self.move_path_for_place(place) { if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) { - self.report_use_of_moved_or_uninitialized(context, desired_action, - place_span, child_mpi, - curr_move_outs); + self.report_use_of_moved_or_uninitialized( + context, + desired_action, + place_span, + child_mpi, + curr_move_outs, + ); return; // don't bother finding other problems. } } @@ -924,9 +1150,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// An Err result includes a tag indicated why the search failed. /// Currenly this can only occur if the place is built off of a /// static variable, as we do not track those in the MoveData. - fn move_path_closest_to(&mut self, place: &Place<'tcx>) - -> Result - { + fn move_path_closest_to( + &mut self, + place: &Place<'tcx>, + ) -> Result { let mut last_prefix = place; for prefix in self.prefixes(place, PrefixSet::All) { if let Some(mpi) = self.move_path_for_place(prefix) { @@ -941,10 +1168,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn move_path_for_place(&mut self, - place: &Place<'tcx>) - -> Option - { + fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option { // If returns None, then there is no move path corresponding // to a direct owner of `place` (which means there is nothing // that borrowck tracks for its analysis). @@ -955,10 +1179,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn check_if_assigned_path_is_moved(&mut self, - context: Context, - (place, span): (&Place<'tcx>, Span), - flow_state: &InProgress<'cx, 'gcx, 'tcx>) { + fn check_if_assigned_path_is_moved( + &mut self, + context: Context, + (place, span): (&Place<'tcx>, Span), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + ) { // recur down place; dispatch to check_if_path_is_moved when necessary let mut place = place; loop { @@ -1021,42 +1247,46 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// Check the permissions for the given place and read or write kind /// /// Returns true if an error is reported, false otherwise. - fn check_access_permissions(&self, - (place, span): (&Place<'tcx>, Span), - kind: ReadOrWrite, - is_local_mutation_allowed: LocalMutationIsAllowed) - -> bool { - debug!("check_access_permissions({:?}, {:?}, {:?})", - place, kind, is_local_mutation_allowed); + fn check_access_permissions( + &self, + (place, span): (&Place<'tcx>, Span), + kind: ReadOrWrite, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> bool { + debug!( + "check_access_permissions({:?}, {:?}, {:?})", + place, + kind, + is_local_mutation_allowed + ); let mut error_reported = false; match kind { Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { if let Err(_place_err) = self.is_unique(place) { span_bug!(span, "&unique borrow for {:?} should not fail", place); } - }, - Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => { - if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { - error_reported = true; + } + Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) = + self.is_mutable(place, is_local_mutation_allowed) + { + error_reported = true; - let item_msg = match self.describe_place(place) { - Some(name) => format!("immutable item `{}`", name), - None => "immutable item".to_owned() - }; + let item_msg = match self.describe_place(place) { + Some(name) => format!("immutable item `{}`", name), + None => "immutable item".to_owned(), + }; - let mut err = self.tcx.cannot_borrow_path_as_mutable(span, - &item_msg, - Origin::Mir); - err.span_label(span, "cannot borrow as mutable"); + let mut err = self.tcx + .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir); + err.span_label(span, "cannot borrow as mutable"); - if place != place_err { - if let Some(name) = self.describe_place(place_err) { - err.note(&format!("Value not mutable causing this error: `{}`", name)); - } + if place != place_err { + if let Some(name) = self.describe_place(place_err) { + err.note(&format!("Value not mutable causing this error: `{}`", name)); } - - err.emit(); } + + err.emit(); }, Write(WriteKind::Mutate) => { if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) { @@ -1064,12 +1294,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let item_msg = match self.describe_place(place) { Some(name) => format!("immutable item `{}`", name), - None => "immutable item".to_owned() + None => "immutable item".to_owned(), }; - let mut err = self.tcx.cannot_assign(span, - &item_msg, - Origin::Mir); + let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir); err.span_label(span, "cannot mutate"); if place != place_err { @@ -1080,17 +1308,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - }, + } Write(WriteKind::Move) | Write(WriteKind::StorageDeadOrDrop) | Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => { if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) { - self.tcx.sess.delay_span_bug(span, - &format!("Accessing `{:?}` with the kind `{:?}` shouldn't be possible", + self.tcx.sess.delay_span_bug( + span, + &format!( + "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", place, - kind)); + kind + ), + ); } - }, + } Read(ReadKind::Borrow(BorrowKind::Unique)) | Read(ReadKind::Borrow(BorrowKind::Mut)) | Read(ReadKind::Borrow(BorrowKind::Shared)) | @@ -1101,28 +1333,26 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } /// Can this value be written or borrowed mutably - fn is_mutable<'d>(&self, - place: &'d Place<'tcx>, - is_local_mutation_allowed: LocalMutationIsAllowed) - -> Result<(), &'d Place<'tcx>> { + fn is_mutable<'d>( + &self, + place: &'d Place<'tcx>, + is_local_mutation_allowed: LocalMutationIsAllowed, + ) -> Result<(), &'d Place<'tcx>> { match *place { Place::Local(local) => { let local = &self.mir.local_decls[local]; match local.mutability { - Mutability::Not => - match is_local_mutation_allowed { - LocalMutationIsAllowed::Yes => Ok(()), - LocalMutationIsAllowed::No => Err(place), - }, - Mutability::Mut => Ok(()) - } - }, - Place::Static(ref static_) => { - if !self.tcx.is_static_mut(static_.def_id) { - Err(place) - } else { - Ok(()) + Mutability::Not => match is_local_mutation_allowed { + LocalMutationIsAllowed::Yes => Ok(()), + LocalMutationIsAllowed::No => Err(place), + }, + Mutability::Mut => Ok(()), } + } + Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) { + Err(place) + } else { + Ok(()) }, Place::Projection(ref proj) => { match proj.elem { @@ -1143,9 +1373,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } else { self.is_unique(&proj.base) } - }, + } } - }, + } ty::TyRawPtr(tnm) => { match tnm.mutbl { // `*const` raw pointers are not mutable @@ -1154,20 +1384,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // The users have to check by themselve. hir::MutMutable => Ok(()), } - }, + } // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => - self.is_mutable(&proj.base, LocalMutationIsAllowed::No), + _ if base_ty.is_box() => { + self.is_mutable(&proj.base, LocalMutationIsAllowed::No) + } // Deref should only be for reference, pointers or boxes _ => bug!("Deref of unexpected type: {:?}", base_ty), } - }, + } // All other projections are owned by their base path, so mutable if // base path is mutable ProjectionElem::Field(..) | ProjectionElem::Index(..) | - ProjectionElem::ConstantIndex{..} | - ProjectionElem::Subslice{..} | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..) => { let field_projection = self.is_upvar_field_projection(place); @@ -1193,11 +1424,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Local(..) => { // Local variables are unique Ok(()) - }, + } Place::Static(..) => { // Static variables are not Err(place) - }, + } Place::Projection(ref proj) => { match proj.elem { ProjectionElem::Deref => { @@ -1217,7 +1448,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `&mut T` is as unique as the context in which it is found hir::MutMutable => self.is_unique(&proj.base), } - }, + } ty::TyRawPtr(tnm) => { match tnm.mutbl { // `*mut` can be aliased, but we leave it to user @@ -1225,18 +1456,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // `*const` is treated the same as `*mut` hir::MutImmutable => Ok(()), } - }, + } // Deref should only be for reference, pointers or boxes - _ => bug!("Deref of unexpected type: {:?}", base_ty) + _ => bug!("Deref of unexpected type: {:?}", base_ty), } - }, + } // Other projections are unique if the base is unique ProjectionElem::Field(..) | ProjectionElem::Index(..) | - ProjectionElem::ConstantIndex{..} | - ProjectionElem::Subslice{..} | - ProjectionElem::Downcast(..) => - self.is_unique(&proj.base) + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } | + ProjectionElem::Downcast(..) => self.is_unique(&proj.base), } } } @@ -1249,12 +1479,14 @@ enum NoMovePathFound { } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn each_borrow_involving_path(&mut self, - _context: Context, - access_place: (ShallowOrDeep, &Place<'tcx>), - flow_state: &InProgress<'cx, 'gcx, 'tcx>, - mut op: F) - where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control + fn each_borrow_involving_path( + &mut self, + _context: Context, + access_place: (ShallowOrDeep, &Place<'tcx>), + flow_state: &InProgress<'cx, 'gcx, 'tcx>, + mut op: F, + ) where + F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control, { let (access, place) = access_place; @@ -1278,7 +1510,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if *accessed_prefix == borrowed.place { // FIXME: pass in enum describing case we are in? let ctrl = op(self, i, borrowed, accessed_prefix); - if ctrl == Control::Break { return; } + if ctrl == Control::Break { + return; + } } } @@ -1304,7 +1538,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if borrowed_prefix == place { // FIXME: pass in enum describing case we are in? let ctrl = op(self, i, borrowed, borrowed_prefix); - if ctrl == Control::Break { return; } + if ctrl == Control::Break { + return; + } } } } @@ -1322,11 +1558,11 @@ use self::prefixes::PrefixSet; /// is borrowed. But: writing `a` is legal if `*a` is borrowed, /// whether or not `a` is a shared or mutable reference. [...] " mod prefixes { - use super::{MirBorrowckCtxt}; + use super::MirBorrowckCtxt; use rustc::hir; use rustc::ty::{self, TyCtxt}; - use rustc::mir::{Place, Mir, ProjectionElem}; + use rustc::mir::{Mir, Place, ProjectionElem}; pub trait IsPrefixOf<'tcx> { fn is_prefix_of(&self, other: &Place<'tcx>) -> bool; @@ -1341,8 +1577,7 @@ mod prefixes { } match *cursor { - Place::Local(_) | - Place::Static(_) => return false, + Place::Local(_) | Place::Static(_) => return false, Place::Projection(ref proj) => { cursor = &proj.base; } @@ -1374,12 +1609,17 @@ mod prefixes { /// Returns an iterator over the prefixes of `place` /// (inclusive) from longest to smallest, potentially /// terminating the iteration early based on `kind`. - pub(super) fn prefixes(&self, - place: &'cx Place<'tcx>, - kind: PrefixSet) - -> Prefixes<'cx, 'gcx, 'tcx> - { - Prefixes { next: Some(place), kind, mir: self.mir, tcx: self.tcx } + pub(super) fn prefixes( + &self, + place: &'cx Place<'tcx>, + kind: PrefixSet, + ) -> Prefixes<'cx, 'gcx, 'tcx> { + Prefixes { + next: Some(place), + kind, + mir: self.mir, + tcx: self.tcx, + } } } @@ -1408,7 +1648,7 @@ mod prefixes { }; match proj.elem { - ProjectionElem::Field(_/*field*/, _/*ty*/) => { + ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { // FIXME: add union handling self.next = Some(&proj.base); return Some(cursor); @@ -1454,13 +1694,25 @@ mod prefixes { let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); match ty.sty { ty::TyRawPtr(_) | - ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => { + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutImmutable, + }, + ) => { // don't continue traversing over derefs of raw pointers or shared borrows. self.next = None; return Some(cursor); } - ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => { + ty::TyRef( + _, /*rgn*/ + ty::TypeAndMut { + ty: _, + mutbl: hir::MutMutable, + }, + ) => { self.next = Some(&proj.base); return Some(cursor); } @@ -1478,47 +1730,59 @@ mod prefixes { } impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { - fn report_use_of_moved_or_uninitialized(&mut self, - _context: Context, - desired_action: InitializationRequiringAction, - (place, span): (&Place<'tcx>, Span), - mpi: MovePathIndex, - curr_move_out: &IdxSetBuf) { - - let mois = self.move_data.path_map[mpi].iter().filter( - |moi| curr_move_out.contains(moi)).collect::>(); + fn report_use_of_moved_or_uninitialized( + &mut self, + _context: Context, + desired_action: InitializationRequiringAction, + (place, span): (&Place<'tcx>, Span), + mpi: MovePathIndex, + curr_move_out: &IdxSetBuf, + ) { + let mois = self.move_data.path_map[mpi] + .iter() + .filter(|moi| curr_move_out.contains(moi)) + .collect::>(); if mois.is_empty() { let item_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; - self.tcx.cannot_act_on_uninitialized_variable(span, - desired_action.as_noun(), - &self.describe_place(place) - .unwrap_or("_".to_owned()), - Origin::Mir) - .span_label(span, format!("use of possibly uninitialized {}", item_msg)) - .emit(); + self.tcx + .cannot_act_on_uninitialized_variable( + span, + desired_action.as_noun(), + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label(span, format!("use of possibly uninitialized {}", item_msg)) + .emit(); } else { let msg = ""; //FIXME: add "partially " or "collaterally " - let mut err = self.tcx.cannot_act_on_moved_value(span, - desired_action.as_noun(), - msg, - &self.describe_place(place) - .unwrap_or("_".to_owned()), - Origin::Mir); + let mut err = self.tcx.cannot_act_on_moved_value( + span, + desired_action.as_noun(), + msg, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); - err.span_label(span, format!("value {} here after move", - desired_action.as_verb_in_past_tense())); + err.span_label( + span, + format!( + "value {} here after move", + desired_action.as_verb_in_past_tense() + ), + ); for moi in mois { let move_msg = ""; //FIXME: add " (into closure)" let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span; if span == move_span { - err.span_label(span, - format!("value moved{} here in previous iteration of loop", - move_msg)); + err.span_label( + span, + format!("value moved{} here in previous iteration of loop", move_msg), + ); } else { err.span_label(move_span, format!("value moved{} here", move_msg)); }; @@ -1528,38 +1792,47 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { } } - fn report_move_out_while_borrowed(&mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - borrow: &BorrowData<'tcx>) { + fn report_move_out_while_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { let value_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; let borrow_msg = match self.describe_place(&borrow.place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; - self.tcx.cannot_move_when_borrowed(span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir) - .span_label(self.retrieve_borrow_span(borrow), - format!("borrow of {} occurs here", borrow_msg)) - .span_label(span, format!("move out of {} occurs here", value_msg)) - .emit(); + self.tcx + .cannot_move_when_borrowed( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ) + .span_label( + self.retrieve_borrow_span(borrow), + format!("borrow of {} occurs here", borrow_msg), + ) + .span_label(span, format!("move out of {} occurs here", value_msg)) + .emit(); } - fn report_use_while_mutably_borrowed(&mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - borrow : &BorrowData<'tcx>) { - + fn report_use_while_mutably_borrowed( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { let mut err = self.tcx.cannot_use_when_mutably_borrowed( span, &self.describe_place(place).unwrap_or("_".to_owned()), self.retrieve_borrow_span(borrow), &self.describe_place(&borrow.place).unwrap_or("_".to_owned()), - Origin::Mir); + Origin::Mir, + ); err.emit(); } @@ -1602,22 +1875,25 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { return None; }; - self.tcx.with_freevars(node_id, |freevars| { - for (v, place) in freevars.iter().zip(places) { - match *place { - Operand::Copy(Place::Local(l)) | - Operand::Move(Place::Local(l)) if local == l => { - debug!( - "find_closure_span: found captured local {:?}", - l - ); - return Some(v.span); + self.tcx + .with_freevars(node_id, |freevars| { + for (v, place) in freevars.iter().zip(places) { + match *place { + Operand::Copy(Place::Local(l)) | + Operand::Move(Place::Local(l)) if local == l => + { + debug!( + "find_closure_span: found captured local {:?}", + l + ); + return Some(v.span); + } + _ => {} } - _ => {} } - } - None - }).map(|var_span| (args_span, var_span)) + None + }) + .map(|var_span| (args_span, var_span)) } else { None }; @@ -1628,13 +1904,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { None } - fn report_conflicting_borrow(&mut self, - context: Context, - common_prefix: &Place<'tcx>, - (place, span): (&Place<'tcx>, Span), - gen_borrow_kind: BorrowKind, - issued_borrow: &BorrowData, - end_issued_loan_span: Option) { + fn report_conflicting_borrow( + &mut self, + context: Context, + common_prefix: &Place<'tcx>, + (place, span): (&Place<'tcx>, Span), + gen_borrow_kind: BorrowKind, + issued_borrow: &BorrowData, + end_issued_loan_span: Option, + ) { use self::prefixes::IsPrefixOf; assert!(common_prefix.is_prefix_of(place)); @@ -1645,47 +1923,89 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let new_closure_span = self.find_closure_span(span, context.loc); let span = new_closure_span.map(|(args, _)| args).unwrap_or(span); let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location); - let issued_span = old_closure_span.map(|(args, _)| args).unwrap_or(issued_span); + let issued_span = old_closure_span + .map(|(args, _)| args) + .unwrap_or(issued_span); let desc_place = self.describe_place(place).unwrap_or("_".to_owned()); // FIXME: supply non-"" `opt_via` when appropriate - let mut err = match (gen_borrow_kind, "immutable", "mutable", - issued_borrow.kind, "immutable", "mutable") { + let mut err = match ( + gen_borrow_kind, + "immutable", + "mutable", + issued_borrow.kind, + "immutable", + "mutable", + ) { (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) | - (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => - self.tcx.cannot_reborrow_already_borrowed( - span, &desc_place, "", lft, issued_span, - "it", rgt, "", end_issued_loan_span, Origin::Mir), - - (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => - self.tcx.cannot_mutably_borrow_multiply( - span, &desc_place, "", issued_span, - "", end_issued_loan_span, Origin::Mir), - - (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => - self.tcx.cannot_uniquely_borrow_by_two_closures( - span, &desc_place, issued_span, - end_issued_loan_span, Origin::Mir), - - (BorrowKind::Unique, _, _, _, _, _) => - self.tcx.cannot_uniquely_borrow_by_one_closure( - span, &desc_place, "", - issued_span, "it", "", end_issued_loan_span, Origin::Mir), - - (_, _, _, BorrowKind::Unique, _, _) => - self.tcx.cannot_reborrow_already_uniquely_borrowed( - span, &desc_place, "it", "", - issued_span, "", end_issued_loan_span, Origin::Mir), - - (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => - unreachable!(), + (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx + .cannot_reborrow_already_borrowed( + span, + &desc_place, + "", + lft, + issued_span, + "it", + rgt, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx + .cannot_mutably_borrow_multiply( + span, + &desc_place, + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_uniquely_borrow_by_two_closures( + span, + &desc_place, + issued_span, + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure( + span, + &desc_place, + "", + issued_span, + "it", + "", + end_issued_loan_span, + Origin::Mir, + ), + + (_, _, _, BorrowKind::Unique, _, _) => self.tcx + .cannot_reborrow_already_uniquely_borrowed( + span, + &desc_place, + "it", + "", + issued_span, + "", + end_issued_loan_span, + Origin::Mir, + ), + + (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(), }; if let Some((_, var_span)) = old_closure_span { err.span_label( var_span, - format!("previous borrow occurs due to use of `{}` in closure", desc_place), + format!( + "previous borrow occurs due to use of `{}` in closure", + desc_place + ), ); } @@ -1699,16 +2019,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - fn report_borrowed_value_does_not_live_long_enough(&mut self, - _: Context, - (place, span): (&Place<'tcx>, Span), - end_span: Option) { + fn report_borrowed_value_does_not_live_long_enough( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + end_span: Option, + ) { let root_place = self.prefixes(place, PrefixSet::All).last().unwrap(); let proper_span = match *root_place { Place::Local(local) => self.mir.local_decls[local].source_info.span, - _ => span + _ => span, }; - let mut err = self.tcx.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); + let mut err = self.tcx + .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir); err.span_label(proper_span, "temporary value created here"); err.span_label(span, "temporary value dropped here while still borrowed"); err.note("consider using a `let` binding to increase its lifetime"); @@ -1720,31 +2043,38 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { err.emit(); } - fn report_illegal_mutation_of_borrowed(&mut self, - _: Context, - (place, span): (&Place<'tcx>, Span), - loan: &BorrowData) { + fn report_illegal_mutation_of_borrowed( + &mut self, + _: Context, + (place, span): (&Place<'tcx>, Span), + loan: &BorrowData, + ) { let mut err = self.tcx.cannot_assign_to_borrowed( span, self.retrieve_borrow_span(loan), &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir); + Origin::Mir, + ); err.emit(); } - fn report_illegal_reassignment(&mut self, - _context: Context, - (place, span): (&Place<'tcx>, Span), - assigned_span: Span) { - let mut err = self.tcx.cannot_reassign_immutable(span, - &self.describe_place(place).unwrap_or("_".to_owned()), - Origin::Mir); + fn report_illegal_reassignment( + &mut self, + _context: Context, + (place, span): (&Place<'tcx>, Span), + assigned_span: Span, + ) { + let mut err = self.tcx.cannot_reassign_immutable( + span, + &self.describe_place(place).unwrap_or("_".to_owned()), + Origin::Mir, + ); err.span_label(span, "cannot assign twice to immutable variable"); if span != assigned_span { let value_msg = match self.describe_place(place) { Some(name) => format!("`{}`", name), - None => "value".to_owned() + None => "value".to_owned(), }; err.span_label(assigned_span, format!("first assignment to {}", value_msg)); } @@ -1759,7 +2089,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let mut buf = String::new(); match self.append_place_to_string(place, &mut buf, false) { Ok(()) => Some(buf), - Err(()) => None + Err(()) => None, } } @@ -1769,33 +2099,35 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// of a closure type. fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option { match *place { - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Field(field, _ty) => { - let is_projection_from_ty_closure = proj.base.ty(self.mir, self.tcx) - .to_ty(self.tcx).is_closure(); - - if is_projection_from_ty_closure { - Some(field) - } else { - None - } - }, - _ => None + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Field(field, _ty) => { + let is_projection_from_ty_closure = proj.base + .ty(self.mir, self.tcx) + .to_ty(self.tcx) + .is_closure(); + + if is_projection_from_ty_closure { + Some(field) + } else { + None + } } + _ => None, }, - _ => None + _ => None, } } // Appends end-user visible description of `place` to `buf`. - fn append_place_to_string(&self, - place: &Place<'tcx>, - buf: &mut String, - mut autoderef: bool) -> Result<(), ()> { + fn append_place_to_string( + &self, + place: &Place<'tcx>, + buf: &mut String, + mut autoderef: bool, + ) -> Result<(), ()> { match *place { Place::Local(local) => { - self.append_local_to_string(local, buf,)?; + self.append_local_to_string(local, buf)?; } Place::Static(ref static_) => { buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id))); @@ -1819,10 +2151,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.append_place_to_string(&proj.base, buf, autoderef)?; } } - }, + } ProjectionElem::Downcast(..) => { self.append_place_to_string(&proj.base, buf, autoderef)?; - }, + } ProjectionElem::Field(field, _ty) => { autoderef = true; @@ -1835,7 +2167,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { self.append_place_to_string(&proj.base, buf, autoderef)?; buf.push_str(&format!(".{}", field_name)); } - }, + } ProjectionElem::Index(index) => { autoderef = true; @@ -1845,7 +2177,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { buf.push_str(".."); } buf.push_str("]"); - }, + } ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { autoderef = true; // Since it isn't possible to borrow an element on a particular index and @@ -1853,7 +2185,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // to avoid confusing the end-user self.append_place_to_string(&proj.base, buf, autoderef)?; buf.push_str(&"[..]"); - }, + } }; } } @@ -1869,8 +2201,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Some(name) => { buf.push_str(&format!("{}", name)); Ok(()) - }, - None => Err(()) + } + None => Err(()), } } @@ -1880,24 +2212,22 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Local(local) => { let local = &self.mir.local_decls[local]; self.describe_field_from_ty(&local.ty, field) - }, - Place::Static(ref static_) => { - self.describe_field_from_ty(&static_.ty, field) - }, - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => - self.describe_field(&proj.base, field), - ProjectionElem::Downcast(def, variant_index) => - format!("{}", def.variants[variant_index].fields[field.index()].name), - ProjectionElem::Field(_, field_type) => - self.describe_field_from_ty(&field_type, field), - ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => - format!("{}", self.describe_field(&proj.base, field)), - } } + Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field), + Place::Projection(ref proj) => match proj.elem { + ProjectionElem::Deref => self.describe_field(&proj.base, field), + ProjectionElem::Downcast(def, variant_index) => { + format!("{}", def.variants[variant_index].fields[field.index()].name) + } + ProjectionElem::Field(_, field_type) => { + self.describe_field_from_ty(&field_type, field) + } + ProjectionElem::Index(..) | + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => { + format!("{}", self.describe_field(&proj.base, field)) + } + }, } } @@ -1906,26 +2236,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { if ty.is_box() { // If the type is a box, the field is described from the boxed type self.describe_field_from_ty(&ty.boxed_ty(), field) - } - else { + } else { match ty.sty { - ty::TyAdt(def, _) => { - if def.is_enum() { - format!("{}", field.index()) - } - else { - format!("{}", def.struct_variant().fields[field.index()].name) - } - }, - ty::TyTuple(_, _) => { + ty::TyAdt(def, _) => if def.is_enum() { format!("{}", field.index()) + } else { + format!("{}", def.struct_variant().fields[field.index()].name) }, + ty::TyTuple(_, _) => format!("{}", field.index()), ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => { self.describe_field_from_ty(&tnm.ty, field) - }, - ty::TyArray(ty, _) | ty::TySlice(ty) => { - self.describe_field_from_ty(&ty, field) - }, + } + ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field), ty::TyClosure(closure_def_id, _) => { // Convert the def-id into a node-id. node-ids are only valid for // the local code in the current crate, so this returns an `Option` in case @@ -1935,11 +2257,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]); self.tcx.hir.name(freevar.var_id()).to_string() - } + } _ => { // Might need a revision when the fields in trait RFC is implemented // (https://github.com/rust-lang/rfcs/pull/1546) - bug!("End-user description not implemented for field access on `{:?}`", ty.sty); + bug!( + "End-user description not implemented for field access on `{:?}`", + ty.sty + ); } } } @@ -1969,8 +2294,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { Place::Local(..) | Place::Static(..) => return deepest, Place::Projection(ref proj) => proj, }; - if proj.elem == ProjectionElem::Deref && - place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() + if proj.elem == ProjectionElem::Deref + && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box() { deepest = &proj.base; } @@ -2003,31 +2328,39 @@ enum ContextKind { } impl ContextKind { - fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } } + fn new(self, loc: Location) -> Context { + Context { + kind: self, + loc: loc, + } + } } impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { - pub(super) fn new(borrows: DataflowResults>, - inits: DataflowResults>, - uninits: DataflowResults>, - move_out: DataflowResults>, - ever_inits: DataflowResults>) - -> Self { + fn new( + borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>, + move_outs: FlowInProgress>, + ever_inits: FlowInProgress>, + ) -> Self { InProgress { - borrows: FlowInProgress::new(borrows), - inits: FlowInProgress::new(inits), - uninits: FlowInProgress::new(uninits), - move_outs: FlowInProgress::new(move_out), - ever_inits: FlowInProgress::new(ever_inits) + borrows, + inits, + uninits, + move_outs, + ever_inits, } } - fn each_flow(&mut self, - mut xform_borrows: XB, - mut xform_inits: XI, - mut xform_uninits: XU, - mut xform_move_outs: XM, - mut xform_ever_inits: XE) where + fn each_flow( + &mut self, + mut xform_borrows: XB, + mut xform_inits: XI, + mut xform_uninits: XU, + mut xform_move_outs: XM, + mut xform_ever_inits: XE, + ) where XB: FnMut(&mut FlowInProgress>), XI: FnMut(&mut FlowInProgress>), XU: FnMut(&mut FlowInProgress>), @@ -2047,7 +2380,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("borrows in effect: ["); let mut saw_one = false; self.borrows.each_state_bit(|borrow| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; s.push_str(&format!("{}", borrow_data)); @@ -2057,7 +2392,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("borrows generated: ["); let mut saw_one = false; self.borrows.each_gen_bit(|borrow| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let borrow_data = &self.borrows.base_results.operator().borrows()[borrow]; s.push_str(&format!("{}", borrow_data)); @@ -2067,10 +2404,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("inits: ["); let mut saw_one = false; self.inits.each_state_bit(|mpi_init| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; - let move_path = - &self.inits.base_results.operator().move_data().move_paths[mpi_init]; + let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init]; s.push_str(&format!("{}", move_path)); }); s.push_str("] "); @@ -2078,7 +2416,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("uninits: ["); let mut saw_one = false; self.uninits.each_state_bit(|mpi_uninit| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let move_path = &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit]; @@ -2089,10 +2429,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("move_out: ["); let mut saw_one = false; self.move_outs.each_state_bit(|mpi_move_out| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; - let move_out = - &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; + let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out]; s.push_str(&format!("{:?}", move_out)); }); s.push_str("] "); @@ -2100,7 +2441,9 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { s.push_str("ever_init: ["); let mut saw_one = false; self.ever_inits.each_state_bit(|mpi_ever_init| { - if saw_one { s.push_str(", "); }; + if saw_one { + s.push_str(", "); + }; saw_one = true; let ever_init = &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init]; @@ -2112,8 +2455,11 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { } } -impl<'b, 'gcx, 'tcx> FlowInProgress> { - fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { +impl<'tcx, T> FlowInProgress +where + T: HasMoveData<'tcx> + BitDenotation, +{ + fn has_any_child_of(&self, mpi: T::Idx) -> Option { let move_data = self.base_results.operator().move_data(); let mut todo = vec![mpi]; @@ -2141,13 +2487,24 @@ impl<'b, 'gcx, 'tcx> FlowInProgress> { } } -impl FlowInProgress where BD: BitDenotation { - fn each_state_bit(&self, f: F) where F: FnMut(BD::Idx) { - self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f) +impl FlowInProgress +where + BD: BitDenotation, +{ + fn each_state_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.curr_state + .each_bit(self.base_results.operator().bits_per_block(), f) } - fn each_gen_bit(&self, f: F) where F: FnMut(BD::Idx) { - self.stmt_gen.each_bit(self.base_results.operator().bits_per_block(), f) + fn each_gen_bit(&self, f: F) + where + F: FnMut(BD::Idx), + { + self.stmt_gen + .each_bit(self.base_results.operator().bits_per_block(), f) } fn new(results: DataflowResults) -> Self { @@ -2172,9 +2529,13 @@ impl FlowInProgress where BD: BitDenotation { self.stmt_kill.reset_to_empty(); let mut ignored = IdxSetBuf::new_empty(0); let mut sets = BlockSets { - on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill, + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, }; - self.base_results.operator().statement_effect(&mut sets, loc); + self.base_results + .operator() + .statement_effect(&mut sets, loc); } fn reconstruct_terminator_effect(&mut self, loc: Location) { @@ -2182,9 +2543,13 @@ impl FlowInProgress where BD: BitDenotation { self.stmt_kill.reset_to_empty(); let mut ignored = IdxSetBuf::new_empty(0); let mut sets = BlockSets { - on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill, + on_entry: &mut ignored, + gen_set: &mut self.stmt_gen, + kill_set: &mut self.stmt_kill, }; - self.base_results.operator().terminator_effect(&mut sets, loc); + self.base_results + .operator() + .terminator_effect(&mut sets, loc); } fn apply_local_effect(&mut self) { @@ -2197,7 +2562,10 @@ impl FlowInProgress where BD: BitDenotation { self.curr_state.elems(univ) } - fn with_elems_outgoing(&self, f: F) where F: FnOnce(indexed_set::Elems) { + fn with_elems_outgoing(&self, f: F) + where + F: FnOnce(indexed_set::Elems), + { let mut curr_state = self.curr_state.clone(); curr_state.union(&self.stmt_gen); curr_state.subtract(&self.stmt_kill); diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs new file mode 100644 index 0000000000000..42225536357da --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -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 or the MIT license +// , 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>, + 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>, + 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)> = 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(&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(), + ); + } + } + } + } +} diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs similarity index 79% rename from src/librustc_mir/transform/nll/mod.rs rename to src/librustc_mir/borrow_check/nll/mod.rs index 147f061ad113f..804f5e2687597 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -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; -/// 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>, +/// 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, - param_env: ty::ParamEnv<'gcx>, mir: &mut Mir<'tcx>, -) -> RegionInferenceContext<'tcx> { +) -> UniversalRegions<'tcx> { // Compute named region information. - let free_regions = &free_regions::free_regions(infcx, def_id); + let universal_regions = universal_regions::universal_regions(infcx, def_id); // Replace all regions with fresh inference variables. - renumber::renumber_mir(infcx, free_regions, mir); + 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(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>, + flow_inits: &mut FlowInProgress>, + move_data: &MoveData<'tcx>, +) -> RegionInferenceContext<'tcx> { // 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( ®ular_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(()) }); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs new file mode 100644 index 0000000000000..d1faaf75a5323 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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 or the MIT license +// , 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>, + + /// 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, + + /// The constraints we have accumulated and used during solving. + constraints: Vec, + + /// 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, + + /// 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>, +} + +#[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 { + 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::>() + }; + + 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::>(); + 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 { + 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 + ) + } +} diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs similarity index 90% rename from src/librustc_mir/transform/nll/renumber.rs rename to src/librustc_mir/borrow_check/nll/renumber.rs index 1076b774de657..371419da02448 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -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>, 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(&mut self, value: &T) -> T + fn renumber_universal_regions(&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[®ion]; + let index = self.universal_regions.indices[®ion]; 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) }; diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs similarity index 93% rename from src/librustc_mir/transform/nll/subtype_constraint_generation.rs rename to src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index c1850c76541d7..dbae40be6fe1d 100644 --- a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -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] } } } diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs similarity index 71% rename from src/librustc_mir/transform/nll/free_regions.rs rename to src/librustc_mir/borrow_check/nll/universal_regions.rs index 92a8a714d525a..3be95a114c3bc 100644 --- a/src/librustc_mir/transform/nll/free_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -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, 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, RegionVid>, + universal_regions: &mut FxHashMap, 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); } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 4349820dbe9e1..d814b092c9d69 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -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> { diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 19ab45dda9545..2bba3e263f3c6 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -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, region_map: FxHashMap, FxHashSet>, region_span_map: FxHashMap, - nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>, + nonlexical_regioncx: Option>, } // 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>) -> 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, 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) { diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 294f48178a8ac..9d91e1344dc37 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -263,6 +263,10 @@ impl<'tcx> MovePathLookup<'tcx> { } } } + + pub fn find_local(&self, local: Local) -> MovePathIndex { + self.locals[local] + } } #[derive(Debug)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index af309342dc522..53f9b885ac6c6 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -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)] diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 9ca044b764806..46193dedf8968 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -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); diff --git a/src/librustc_mir/transform/lower_128bit.rs b/src/librustc_mir/transform/lower_128bit.rs index 7027d827c84f5..f8c45dd3d2570 100644 --- a/src/librustc_mir/transform/lower_128bit.rs +++ b/src/librustc_mir/transform/lower_128bit.rs @@ -144,7 +144,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); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 830838c603758..fb9daf07c71dc 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -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) { diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs deleted file mode 100644 index 73d5a610dbd53..0000000000000 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ /dev/null @@ -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 or the MIT license -// , 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(&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); - } -} diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs deleted file mode 100644 index 1609c1236b0ca..0000000000000 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ /dev/null @@ -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 or the MIT license -// , 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>, - - /// The constraints we have accumulated and used during solving. - constraints: Vec, -} - -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>, - - /// 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, - free_regions: BTreeSet, -} - -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 { - 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::>() - }; - - 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 - ) - } -} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 3b41b2545bf55..f0b62e28a0da6 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -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>, + ak: &AggregateKind<'tcx>, field_index: usize, location: Location, ) -> Result, 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); diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index feea0e2880974..eebe5a86018ea 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -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; diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 4a0090204851c..8a3db0eb25b99 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -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,64 +59,124 @@ 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 _: 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)?; + writeln!(file, "// disambiguator = {}", disambiguator)?; + if let Some(ref layout) = mir.generator_layout { + writeln!(file, "// generator_layout = {:?}", layout)?; + } + writeln!(file, "")?; + extra_data(PassWhere::BeforeCFG, &mut file)?; + 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 { + 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() + None => String::new(), }; let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { @@ -126,48 +189,64 @@ where }; 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); + 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); - let _ = fs::File::create(&file_path).and_then(|mut file| { - writeln!(file, "// MIR for `{}`", node_path)?; - writeln!(file, "// source = {:?}", source)?; - writeln!(file, "// pass_name = {}", pass_name)?; - writeln!(file, "// disambiguator = {}", disambiguator)?; - if let Some(ref layout) = mir.generator_layout { - writeln!(file, "// generator_layout = {:?}", layout)?; - } - writeln!(file, "")?; - extra_data(PassWhere::BeforeCFG, &mut file)?; - 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| { - write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; - Ok(()) - }); + 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 { + 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, - 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, + 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(tcx: TyCtxt, - block: BasicBlock, - mir: &Mir, - extra_data: &mut F, - w: &mut Write) - -> io::Result<()> +pub fn write_basic_block( + 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>, - w: &mut Write, - parent: VisibilityScope, - depth: usize) - -> io::Result<()> { +fn write_scope_tree( + tcx: TyCtxt, + mir: &Mir, + scope_tree: &FxHashMap>, + 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> = 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(()) diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index 1f92c1067845e..405647af324d6 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -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, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index f03f782ebb452..d5e4aa69c5b4e 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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); diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 5b5d697bcf435..147347a75abe8 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -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, diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 24efb79170427..70607bf4842a5 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -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 = diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 610d07efa359d..55700c452e57b 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -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, ®ion_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, ®ion_scope_tree, &outlives_env); Ok(()) }) } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index c1adb0e65a4da..23243c3ad66c0 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -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 \ diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 09dd334a62d8a..27a0e4f6dfe63 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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(&self, - all_outlive_scope: DefId, - value: &ty::Binder) - -> 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, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index b91137aeb68a5..7ef6027772be2 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -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) { diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 0ca259fd44d78..d4625bb58c338 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -451,7 +451,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { implied_bounds: &mut Vec>) { 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) ); diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index fedfa51d61d11..d63980eaa506b 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -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(¶m_env.caller_bounds); - infcx.resolve_regions_and_report_errors(impl_did, ®ion_scope_tree, &free_regions); + let outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors( + impl_did, + ®ion_scope_tree, + &outlives_env, + ); CoerceUnsizedInfo { custom_kind: kind diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7de29868d4341..b754c981b2101 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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 diff --git a/src/test/compile-fail/regions-normalize-in-where-clause-list.rs b/src/test/compile-fail/regions-normalize-in-where-clause-list.rs new file mode 100644 index 0000000000000..68642598ed2df --- /dev/null +++ b/src/test/compile-fail/regions-normalize-in-where-clause-list.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 34d0482a623a9..7039de727faa9 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -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 diff --git a/src/test/mir-opt/nll/region-liveness-basic.rs b/src/test/mir-opt/nll/region-liveness-basic.rs index 36dedeebd538a..cfbc51f9e1861 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -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; diff --git a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs index de2b18fe4afa3..679f31fdab903 100644 --- a/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs +++ b/src/test/mir-opt/nll/region-liveness-two-disjoint-uses.rs @@ -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; diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index 6a2a7cc714997..471d77aefac62 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -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 diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs new file mode 100644 index 0000000000000..0047f6d59237c --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr new file mode 100644 index 0000000000000..389334f9c1d8d --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr @@ -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 + diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs new file mode 100644 index 0000000000000..64a4d39100063 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs new file mode 100644 index 0000000000000..3242136f005e7 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs @@ -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 or the MIT license +// , 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] +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr new file mode 100644 index 0000000000000..9edeca2d18801 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr @@ -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 + diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs new file mode 100644 index 0000000000000..3e32818b8dcf3 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs @@ -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 or the MIT license +// , 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. +} diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr new file mode 100644 index 0000000000000..24d0d6d04c8da --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr @@ -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 + diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs new file mode 100644 index 0000000000000..291fcbd73f3e1 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -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 or the MIT license +// , 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] +} diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr new file mode 100644 index 0000000000000..7b1b55d133ac5 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -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 +