From a33993208c8ba1b942323f9631ca18a06485eb8b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 12:26:31 -0400 Subject: [PATCH 01/68] fulfill: remove dead code --- src/librustc/traits/fulfill.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index cc2506d1afc50..8afc525b9f878 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -157,14 +157,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); } - pub fn register_region_obligation(&mut self, - t_a: Ty<'tcx>, - r_b: ty::Region<'tcx>, - cause: ObligationCause<'tcx>) - { - register_region_obligation(t_a, r_b, cause, &mut self.region_obligations); - } - pub fn register_predicate_obligation(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, obligation: PredicateObligation<'tcx>) From 1e32a6bbc9f2bbdbffda011dc39651f835e00839 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 13:41:07 -0400 Subject: [PATCH 02/68] move region constraints into inference context --- src/librustc/infer/mod.rs | 77 +++++++++++++++++- src/librustc/traits/fulfill.rs | 101 +++++------------------- src/librustc/traits/mod.rs | 2 +- src/librustc/traits/structural_impls.rs | 7 -- src/librustc_typeck/check/regionck.rs | 9 +-- 5 files changed, 97 insertions(+), 99 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 79eeebfb25031..3010123f49566 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -36,7 +36,7 @@ use std::fmt; use syntax::ast; use errors::DiagnosticBuilder; use syntax_pos::{self, Span, DUMMY_SP}; -use util::nodemap::FxHashMap; +use util::nodemap::{NodeMap, FxHashMap}; use arena::DroplessArena; use self::combine::CombineFields; @@ -135,6 +135,32 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // This flag is true while there is an active snapshot. in_snapshot: Cell, + + // A set of constraints that regionck must validate. Each + // constraint has the form `T:'a`, meaning "some type `T` must + // outlive the lifetime 'a". These constraints derive from + // instantiated type parameters. So if you had a struct defined + // like + // + // struct Foo { ... } + // + // then in some expression `let x = Foo { ... }` it will + // instantiate the type parameter `T` with a fresh type `$0`. At + // the same time, it will record a region obligation of + // `$0:'static`. This will get checked later by regionck. (We + // can't generally check these things right away because we have + // to wait until types are resolved.) + // + // These are stored in a map keyed to the id of the innermost + // enclosing fn body / static initializer expression. This is + // because the location where the obligation was incurred can be + // relevant with respect to which sublifetime assumptions are in + // place. The reason that we store under the fn-id, and not + // something more fine-grained, is so that it is easier for + // regionck to be sure that it has found *all* the region + // obligations (otherwise, it's easy to fail to walk to a + // particular node-id). + region_obligations: RefCell>>>, } /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized @@ -317,6 +343,14 @@ pub enum FixupError { UnresolvedTy(TyVid) } +/// See the `region_obligations` field for more information. +#[derive(Clone)] +pub struct RegionObligation<'tcx> { + pub sub_region: ty::Region<'tcx>, + pub sup_type: Ty<'tcx>, + pub cause: ObligationCause<'tcx>, +} + impl fmt::Display for FixupError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::FixupError::*; @@ -386,6 +420,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { tainted_by_errors_flag: Cell::new(false), err_count_on_creation: tcx.sess.err_count(), in_snapshot: Cell::new(false), + region_obligations: RefCell::new(NodeMap()), })) } } @@ -953,6 +988,33 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }) } + /// Registers that the given region obligation must be resolved + /// from within the scope of `body_id`. These regions are enqueued + /// and later processed by regionck, when full type information is + /// available (see `region_obligations` field for more + /// information). + pub fn register_region_obligation(&self, + body_id: ast::NodeId, + obligation: RegionObligation<'tcx>) + { + self.region_obligations.borrow_mut().entry(body_id) + .or_insert(vec![]) + .push(obligation); + } + + /// Get the region obligations that must be proven (during + /// `regionck`) for the given `body_id` (removing them from the + /// map as a side-effect). + pub fn take_region_obligations(&self, + body_id: ast::NodeId) + -> Vec> + { + match self.region_obligations.borrow_mut().remove(&body_id) { + None => vec![], + Some(vec) => vec, + } + } + pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid { self.type_variables .borrow_mut() @@ -1073,6 +1135,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context: DefId, region_map: ®ion::ScopeTree, free_regions: &FreeRegionMap<'tcx>) { + // TODO assert!(self.region_obligations.borrow().is_empty(), + // TODO "region_obligations not empty: {:#?}", + // TODO self.region_obligations.borrow()); + let region_rels = RegionRelations::new(self.tcx, region_context, region_map, @@ -1533,3 +1599,12 @@ impl<'tcx> TypeFoldable<'tcx> for TypeTrace<'tcx> { self.cause.visit_with(visitor) || self.values.visit_with(visitor) } } + +impl<'tcx> fmt::Debug for RegionObligation<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", + self.sub_region, + self.sup_type) + } +} + diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 8afc525b9f878..6767fbae3d8b5 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -8,14 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{InferCtxt, InferOk}; +use infer::{RegionObligation, InferCtxt, InferOk}; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{ObligationForest, Error}; use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor}; use std::marker::PhantomData; -use syntax::ast; -use util::nodemap::NodeMap; use hir::def_id::DefId; use super::CodeAmbiguity; @@ -48,39 +46,6 @@ pub struct FulfillmentContext<'tcx> { // A list of all obligations that have been registered with this // fulfillment context. predicates: ObligationForest>, - - // A set of constraints that regionck must validate. Each - // constraint has the form `T:'a`, meaning "some type `T` must - // outlive the lifetime 'a". These constraints derive from - // instantiated type parameters. So if you had a struct defined - // like - // - // struct Foo { ... } - // - // then in some expression `let x = Foo { ... }` it will - // instantiate the type parameter `T` with a fresh type `$0`. At - // the same time, it will record a region obligation of - // `$0:'static`. This will get checked later by regionck. (We - // can't generally check these things right away because we have - // to wait until types are resolved.) - // - // These are stored in a map keyed to the id of the innermost - // enclosing fn body / static initializer expression. This is - // because the location where the obligation was incurred can be - // relevant with respect to which sublifetime assumptions are in - // place. The reason that we store under the fn-id, and not - // something more fine-grained, is so that it is easier for - // regionck to be sure that it has found *all* the region - // obligations (otherwise, it's easy to fail to walk to a - // particular node-id). - region_obligations: NodeMap>>, -} - -#[derive(Clone)] -pub struct RegionObligation<'tcx> { - pub sub_region: ty::Region<'tcx>, - pub sup_type: Ty<'tcx>, - pub cause: ObligationCause<'tcx>, } #[derive(Clone, Debug)] @@ -94,7 +59,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { pub fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), - region_obligations: NodeMap(), } } @@ -184,17 +148,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { } } - - pub fn region_obligations(&self, - body_id: ast::NodeId) - -> &[RegionObligation<'tcx>] - { - match self.region_obligations.get(&body_id) { - None => Default::default(), - Some(vec) => vec, - } - } - pub fn select_all_or_error(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>) -> Result<(),Vec>> @@ -237,10 +190,7 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { debug!("select: starting another iteration"); // Process pending obligations. - let outcome = self.predicates.process_obligations(&mut FulfillProcessor { - selcx, - region_obligations: &mut self.region_obligations, - }); + let outcome = self.predicates.process_obligations(&mut FulfillProcessor { selcx }); debug!("select: outcome={:?}", outcome); // FIXME: if we kept the original cache key, we could mark projection @@ -269,7 +219,6 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { struct FulfillProcessor<'a, 'b: 'a, 'gcx: 'tcx, 'tcx: 'b> { selcx: &'a mut SelectionContext<'b, 'gcx, 'tcx>, - region_obligations: &'a mut NodeMap>>, } impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, 'tcx> { @@ -280,9 +229,7 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, obligation: &mut Self::Obligation) -> Result>, Self::Error> { - process_predicate(self.selcx, - obligation, - self.region_obligations) + process_predicate(self.selcx, obligation) .map(|os| os.map(|os| os.into_iter().map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] @@ -321,8 +268,7 @@ fn trait_ref_type_vars<'a, 'gcx, 'tcx>(selcx: &mut SelectionContext<'a, 'gcx, 't /// - `Err` if the predicate does not hold fn process_predicate<'a, 'gcx, 'tcx>( selcx: &mut SelectionContext<'a, 'gcx, 'tcx>, - pending_obligation: &mut PendingPredicateObligation<'tcx>, - region_obligations: &mut NodeMap>>) + pending_obligation: &mut PendingPredicateObligation<'tcx>) -> Result>>, FulfillmentErrorCode<'tcx>> { @@ -444,18 +390,26 @@ fn process_predicate<'a, 'gcx, 'tcx>( // `for<'a> T: 'a where 'a not in T`, which we can treat as `T: 'static`. Some(t_a) => { let r_static = selcx.tcx().types.re_static; - register_region_obligation(t_a, r_static, - obligation.cause.clone(), - region_obligations); + selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_static, + cause: obligation.cause.clone(), + }); Ok(Some(vec![])) } } } // If there aren't, register the obligation. Some(ty::OutlivesPredicate(t_a, r_b)) => { - register_region_obligation(t_a, r_b, - obligation.cause.clone(), - region_obligations); + selcx.infcx().register_region_obligation( + obligation.cause.body_id, + RegionObligation { + sup_type: t_a, + sub_region: r_b, + cause: obligation.cause.clone() + }); Ok(Some(vec![])) } } @@ -558,25 +512,6 @@ fn process_predicate<'a, 'gcx, 'tcx>( } } - -fn register_region_obligation<'tcx>(t_a: Ty<'tcx>, - r_b: ty::Region<'tcx>, - cause: ObligationCause<'tcx>, - region_obligations: &mut NodeMap>>) -{ - let region_obligation = RegionObligation { sup_type: t_a, - sub_region: r_b, - cause: cause }; - - debug!("register_region_obligation({:?}, cause={:?})", - region_obligation, region_obligation.cause); - - region_obligations.entry(region_obligation.cause.body_id) - .or_insert(vec![]) - .push(region_obligation); - -} - fn to_fulfillment_error<'tcx>( error: Error, FulfillmentErrorCode<'tcx>>) -> FulfillmentError<'tcx> diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index c08fe187f99bf..190279db732f6 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -30,7 +30,7 @@ use syntax::ast; use syntax_pos::{Span, DUMMY_SP}; pub use self::coherence::{orphan_check, overlapping_impls, OrphanCheckErr, OverlapResult}; -pub use self::fulfill::{FulfillmentContext, RegionObligation}; +pub use self::fulfill::FulfillmentContext; pub use self::project::MismatchedProjectionTypes; pub use self::project::{normalize, normalize_projection_type, Normalized}; pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, Reveal}; diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index fd93aa162a612..56c2a38501ef5 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -26,13 +26,6 @@ impl<'tcx, T: fmt::Debug> fmt::Debug for Normalized<'tcx, T> { } } -impl<'tcx> fmt::Debug for traits::RegionObligation<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionObligation(sub_region={:?}, sup_type={:?})", - self.sub_region, - self.sup_type) - } -} impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Obligation(predicate={:?},depth={})", diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index ad7978480a6b1..b1ac3abe23024 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -360,11 +360,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // Make a copy of the region obligations vec because we'll need // to be able to borrow the fulfillment-cx below when projecting. - let region_obligations = - self.fulfillment_cx - .borrow() - .region_obligations(node_id) - .to_vec(); + let region_obligations = self.infcx.take_region_obligations(node_id); for r_o in ®ion_obligations { debug!("visit_region_obligations: r_o={:?} cause={:?}", @@ -375,8 +371,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } // Processing the region obligations should not cause the list to grow further: - assert_eq!(region_obligations.len(), - self.fulfillment_cx.borrow().region_obligations(node_id).len()); + assert!(self.infcx.take_region_obligations(node_id).is_empty()); } fn code_to_origin(&self, From 58d454830d2319d635ba29fded6f039ab8026ae7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 14:23:30 -0400 Subject: [PATCH 03/68] convert EXTRA_REQUIREMENT_IN_IMPL into a hard error cc #37166 --- src/librustc/infer/error_reporting/mod.rs | 5 ++-- src/librustc/infer/error_reporting/note.rs | 6 ++-- src/librustc/infer/mod.rs | 8 +----- src/librustc/lint/builtin.rs | 7 ----- src/librustc/traits/error_reporting.rs | 32 +++++---------------- src/librustc/traits/mod.rs | 1 - src/librustc/traits/structural_impls.rs | 4 +-- src/librustc_lint/lib.rs | 6 ++-- src/librustc_typeck/check/compare_method.rs | 30 ++++--------------- src/librustc_typeck/check/mod.rs | 14 +-------- src/test/compile-fail/issue-18937.rs | 1 - 11 files changed, 21 insertions(+), 93 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index e9916bd77e758..c262e966576bd 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -880,14 +880,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }; if let SubregionOrigin::CompareImplMethodObligation { - span, item_name, impl_item_def_id, trait_item_def_id, lint_id + span, item_name, impl_item_def_id, trait_item_def_id, } = origin { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", bound_kind, sub), - lint_id) + &format!("`{}: {}`", bound_kind, sub)) .emit(); return; } diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs index 1f0fd7b01d37d..e46613b3e4da0 100644 --- a/src/librustc/infer/error_reporting/note.rs +++ b/src/librustc/infer/error_reporting/note.rs @@ -445,14 +445,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { infer::CompareImplMethodObligation { span, item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => { + trait_item_def_id } => { self.report_extra_impl_obligation(span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", sup, sub), - lint_id) + &format!("`{}: {}`", sup, sub)) } } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 3010123f49566..110c49d820abb 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -274,10 +274,6 @@ pub enum SubregionOrigin<'tcx> { item_name: ast::Name, impl_item_def_id: DefId, trait_item_def_id: DefId, - - // this is `Some(_)` if this error arises from the bug fix for - // #18937. This is a temporary measure. - lint_id: Option, }, } @@ -1532,14 +1528,12 @@ impl<'tcx> SubregionOrigin<'tcx> { traits::ObligationCauseCode::CompareImplMethodObligation { item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => + trait_item_def_id, } => SubregionOrigin::CompareImplMethodObligation { span: cause.span, item_name, impl_item_def_id, trait_item_def_id, - lint_id, }, _ => default(), diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 855cc069d117a..75446586365dd 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -161,12 +161,6 @@ declare_lint! { "patterns in functions without body were erroneously allowed" } -declare_lint! { - pub EXTRA_REQUIREMENT_IN_IMPL, - Deny, - "detects extra requirements in impls that were erroneously allowed" -} - declare_lint! { pub LEGACY_DIRECTORY_OWNERSHIP, Deny, @@ -254,7 +248,6 @@ impl LintPass for HardwiredLints { RESOLVE_TRAIT_ON_DEFAULTED_UNIT, SAFE_EXTERN_STATICS, PATTERNS_IN_FNS_WITHOUT_BODY, - EXTRA_REQUIREMENT_IN_IMPL, LEGACY_DIRECTORY_OWNERSHIP, LEGACY_IMPORTS, LEGACY_CONSTRUCTOR_VISIBILITY, diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index e2b23c12cf1f3..a860c643930c5 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -33,7 +33,6 @@ use hir::def_id::DefId; use infer::{self, InferCtxt}; use infer::type_variable::TypeVariableOrigin; use middle::const_val; -use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL; use std::fmt; use syntax::ast; use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; @@ -474,30 +473,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { item_name: ast::Name, _impl_item_def_id: DefId, trait_item_def_id: DefId, - requirement: &fmt::Display, - lint_id: Option) // (*) + requirement: &fmt::Display) -> DiagnosticBuilder<'tcx> { - // (*) This parameter is temporary and used only for phasing - // in the bug fix to #18937. If it is `Some`, it has a kind of - // weird effect -- the diagnostic is reported as a lint, and - // the builder which is returned is marked as canceled. - let msg = "impl has stricter requirements than trait"; - let mut err = match lint_id { - Some(node_id) => { - self.tcx.struct_span_lint_node(EXTRA_REQUIREMENT_IN_IMPL, - node_id, - error_span, - msg) - } - None => { - struct_span_err!(self.tcx.sess, - error_span, - E0276, - "{}", msg) - } - }; + let mut err = struct_span_err!(self.tcx.sess, + error_span, + E0276, + "{}", msg); if let Some(trait_item_span) = self.tcx.hir.span_if_local(trait_item_def_id) { let span = self.tcx.sess.codemap().def_span(trait_item_span); @@ -536,15 +519,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let mut err = match *error { SelectionError::Unimplemented => { if let ObligationCauseCode::CompareImplMethodObligation { - item_name, impl_item_def_id, trait_item_def_id, lint_id + item_name, impl_item_def_id, trait_item_def_id, } = obligation.cause.code { self.report_extra_impl_obligation( span, item_name, impl_item_def_id, trait_item_def_id, - &format!("`{}`", obligation.predicate), - lint_id) + &format!("`{}`", obligation.predicate)) .emit(); return; } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 190279db732f6..a74504ac627c1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -152,7 +152,6 @@ pub enum ObligationCauseCode<'tcx> { item_name: ast::Name, impl_item_def_id: DefId, trait_item_def_id: DefId, - lint_id: Option, }, /// Checking that this expression can be assigned where it needs to be diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 56c2a38501ef5..9231995018065 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -214,13 +214,11 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { } super::CompareImplMethodObligation { item_name, impl_item_def_id, - trait_item_def_id, - lint_id } => { + trait_item_def_id } => { Some(super::CompareImplMethodObligation { item_name, impl_item_def_id, trait_item_def_id, - lint_id, }) } super::ExprAssignable => Some(super::ExprAssignable), diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 1a8ad9718cfab..97c34a1c30275 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -207,10 +207,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { id: LintId::of(INVALID_TYPE_PARAM_DEFAULT), reference: "issue #36887 ", }, - FutureIncompatibleInfo { - id: LintId::of(EXTRA_REQUIREMENT_IN_IMPL), - reference: "issue #37166 ", - }, FutureIncompatibleInfo { id: LintId::of(LEGACY_DIRECTORY_OWNERSHIP), reference: "issue #37872 ", @@ -276,4 +272,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { "converted into hard error, see https://github.com/rust-lang/rust/issues/36891"); store.register_removed("lifetime_underscore", "converted into hard error, see https://github.com/rust-lang/rust/issues/36892"); + store.register_removed("extra_requirement_in_impl", + "converted into hard error, see https://github.com/rust-lang/rust/issues/37166"); } diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 554a858bcc173..a7e69aebd6528 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -10,8 +10,6 @@ use rustc::hir::{self, ImplItemKind, TraitItemKind}; use rustc::infer::{self, InferOk}; -use rustc::middle::free_region::FreeRegionMap; -use rustc::middle::region; use rustc::ty::{self, TyCtxt}; use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use rustc::ty::error::{ExpectedFound, TypeError}; @@ -38,8 +36,7 @@ pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m_span: Span, trait_m: &ty::AssociatedItem, impl_trait_ref: ty::TraitRef<'tcx>, - trait_item_span: Option, - old_broken_mode: bool) { + trait_item_span: Option) { debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); @@ -71,8 +68,7 @@ pub fn compare_impl_method<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m, impl_m_span, trait_m, - impl_trait_ref, - old_broken_mode) { + impl_trait_ref) { return; } } @@ -81,8 +77,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_m: &ty::AssociatedItem, impl_m_span: Span, trait_m: &ty::AssociatedItem, - impl_trait_ref: ty::TraitRef<'tcx>, - old_broken_mode: bool) + impl_trait_ref: ty::TraitRef<'tcx>) -> Result<(), ErrorReported> { let trait_to_impl_substs = impl_trait_ref.substs; @@ -98,7 +93,6 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_name: impl_m.name, impl_item_def_id: impl_m.def_id, trait_item_def_id: trait_m.def_id, - lint_id: if !old_broken_mode { Some(impl_m_node_id) } else { None }, }, }; @@ -334,22 +328,8 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - if old_broken_mode { - // FIXME(#18937) -- this is how the code used to - // work. This is buggy because the fulfillment cx creates - // region obligations that get overlooked. The right - // thing to do is the code below. But we keep this old - // pass around temporarily. - 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_m.def_id, - ®ion_scope_tree, - &free_regions); - } else { - let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id); - fcx.regionck_item(impl_m_node_id, impl_m_span, &[]); - } + let fcx = FnCtxt::new(&inh, param_env, impl_m_node_id); + fcx.regionck_item(impl_m_node_id, impl_m_span, &[]); Ok(()) }) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 82d59ecfc92cf..19ea1b1795004 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1339,24 +1339,12 @@ fn check_impl_items_against_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir::ImplItemKind::Method(..) => { let trait_span = tcx.hir.span_if_local(ty_trait_item.def_id); if ty_trait_item.kind == ty::AssociatedKind::Method { - let err_count = tcx.sess.err_count(); compare_impl_method(tcx, &ty_impl_item, impl_item.span, &ty_trait_item, impl_trait_ref, - trait_span, - true); // start with old-broken-mode - if err_count == tcx.sess.err_count() { - // old broken mode did not report an error. Try with the new mode. - compare_impl_method(tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - trait_span, - false); // use the new mode - } + trait_span); } else { let mut err = struct_span_err!(tcx.sess, impl_item.span, E0324, "item `{}` is an associated method, \ diff --git a/src/test/compile-fail/issue-18937.rs b/src/test/compile-fail/issue-18937.rs index 5996c8e543878..f7f84e6452ddb 100644 --- a/src/test/compile-fail/issue-18937.rs +++ b/src/test/compile-fail/issue-18937.rs @@ -27,7 +27,6 @@ trait A<'a> { impl<'a> A<'a> for B { fn foo(&mut self, f: F) //~ ERROR impl has stricter - //~^ WARNING future release where F: fmt::Debug + 'static, { self.list.push(Box::new(f)); From 57b08b025a4cebec3fb188e8b126fcdba840a575 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 14:52:24 -0400 Subject: [PATCH 04/68] assert that we are consuming all of the region obligations When we get around to resolving regions, we really ought to take region obligations into account. There is one case where they are presently being ignored. Keep ignoring them there for now but leave a TODO. --- src/librustc/infer/mod.rs | 6 +++--- src/librustc/traits/mod.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 110c49d820abb..ba99ff5291a1c 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1131,9 +1131,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context: DefId, region_map: ®ion::ScopeTree, free_regions: &FreeRegionMap<'tcx>) { - // TODO assert!(self.region_obligations.borrow().is_empty(), - // TODO "region_obligations not empty: {:#?}", - // TODO self.region_obligations.borrow()); + assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), + "region_obligations not empty: {:#?}", + self.region_obligations.borrow()); let region_rels = RegionRelations::new(self.tcx, region_context, diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a74504ac627c1..d39a6816babc1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -511,6 +511,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, unnormalized_env.reveal); tcx.infer_ctxt().enter(|infcx| { + let body_id = cause.body_id; let predicates = match fully_normalize( &infcx, cause, @@ -536,6 +537,14 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let region_scope_tree = region::ScopeTree::default(); let free_regions = FreeRegionMap::new(); + + // TODO We should really... do something with these. But as of + // this writing we were ignoring them, just without knowing + // it, and it would take some refactoring to stop doing so. + // (In particular, the needed methods currently live in + // regionck.) -nmatsakis + let _ = infcx.take_region_obligations(body_id); + infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, From 279882f4d07b73e27b15b2e518c8277ac3bd65d6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 Nov 2017 06:06:09 -0400 Subject: [PATCH 05/68] extract `regionck_outlives` into a separate helper function This helps make its inputs and outputs more clear. --- src/librustc/infer/mod.rs | 21 + src/librustc_typeck/check/mod.rs | 32 +- src/librustc_typeck/check/regionck.rs | 366 +------------- .../check/regionck_outlives.rs | 445 ++++++++++++++++++ src/librustc_typeck/lib.rs | 1 + 5 files changed, 498 insertions(+), 367 deletions(-) create mode 100644 src/librustc_typeck/check/regionck_outlives.rs diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index ba99ff5291a1c..c76d098bd6995 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1451,6 +1451,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tcx.generator_sig(def_id) } + + /// Normalizes associated types in `value`, potentially returning + /// new obligations that must further be processed. + pub fn partially_normalize_associated_types_in(&self, + span: Span, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T) + -> InferOk<'tcx, T> + where T : TypeFoldable<'tcx> + { + debug!("partially_normalize_associated_types_in(value={:?})", value); + let mut selcx = traits::SelectionContext::new(self); + let cause = ObligationCause::misc(span, body_id); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, param_env, cause, value); + debug!("partially_normalize_associated_types_in: result={:?} predicates={:?}", + value, + obligations); + InferOk { value, obligations } + } } impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 19ea1b1795004..0c4a5512dd601 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -136,7 +136,8 @@ mod autoderef; pub mod dropck; pub mod _match; pub mod writeback; -pub mod regionck; +mod regionck; +mod regionck_outlives; pub mod coercion; pub mod demand; pub mod method; @@ -657,29 +658,10 @@ impl<'a, 'gcx, 'tcx> Inherited<'a, 'gcx, 'tcx> { value: &T) -> T where T : TypeFoldable<'tcx> { - let ok = self.normalize_associated_types_in_as_infer_ok(span, body_id, param_env, value); + let ok = self.partially_normalize_associated_types_in(span, body_id, param_env, value); self.register_infer_ok_obligations(ok) } - fn normalize_associated_types_in_as_infer_ok(&self, - span: Span, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'tcx>, - value: &T) - -> InferOk<'tcx, T> - where T : TypeFoldable<'tcx> - { - debug!("normalize_associated_types_in(value={:?})", value); - let mut selcx = traits::SelectionContext::new(self); - let cause = ObligationCause::misc(span, body_id); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, param_env, cause, value); - debug!("normalize_associated_types_in: result={:?} predicates={:?}", - value, - obligations); - InferOk { value, obligations } - } - /// Replace any late-bound regions bound in `value` with /// free variants attached to `all_outlive_scope`. fn liberate_late_bound_regions(&self, @@ -1970,10 +1952,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { -> InferOk<'tcx, T> where T : TypeFoldable<'tcx> { - self.inh.normalize_associated_types_in_as_infer_ok(span, - self.body_id, - self.param_env, - value) + self.inh.partially_normalize_associated_types_in(span, + self.body_id, + self.param_env, + value) } pub fn require_type_meets(&self, diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index b1ac3abe23024..77a34023df3df 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -92,7 +92,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::traits; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, GenericKind, SubregionOrigin, VerifyBound}; +use rustc::infer::{self, GenericKind, SubregionOrigin}; use rustc::ty::adjustment; use rustc::ty::outlives::Component; use rustc::ty::wf; @@ -105,6 +105,8 @@ use syntax_pos::Span; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, PatKind}; +use super::regionck_outlives::RegionckOutlives; + // a variation on try that just returns unit macro_rules! ignore_err { ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () }) @@ -1132,6 +1134,27 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin, ty, minimum_lifetime); } + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive(&self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>) + { + let outlives = RegionckOutlives::new(&self.infcx, + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + self.body_id); + self.register_infer_ok_obligations(outlives.type_must_outlive(origin, ty, region)); + } + /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the /// resulting pointer is linked to the lifetime of its guarantor (if any). fn link_addr_of(&mut self, expr: &hir::Expr, @@ -1487,345 +1510,4 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_must_outlive(origin.clone(), ty, expr_region); } } - - /// Ensures that type is well-formed in `region`, which implies (among - /// other things) that all borrowed data reachable via `ty` outlives - /// `region`. - pub fn type_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>) - { - let ty = self.resolve_type(ty); - - debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", - ty, - region, - origin); - - assert!(!ty.has_escaping_regions()); - - let components = self.tcx.outlives_components(ty); - self.components_must_outlive(origin, components, region); - } - - fn components_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - components: Vec>, - region: ty::Region<'tcx>) - { - for component in components { - let origin = origin.clone(); - match component { - Component::Region(region1) => { - self.sub_regions(origin, region, region1); - } - Component::Param(param_ty) => { - self.param_ty_must_outlive(origin, region, param_ty); - } - Component::Projection(projection_ty) => { - self.projection_must_outlive(origin, region, projection_ty); - } - Component::EscapingProjection(subcomponents) => { - self.components_must_outlive(origin, subcomponents, region); - } - Component::UnresolvedInferenceVariable(v) => { - // ignore this, we presume it will yield an error - // later, since if a type variable is not resolved by - // this point it never will be - self.tcx.sess.delay_span_bug( - origin.span(), - &format!("unresolved inference variable in outlives: {:?}", v)); - } - } - } - } - - fn param_ty_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - param_ty: ty::ParamTy) { - debug!("param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", - region, param_ty, origin); - - let verify_bound = self.param_bound(param_ty); - let generic = GenericKind::Param(param_ty); - self.verify_generic_bound(origin, generic, region, verify_bound); - } - - fn projection_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>) - { - debug!("projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", - region, projection_ty, origin); - - // This case is thorny for inference. The fundamental problem is - // that there are many cases where we have choice, and inference - // doesn't like choice (the current region inference in - // particular). :) First off, we have to choose between using the - // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and - // OutlivesProjectionComponent rules, any one of which is - // sufficient. If there are no inference variables involved, it's - // not hard to pick the right rule, but if there are, we're in a - // bit of a catch 22: if we picked which rule we were going to - // use, we could add constraints to the region inference graph - // that make it apply, but if we don't add those constraints, the - // rule might not apply (but another rule might). For now, we err - // on the side of adding too few edges into the graph. - - // Compute the bounds we can derive from the environment or trait - // definition. We know that the projection outlives all the - // regions in this list. - let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); - - debug!("projection_must_outlive: env_bounds={:?}", - env_bounds); - - // If we know that the projection outlives 'static, then we're - // done here. - if env_bounds.contains(&&ty::ReStatic) { - debug!("projection_must_outlive: 'static as declared bound"); - return; - } - - // If declared bounds list is empty, the only applicable rule is - // OutlivesProjectionComponent. If there are inference variables, - // then, we can break down the outlives into more primitive - // components without adding unnecessary edges. - // - // If there are *no* inference variables, however, we COULD do - // this, but we choose not to, because the error messages are less - // good. For example, a requirement like `T::Item: 'r` would be - // translated to a requirement that `T: 'r`; when this is reported - // to the user, it will thus say "T: 'r must hold so that T::Item: - // 'r holds". But that makes it sound like the only way to fix - // the problem is to add `T: 'r`, which isn't true. So, if there are no - // inference variables, we use a verify constraint instead of adding - // edges, which winds up enforcing the same condition. - let needs_infer = projection_ty.needs_infer(); - if env_bounds.is_empty() && needs_infer { - debug!("projection_must_outlive: no declared bounds"); - - for component_ty in projection_ty.substs.types() { - self.type_must_outlive(origin.clone(), component_ty, region); - } - - for r in projection_ty.substs.regions() { - self.sub_regions(origin.clone(), region, r); - } - - return; - } - - // If we find that there is a unique declared bound `'b`, and this bound - // appears in the trait reference, then the best action is to require that `'b:'r`, - // so do that. This is best no matter what rule we use: - // - // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to - // the requirement that `'b:'r` - // - OutlivesProjectionComponent: this would require `'b:'r` in addition to - // other conditions - if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { - let unique_bound = env_bounds[0]; - debug!("projection_must_outlive: unique declared bound = {:?}", unique_bound); - if projection_ty.substs.regions().any(|r| env_bounds.contains(&r)) { - debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.sub_regions(origin.clone(), region, unique_bound); - return; - } - } - - // Fallback to verifying after the fact that there exists a - // declared bound, or that all the components appearing in the - // projection outlive; in some cases, this may add insufficient - // edges into the inference graph, leading to inference failures - // even though a satisfactory solution exists. - let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); - let generic = GenericKind::Projection(projection_ty); - self.verify_generic_bound(origin, generic.clone(), region, verify_bound); - } - - fn type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.sty { - ty::TyParam(p) => { - self.param_bound(p) - } - ty::TyProjection(data) => { - let declared_bounds = self.projection_declared_bounds(span, data); - self.projection_bound(span, declared_bounds, data) - } - _ => { - self.recursive_type_bound(span, ty) - } - } - } - - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { - debug!("param_bound(param_ty={:?})", - param_ty); - - let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); - - // Add in the default bound of fn body that applies to all in - // scope type parameters: - param_bounds.extend(self.implicit_region_bound); - - VerifyBound::AnyRegion(param_bounds) - } - - fn projection_declared_bounds(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec> - { - // First assemble bounds from where clauses and traits. - - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); - - declared_bounds.extend_from_slice( - &self.declared_projection_bounds_from_trait(span, projection_ty)); - - declared_bounds - } - - fn projection_bound(&self, - span: Span, - declared_bounds: Vec>, - projection_ty: ty::ProjectionTy<'tcx>) - -> VerifyBound<'tcx> { - debug!("projection_bound(declared_bounds={:?}, projection_ty={:?})", - declared_bounds, projection_ty); - - // see the extensive comment in projection_must_outlive - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(span, ty); - - VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) - } - - fn recursive_type_bound(&self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = vec![]; - - for subty in ty.walk_shallow() { - bounds.push(self.type_bound(span, subty)); - } - - let mut regions = ty.regions(); - regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllRegions(regions)); - - // remove bounds that must hold, since they are not interesting - bounds.retain(|b| !b.must_hold()); - - if bounds.len() == 1 { - bounds.pop().unwrap() - } else { - VerifyBound::AllBounds(bounds) - } - } - - fn declared_generic_bounds_from_env(&self, generic: GenericKind<'tcx>) - -> Vec> - { - let param_env = &self.param_env; - - // To start, collect bounds from user: - let mut param_bounds = self.tcx.required_region_bounds(generic.to_ty(self.tcx), - param_env.caller_bounds.to_vec()); - - // Next, collect regions we scraped from the well-formedness - // constraints in the fn signature. To do that, we walk the list - // of known relations from the fn ctxt. - // - // This is crucial because otherwise code like this fails: - // - // fn foo<'a, A>(x: &'a A) { x.bar() } - // - // The problem is that the type of `x` is `&'a A`. To be - // well-formed, then, A must be lower-generic by `'a`, but we - // don't know that this holds from first principles. - for &(r, p) in &self.region_bound_pairs { - debug!("generic={:?} p={:?}", - generic, - p); - if generic == p { - param_bounds.push(r); - } - } - - param_bounds - } - - fn declared_projection_bounds_from_trait(&self, - span: Span, - projection_ty: ty::ProjectionTy<'tcx>) - -> Vec> - { - debug!("projection_bounds(projection_ty={:?})", - projection_ty); - let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); - - // Say we have a projection `>::SomeType`. We are interested - // in looking for a trait definition like: - // - // ``` - // trait SomeTrait<'a> { - // type SomeType : 'a; - // } - // ``` - // - // we can thus deduce that `>::SomeType : 'a`. - let trait_predicates = self.tcx.predicates_of(projection_ty.trait_ref(self.tcx).def_id); - assert_eq!(trait_predicates.parent, None); - let predicates = trait_predicates.predicates.as_slice().to_vec(); - traits::elaborate_predicates(self.tcx, predicates) - .filter_map(|predicate| { - // we're only interesting in `T : 'a` style predicates: - let outlives = match predicate { - ty::Predicate::TypeOutlives(data) => data, - _ => { return None; } - }; - - debug!("projection_bounds: outlives={:?} (1)", - outlives); - - // apply the substitutions (and normalize any projected types) - let outlives = self.instantiate_type_scheme(span, - projection_ty.substs, - &outlives); - - debug!("projection_bounds: outlives={:?} (2)", - outlives); - - let region_result = self.commit_if_ok(|_| { - let (outlives, _) = - self.replace_late_bound_regions_with_fresh_var( - span, - infer::AssocTypeProjection(projection_ty.item_def_id), - &outlives); - - debug!("projection_bounds: outlives={:?} (3)", - outlives); - - // check whether this predicate applies to our current projection - let cause = self.fcx.misc(span); - match self.at(&cause, self.fcx.param_env).eq(outlives.0, ty) { - Ok(ok) => Ok((ok, outlives.1)), - Err(_) => Err(()) - } - }).map(|(ok, result)| { - self.register_infer_ok_obligations(ok); - result - }); - - debug!("projection_bounds: region_result={:?}", - region_result); - - region_result.ok() - }) - .collect() - } } diff --git a/src/librustc_typeck/check/regionck_outlives.rs b/src/librustc_typeck/check/regionck_outlives.rs new file mode 100644 index 0000000000000..9bd7384a48e15 --- /dev/null +++ b/src/librustc_typeck/check/regionck_outlives.rs @@ -0,0 +1,445 @@ +//! Temporary holding spot for some code I want to factor out. + +use rustc::traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::infer::{self, GenericKind, InferCtxt, InferOk, VerifyBound}; +use rustc::ty::subst::Subst; +use rustc::ty::outlives::Component; +use syntax::ast; +use syntax_pos::Span; + +pub struct RegionckOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + // Context provided by the caller: + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + + // Obligations that we accrue as we go: + obligations: PredicateObligations<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> { + pub fn new( + infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ) -> Self { + Self { + infcx, + region_bound_pairs, + implicit_region_bound, + param_env, + body_id, + obligations: vec![], + } + } + + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive( + mut self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) -> InferOk<'tcx, ()> { + self.type_must_outlive_pushing_obligations(origin, ty, region); + InferOk { + value: (), + obligations: self.obligations, + } + } + + /// Internal helper: ensure that `ty_must_outlive` and push obligations onto + /// our internal vector. + fn type_must_outlive_pushing_obligations( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + let ty = self.infcx.resolve_type_vars_if_possible(&ty); + + debug!( + "type_must_outlive(ty={:?}, region={:?}, origin={:?})", + ty, + region, + origin + ); + + assert!(!ty.has_escaping_regions()); + + let components = self.tcx().outlives_components(ty); + self.components_must_outlive(origin, components, region); + } + + fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + self.infcx.tcx + } + + fn components_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + components: Vec>, + region: ty::Region<'tcx>, + ) { + for component in components { + let origin = origin.clone(); + match component { + Component::Region(region1) => { + self.infcx.sub_regions(origin, region, region1); + } + Component::Param(param_ty) => { + self.param_ty_must_outlive(origin, region, param_ty); + } + Component::Projection(projection_ty) => { + self.projection_must_outlive(origin, region, projection_ty); + } + Component::EscapingProjection(subcomponents) => { + self.components_must_outlive(origin, subcomponents, region); + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.infcx.tcx.sess.delay_span_bug( + origin.span(), + &format!("unresolved inference variable in outlives: {:?}", v), + ); + } + } + } + } + + fn param_ty_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + param_ty: ty::ParamTy, + ) { + debug!( + "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", + region, + param_ty, + origin + ); + + let verify_bound = self.param_bound(param_ty); + let generic = GenericKind::Param(param_ty); + self.infcx + .verify_generic_bound(origin, generic, region, verify_bound); + } + + fn projection_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + ) { + debug!( + "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", + region, + projection_ty, + origin + ); + + // This case is thorny for inference. The fundamental problem is + // that there are many cases where we have choice, and inference + // doesn't like choice (the current region inference in + // particular). :) First off, we have to choose between using the + // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and + // OutlivesProjectionComponent rules, any one of which is + // sufficient. If there are no inference variables involved, it's + // not hard to pick the right rule, but if there are, we're in a + // bit of a catch 22: if we picked which rule we were going to + // use, we could add constraints to the region inference graph + // that make it apply, but if we don't add those constraints, the + // rule might not apply (but another rule might). For now, we err + // on the side of adding too few edges into the graph. + + // Compute the bounds we can derive from the environment or trait + // definition. We know that the projection outlives all the + // regions in this list. + let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); + + debug!("projection_must_outlive: env_bounds={:?}", env_bounds); + + // If we know that the projection outlives 'static, then we're + // done here. + if env_bounds.contains(&&ty::ReStatic) { + debug!("projection_must_outlive: 'static as declared bound"); + return; + } + + // If declared bounds list is empty, the only applicable rule is + // OutlivesProjectionComponent. If there are inference variables, + // then, we can break down the outlives into more primitive + // components without adding unnecessary edges. + // + // If there are *no* inference variables, however, we COULD do + // this, but we choose not to, because the error messages are less + // good. For example, a requirement like `T::Item: 'r` would be + // translated to a requirement that `T: 'r`; when this is reported + // to the user, it will thus say "T: 'r must hold so that T::Item: + // 'r holds". But that makes it sound like the only way to fix + // the problem is to add `T: 'r`, which isn't true. So, if there are no + // inference variables, we use a verify constraint instead of adding + // edges, which winds up enforcing the same condition. + let needs_infer = projection_ty.needs_infer(); + if env_bounds.is_empty() && needs_infer { + debug!("projection_must_outlive: no declared bounds"); + + for component_ty in projection_ty.substs.types() { + self.type_must_outlive_pushing_obligations(origin.clone(), component_ty, region); + } + + for r in projection_ty.substs.regions() { + self.infcx.sub_regions(origin.clone(), region, r); + } + + return; + } + + // If we find that there is a unique declared bound `'b`, and this bound + // appears in the trait reference, then the best action is to require that `'b:'r`, + // so do that. This is best no matter what rule we use: + // + // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to + // the requirement that `'b:'r` + // - OutlivesProjectionComponent: this would require `'b:'r` in addition to + // other conditions + if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { + let unique_bound = env_bounds[0]; + debug!( + "projection_must_outlive: unique declared bound = {:?}", + unique_bound + ); + if projection_ty + .substs + .regions() + .any(|r| env_bounds.contains(&r)) + { + debug!("projection_must_outlive: unique declared bound appears in trait ref"); + self.infcx.sub_regions(origin.clone(), region, unique_bound); + return; + } + } + + // Fallback to verifying after the fact that there exists a + // declared bound, or that all the components appearing in the + // projection outlive; in some cases, this may add insufficient + // edges into the inference graph, leading to inference failures + // even though a satisfactory solution exists. + let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); + let generic = GenericKind::Projection(projection_ty); + self.infcx + .verify_generic_bound(origin, generic.clone(), region, verify_bound); + } + + fn type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + match ty.sty { + ty::TyParam(p) => self.param_bound(p), + ty::TyProjection(data) => { + let declared_bounds = self.projection_declared_bounds(span, data); + self.projection_bound(span, declared_bounds, data) + } + _ => self.recursive_type_bound(span, ty), + } + } + + fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + debug!("param_bound(param_ty={:?})", param_ty); + + let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + param_bounds.extend(self.implicit_region_bound); + + VerifyBound::AnyRegion(param_bounds) + } + + fn projection_declared_bounds( + &mut self, + span: Span, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + // First assemble bounds from where clauses and traits. + + let mut declared_bounds = + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + + declared_bounds + .extend_from_slice(&mut self.declared_projection_bounds_from_trait(span, projection_ty)); + + declared_bounds + } + + fn projection_bound( + &mut self, + span: Span, + declared_bounds: Vec>, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> VerifyBound<'tcx> { + debug!( + "projection_bound(declared_bounds={:?}, projection_ty={:?})", + declared_bounds, + projection_ty + ); + + // see the extensive comment in projection_must_outlive + let ty = self.infcx + .tcx + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + let recursive_bound = self.recursive_type_bound(span, ty); + + VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) + } + + fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + let mut bounds = vec![]; + + for subty in ty.walk_shallow() { + bounds.push(self.type_bound(span, subty)); + } + + let mut regions = ty.regions(); + regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions + bounds.push(VerifyBound::AllRegions(regions)); + + // remove bounds that must hold, since they are not interesting + bounds.retain(|b| !b.must_hold()); + + if bounds.len() == 1 { + bounds.pop().unwrap() + } else { + VerifyBound::AllBounds(bounds) + } + } + + fn declared_generic_bounds_from_env( + &mut self, + generic: GenericKind<'tcx>, + ) -> Vec> { + let tcx = self.tcx(); + + // To start, collect bounds from user: + let mut param_bounds = + tcx.required_region_bounds(generic.to_ty(tcx), self.param_env.caller_bounds.to_vec()); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must be lower-generic by `'a`, but we + // don't know that this holds from first principles. + for &(r, p) in self.region_bound_pairs { + debug!("generic={:?} p={:?}", generic, p); + if generic == p { + param_bounds.push(r); + } + } + + param_bounds + } + + fn declared_projection_bounds_from_trait( + &mut self, + span: Span, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + debug!("projection_bounds(projection_ty={:?})", projection_ty); + let ty = self.tcx() + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + + // Say we have a projection `>::SomeType`. We are interested + // in looking for a trait definition like: + // + // ``` + // trait SomeTrait<'a> { + // type SomeType : 'a; + // } + // ``` + // + // we can thus deduce that `>::SomeType : 'a`. + let trait_predicates = self.tcx() + .predicates_of(projection_ty.trait_ref(self.tcx()).def_id); + assert_eq!(trait_predicates.parent, None); + let predicates = trait_predicates.predicates.as_slice().to_vec(); + traits::elaborate_predicates(self.tcx(), predicates) + .filter_map(|predicate| { + // we're only interesting in `T : 'a` style predicates: + let outlives = match predicate { + ty::Predicate::TypeOutlives(data) => data, + _ => { + return None; + } + }; + + debug!("projection_bounds: outlives={:?} (1)", outlives); + + // apply the substitutions (and normalize any projected types) + let outlives = outlives.subst(self.tcx(), projection_ty.substs); + let outlives = self.infcx.partially_normalize_associated_types_in( + span, + self.body_id, + self.param_env, + &outlives, + ); + let outlives = self.register_infer_ok_obligations(outlives); + + debug!("projection_bounds: outlives={:?} (2)", outlives); + + let region_result = self.infcx + .commit_if_ok(|_| { + let (outlives, _) = self.infcx.replace_late_bound_regions_with_fresh_var( + span, + infer::AssocTypeProjection(projection_ty.item_def_id), + &outlives, + ); + + debug!("projection_bounds: outlives={:?} (3)", outlives); + + // check whether this predicate applies to our current projection + let cause = ObligationCause::new( + span, + self.body_id, + ObligationCauseCode::MiscObligation, + ); + match self.infcx.at(&cause, self.param_env).eq(outlives.0, ty) { + Ok(ok) => Ok((ok, outlives.1)), + Err(_) => Err(()), + } + }) + .map(|(ok, result)| { + self.register_infer_ok_obligations(ok); + result + }); + + debug!("projection_bounds: region_result={:?}", region_result); + + region_result.ok() + }) + .collect() + } + + fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.obligations.extend(obligations); + value + } +} diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 5227955d7b902..014b8b14edb83 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -75,6 +75,7 @@ This API is completely unstable and subject to change. #![feature(advanced_slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(crate_visibility_modifier)] #![feature(conservative_impl_trait)] #![feature(match_default_bindings)] #![feature(never_type)] From 7317a4f9bdec6bc04925da0e33a2a7887911a4cd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 Nov 2017 05:31:19 -0400 Subject: [PATCH 06/68] move the `region_obligations` processing code into `InferCtxt` --- src/librustc/infer/mod.rs | 35 +-- .../infer/region_obligations.rs} | 232 +++++++++++++++--- src/librustc/traits/mod.rs | 2 +- src/librustc_typeck/check/mod.rs | 1 - src/librustc_typeck/check/regionck.rs | 56 ++--- 5 files changed, 236 insertions(+), 90 deletions(-) rename src/{librustc_typeck/check/regionck_outlives.rs => librustc/infer/region_obligations.rs} (66%) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index c76d098bd6995..41e1bf303b384 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -55,6 +55,7 @@ mod higher_ranked; pub mod lattice; mod lub; pub mod region_inference; +mod region_obligations; pub mod resolve; mod freshen; mod sub; @@ -160,6 +161,13 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // regionck to be sure that it has found *all* the region // obligations (otherwise, it's easy to fail to walk to a // particular node-id). + // + // Before running `resolve_regions_and_report_errors`, the creator + // of the inference context is expected to invoke + // `process_region_obligations` (defined in `self::region_obligations`) + // for each body-id in this map, which will process the + // obligations within. This is expected to be done 'late enough' + // that all type inference variables have been bound and so forth. region_obligations: RefCell>>>, } @@ -984,33 +992,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }) } - /// Registers that the given region obligation must be resolved - /// from within the scope of `body_id`. These regions are enqueued - /// and later processed by regionck, when full type information is - /// available (see `region_obligations` field for more - /// information). - pub fn register_region_obligation(&self, - body_id: ast::NodeId, - obligation: RegionObligation<'tcx>) - { - self.region_obligations.borrow_mut().entry(body_id) - .or_insert(vec![]) - .push(obligation); - } - - /// Get the region obligations that must be proven (during - /// `regionck`) for the given `body_id` (removing them from the - /// map as a side-effect). - pub fn take_region_obligations(&self, - body_id: ast::NodeId) - -> Vec> - { - match self.region_obligations.borrow_mut().remove(&body_id) { - None => vec![], - Some(vec) => vec, - } - } - pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid { self.type_variables .borrow_mut() diff --git a/src/librustc_typeck/check/regionck_outlives.rs b/src/librustc/infer/region_obligations.rs similarity index 66% rename from src/librustc_typeck/check/regionck_outlives.rs rename to src/librustc/infer/region_obligations.rs index 9bd7384a48e15..edcabd5318142 100644 --- a/src/librustc_typeck/check/regionck_outlives.rs +++ b/src/librustc/infer/region_obligations.rs @@ -1,27 +1,210 @@ -//! Temporary holding spot for some code I want to factor out. - -use rustc::traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations}; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::infer::{self, GenericKind, InferCtxt, InferOk, VerifyBound}; -use rustc::ty::subst::Subst; -use rustc::ty::outlives::Component; +//! Code that handles "type-outlives" constraints like `T: 'a`. This +//! is based on the `outlives_components` function defined on the tcx, +//! but it adds a bit of heuristics on top, in particular to deal with +//! associated types and projections. +//! +//! When we process a given `T: 'a` obligation, we may produce two +//! kinds of constraints for the region inferencer: +//! +//! - Relationships between inference variables and other regions. +//! For example, if we have `&'?0 u32: 'a`, then we would produce +//! a constraint that `'a <= '?0`. +//! - "Verifys" that must be checked after inferencing is done. +//! For example, if we know that, for some type parameter `T`, +//! `T: 'a + 'b`, and we have a requirement that `T: '?1`, +//! then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`. +//! - Note the difference with the previous case: here, the region +//! variable must be less than something else, so this doesn't +//! affect how inference works (it finds the smallest region that +//! will do); it's just a post-condition that we have to check. +//! +//! **The key point is that once this function is done, we have +//! reduced all of our "type-region outlives" obligations into relationships +//! between individual regions.** +//! +//! One key input to this function is the set of "region-bound pairs". +//! These are basically the relationships between type parameters and +//! regions that are in scope at the point where the outlives +//! obligation was incurred. **When type-checking a function, +//! particularly in the face of closures, this is not known until +//! regionck runs!** This is because some of those bounds come +//! from things we have yet to infer. +//! +//! Consider: +//! +//! ``` +//! fn bar(a: T, b: impl for<'a> Fn(&'a T)); +//! fn foo(x: T) { +//! bar(x, |y| { ... }) +//! // ^ closure arg +//! } +//! ``` +//! +//! Here, the type of `y` may involve inference variables and the +//! like, and it may also contain implied bounds that are needed to +//! type-check the closure body (e.g., here it informs us that `T` +//! outlives the late-bound region `'a`). +//! +//! > That said, in writing this, I have come to wonder: this +//! inference dependency, I think, is only interesting for +//! late-bound regions in the closure -- if the region appears free +//! in the closure signature, then the relationship must be known to +//! the caller (here, `foo`), and hence could be verified earlier +//! up. Moreover, we infer late-bound regions quite early on right +//! now, i.e., only when the expected signature is known. So we +//! *may* be able to sidestep this. Regardless, once the NLL +//! transition is complete, this concern will be gone. -nmatsakis + +use infer::{self, GenericKind, InferCtxt, InferOk, RegionObligation, SubregionOrigin, VerifyBound}; +use traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations}; +use ty::{self, Ty, TyCtxt, TypeFoldable}; +use ty::subst::Subst; +use ty::outlives::Component; use syntax::ast; use syntax_pos::Span; -pub struct RegionckOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - // Context provided by the caller: +impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { + /// Registers that the given region obligation must be resolved + /// from within the scope of `body_id`. These regions are enqueued + /// and later processed by regionck, when full type information is + /// available (see `region_obligations` field for more + /// information). + pub fn register_region_obligation( + &self, + body_id: ast::NodeId, + obligation: RegionObligation<'tcx>, + ) { + self.region_obligations + .borrow_mut() + .entry(body_id) + .or_insert(vec![]) + .push(obligation); + } + + /// Process the region obligations that must be proven (during + /// `regionck`) for the given `body_id`, given information about + /// the region bounds in scope and so forth. This function must be + /// invoked for all relevant body-ids before region inference is + /// done (or else an assert will fire). + /// + /// See the `region_obligations` field of `InferCtxt` for some + /// comments about how this funtion fits into the overall expected + /// flow of the the inferencer. The key point is that it is + /// invoked after all type-inference variables have been bound -- + /// towards the end of regionck. This also ensures that the + /// region-bound-pairs are available (see comments above regarding + /// closures). + /// + /// # Parameters + /// + /// - `region_bound_pairs`: the set of region bounds implied by + /// the parameters and where-clauses. In particular, each pair + /// `('a, K)` in this list tells us that the bounds in scope + /// indicate that `K: 'a`, where `K` is either a generic + /// parameter like `T` or a projection like `T::Item`. + /// - `implicit_region_bound`: if some, this is a region bound + /// that is considered to hold for all type parameters (the + /// function body). + /// - `param_env` is the parameter environment for the enclosing function. + /// - `body_id` is the body-id whose region obligations are being + /// processed. + /// + /// # Returns + /// + /// This function may have to perform normalizations, and hence it + /// returns an `InferOk` with subobligations that must be + /// processed. + pub fn process_registered_region_obligations( + &self, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + ) -> InferOk<'tcx, ()> { + let region_obligations = match self.region_obligations.borrow_mut().remove(&body_id) { + None => vec![], + Some(vec) => vec, + }; + + let mut outlives = TypeOutlives::new( + self, + region_bound_pairs, + implicit_region_bound, + param_env, + body_id, + ); + + for RegionObligation { + sup_type, + sub_region, + cause, + } in region_obligations + { + let origin = SubregionOrigin::from_obligation_cause( + &cause, + || infer::RelateParamBound(cause.span, sup_type), + ); + + outlives.type_must_outlive(origin, sup_type, sub_region); + } + + InferOk { + value: (), + obligations: outlives.into_accrued_obligations(), + } + } + + /// Processes a single ad-hoc region obligation that was not + /// registered in advance. + pub fn type_must_outlive( + &self, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + body_id: ast::NodeId, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) -> InferOk<'tcx, ()> { + let mut outlives = TypeOutlives::new( + self, + region_bound_pairs, + implicit_region_bound, + param_env, + body_id, + ); + outlives.type_must_outlive(origin, ty, region); + InferOk { + value: (), + obligations: outlives.into_accrued_obligations(), + } + } + + /// Ignore the region obligations for a given `body_id`, not bothering to + /// prove them. This function should not really exist; it is used to accommodate some older + /// code for the time being. + pub fn ignore_region_obligations(&self, body_id: ast::NodeId) { + self.region_obligations.borrow_mut().remove(&body_id); + } +} + +#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =) +struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + // See the comments on `process_registered_region_obligations` for the meaning + // of these fields. infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, - // Obligations that we accrue as we go: + /// These are sub-obligations that we accrue as we go; they result + /// from any normalizations we had to do. obligations: PredicateObligations<'tcx>, } -impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> { - pub fn new( +impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { + fn new( infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, @@ -38,6 +221,12 @@ impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> { } } + /// Returns the obligations that accrued as a result of the + /// `type_must_outlive` calls. + fn into_accrued_obligations(self) -> PredicateObligations<'tcx> { + self.obligations + } + /// Adds constraints to inference such that `T: 'a` holds (or /// reports an error if it cannot). /// @@ -46,22 +235,7 @@ impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> { /// - `origin`, the reason we need this constraint /// - `ty`, the type `T` /// - `region`, the region `'a` - pub fn type_must_outlive( - mut self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>, - ) -> InferOk<'tcx, ()> { - self.type_must_outlive_pushing_obligations(origin, ty, region); - InferOk { - value: (), - obligations: self.obligations, - } - } - - /// Internal helper: ensure that `ty_must_outlive` and push obligations onto - /// our internal vector. - fn type_must_outlive_pushing_obligations( + fn type_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, @@ -199,7 +373,7 @@ impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> { debug!("projection_must_outlive: no declared bounds"); for component_ty in projection_ty.substs.types() { - self.type_must_outlive_pushing_obligations(origin.clone(), component_ty, region); + self.type_must_outlive(origin.clone(), component_ty, region); } for r in projection_ty.substs.regions() { diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index d39a6816babc1..f0f35aa4bc7b6 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -543,7 +543,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // it, and it would take some refactoring to stop doing so. // (In particular, the needed methods currently live in // regionck.) -nmatsakis - let _ = infcx.take_region_obligations(body_id); + let _ = infcx.ignore_region_obligations(body_id); infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); let predicates = match infcx.fully_resolve(&predicates) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0c4a5512dd601..c8b2032a49871 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -137,7 +137,6 @@ pub mod dropck; pub mod _match; pub mod writeback; mod regionck; -mod regionck_outlives; pub mod coercion; pub mod demand; pub mod method; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 77a34023df3df..20487dda20190 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -90,9 +90,8 @@ use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; -use rustc::traits; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, GenericKind, SubregionOrigin}; +use rustc::infer::{self, InferOk, GenericKind}; use rustc::ty::adjustment; use rustc::ty::outlives::Component; use rustc::ty::wf; @@ -105,8 +104,6 @@ use syntax_pos::Span; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, PatKind}; -use super::regionck_outlives::RegionckOutlives; - // a variation on try that just returns unit macro_rules! ignore_err { ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () }) @@ -360,28 +357,21 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // obligations. So make sure we process those. self.select_all_obligations_or_error(); - // Make a copy of the region obligations vec because we'll need - // to be able to borrow the fulfillment-cx below when projecting. - let region_obligations = self.infcx.take_region_obligations(node_id); - - for r_o in ®ion_obligations { - debug!("visit_region_obligations: r_o={:?} cause={:?}", - r_o, r_o.cause); - let sup_type = self.resolve_type(r_o.sup_type); - let origin = self.code_to_origin(&r_o.cause, sup_type); - self.type_must_outlive(origin, sup_type, r_o.sub_region); - } - - // Processing the region obligations should not cause the list to grow further: - assert!(self.infcx.take_region_obligations(node_id).is_empty()); - } - - fn code_to_origin(&self, - cause: &traits::ObligationCause<'tcx>, - sup_type: Ty<'tcx>) - -> SubregionOrigin<'tcx> { - SubregionOrigin::from_obligation_cause(cause, - || infer::RelateParamBound(cause.span, sup_type)) + let InferOk { value: (), obligations } = + self.infcx.process_registered_region_obligations( + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + self.body_id); + + // TODO -- It feels like we ought to loop here; these new + // obligations, when selected, could cause the list of region + // obligations to grow further. Fortunately, I believe that if + // that happens it will at least lead to an ICE today, because + // `resolve_regions_and_report_errors` (which runs after *all* + // obligations have been selected) will assert that there are + // no unsolved region obligations. + self.register_predicates(obligations); } /// This method populates the region map's `free_region_map`. It walks over the transformed @@ -1147,12 +1137,14 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ty: Ty<'tcx>, region: ty::Region<'tcx>) { - let outlives = RegionckOutlives::new(&self.infcx, - &self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - self.body_id); - self.register_infer_ok_obligations(outlives.type_must_outlive(origin, ty, region)); + let infer_ok = self.infcx.type_must_outlive(&self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + self.body_id, + origin, + ty, + region); + self.register_infer_ok_obligations(infer_ok) } /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the From 9817b535a2ea011fe3b0b7d59acf653c9ffb6104 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 Nov 2017 18:03:43 -0400 Subject: [PATCH 07/68] refactor how we extract outlives bounds from trait definitions This new way is **slightly** less expressive (I would be shocked if it affects any code, though) when it comes to higher-ranked bounds or a few other weird tricks. But we don't handle those consistently regardless, and the new way does not require normalization and is just wildly simpler. --- src/librustc/infer/region_obligations.rs | 234 +++++++++-------------- src/librustc/ty/mod.rs | 28 +++ src/librustc_typeck/check/regionck.rs | 36 ++-- 3 files changed, 132 insertions(+), 166 deletions(-) diff --git a/src/librustc/infer/region_obligations.rs b/src/librustc/infer/region_obligations.rs index edcabd5318142..4cd9c3f9ae39c 100644 --- a/src/librustc/infer/region_obligations.rs +++ b/src/librustc/infer/region_obligations.rs @@ -55,13 +55,13 @@ //! *may* be able to sidestep this. Regardless, once the NLL //! transition is complete, this concern will be gone. -nmatsakis -use infer::{self, GenericKind, InferCtxt, InferOk, RegionObligation, SubregionOrigin, VerifyBound}; -use traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations}; +use hir::def_id::DefId; +use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use traits; use ty::{self, Ty, TyCtxt, TypeFoldable}; -use ty::subst::Subst; +use ty::subst::{Subst, Substs}; use ty::outlives::Component; use syntax::ast; -use syntax_pos::Span; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Registers that the given region obligation must be resolved @@ -120,19 +120,14 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, - ) -> InferOk<'tcx, ()> { + ) { let region_obligations = match self.region_obligations.borrow_mut().remove(&body_id) { None => vec![], Some(vec) => vec, }; - let mut outlives = TypeOutlives::new( - self, - region_bound_pairs, - implicit_region_bound, - param_env, - body_id, - ); + let outlives = + TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); for RegionObligation { sup_type, @@ -147,11 +142,6 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { outlives.type_must_outlive(origin, sup_type, sub_region); } - - InferOk { - value: (), - obligations: outlives.into_accrued_obligations(), - } } /// Processes a single ad-hoc region obligation that was not @@ -161,23 +151,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, - body_id: ast::NodeId, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, region: ty::Region<'tcx>, - ) -> InferOk<'tcx, ()> { - let mut outlives = TypeOutlives::new( - self, - region_bound_pairs, - implicit_region_bound, - param_env, - body_id, - ); + ) { + let outlives = + TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); outlives.type_must_outlive(origin, ty, region); - InferOk { - value: (), - obligations: outlives.into_accrued_obligations(), - } } /// Ignore the region obligations for a given `body_id`, not bothering to @@ -196,11 +176,6 @@ struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> { region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, - body_id: ast::NodeId, - - /// These are sub-obligations that we accrue as we go; they result - /// from any normalizations we had to do. - obligations: PredicateObligations<'tcx>, } impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { @@ -209,24 +184,15 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, - body_id: ast::NodeId, ) -> Self { Self { infcx, region_bound_pairs, implicit_region_bound, param_env, - body_id, - obligations: vec![], } } - /// Returns the obligations that accrued as a result of the - /// `type_must_outlive` calls. - fn into_accrued_obligations(self) -> PredicateObligations<'tcx> { - self.obligations - } - /// Adds constraints to inference such that `T: 'a` holds (or /// reports an error if it cannot). /// @@ -236,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { /// - `ty`, the type `T` /// - `region`, the region `'a` fn type_must_outlive( - &mut self, + &self, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, region: ty::Region<'tcx>, @@ -261,7 +227,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } fn components_must_outlive( - &mut self, + &self, origin: infer::SubregionOrigin<'tcx>, components: Vec>, region: ty::Region<'tcx>, @@ -295,7 +261,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } fn param_ty_must_outlive( - &mut self, + &self, origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, param_ty: ty::ParamTy, @@ -314,7 +280,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } fn projection_must_outlive( - &mut self, + &self, origin: infer::SubregionOrigin<'tcx>, region: ty::Region<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, @@ -343,7 +309,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // Compute the bounds we can derive from the environment or trait // definition. We know that the projection outlives all the // regions in this list. - let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty); + let env_bounds = self.projection_declared_bounds(projection_ty); debug!("projection_must_outlive: env_bounds={:?}", env_bounds); @@ -413,24 +379,24 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // projection outlive; in some cases, this may add insufficient // edges into the inference graph, leading to inference failures // even though a satisfactory solution exists. - let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty); + let verify_bound = self.projection_bound(env_bounds, projection_ty); let generic = GenericKind::Projection(projection_ty); self.infcx .verify_generic_bound(origin, generic.clone(), region, verify_bound); } - fn type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { match ty.sty { ty::TyParam(p) => self.param_bound(p), ty::TyProjection(data) => { - let declared_bounds = self.projection_declared_bounds(span, data); - self.projection_bound(span, declared_bounds, data) + let declared_bounds = self.projection_declared_bounds(data); + self.projection_bound(declared_bounds, data) } - _ => self.recursive_type_bound(span, ty), + _ => self.recursive_type_bound(ty), } } - fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { debug!("param_bound(param_ty={:?})", param_ty); let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); @@ -443,8 +409,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } fn projection_declared_bounds( - &mut self, - span: Span, + &self, projection_ty: ty::ProjectionTy<'tcx>, ) -> Vec> { // First assemble bounds from where clauses and traits. @@ -453,14 +418,13 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); declared_bounds - .extend_from_slice(&mut self.declared_projection_bounds_from_trait(span, projection_ty)); + .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty)); declared_bounds } fn projection_bound( - &mut self, - span: Span, + &self, declared_bounds: Vec>, projection_ty: ty::ProjectionTy<'tcx>, ) -> VerifyBound<'tcx> { @@ -474,16 +438,16 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { let ty = self.infcx .tcx .mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(span, ty); + let recursive_bound = self.recursive_type_bound(ty); VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) } - fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { let mut bounds = vec![]; for subty in ty.walk_shallow() { - bounds.push(self.type_bound(span, subty)); + bounds.push(self.type_bound(subty)); } let mut regions = ty.regions(); @@ -501,7 +465,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { } fn declared_generic_bounds_from_env( - &mut self, + &self, generic: GenericKind<'tcx>, ) -> Vec> { let tcx = self.tcx(); @@ -531,89 +495,75 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { param_bounds } + /// Given a projection like `>::Bar`, returns any bounds + /// declared in the trait definition. For example, if the trait were + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// then this function would return `'x`. This is subject to the + /// limitations around higher-ranked bounds described in + /// `region_bounds_declared_on_associated_item`. fn declared_projection_bounds_from_trait( - &mut self, - span: Span, + &self, projection_ty: ty::ProjectionTy<'tcx>, ) -> Vec> { debug!("projection_bounds(projection_ty={:?})", projection_ty); - let ty = self.tcx() - .mk_projection(projection_ty.item_def_id, projection_ty.substs); - - // Say we have a projection `>::SomeType`. We are interested - // in looking for a trait definition like: - // - // ``` - // trait SomeTrait<'a> { - // type SomeType : 'a; - // } - // ``` - // - // we can thus deduce that `>::SomeType : 'a`. - let trait_predicates = self.tcx() - .predicates_of(projection_ty.trait_ref(self.tcx()).def_id); - assert_eq!(trait_predicates.parent, None); - let predicates = trait_predicates.predicates.as_slice().to_vec(); - traits::elaborate_predicates(self.tcx(), predicates) - .filter_map(|predicate| { - // we're only interesting in `T : 'a` style predicates: - let outlives = match predicate { - ty::Predicate::TypeOutlives(data) => data, - _ => { - return None; - } - }; - - debug!("projection_bounds: outlives={:?} (1)", outlives); - - // apply the substitutions (and normalize any projected types) - let outlives = outlives.subst(self.tcx(), projection_ty.substs); - let outlives = self.infcx.partially_normalize_associated_types_in( - span, - self.body_id, - self.param_env, - &outlives, - ); - let outlives = self.register_infer_ok_obligations(outlives); - - debug!("projection_bounds: outlives={:?} (2)", outlives); - - let region_result = self.infcx - .commit_if_ok(|_| { - let (outlives, _) = self.infcx.replace_late_bound_regions_with_fresh_var( - span, - infer::AssocTypeProjection(projection_ty.item_def_id), - &outlives, - ); - - debug!("projection_bounds: outlives={:?} (3)", outlives); - - // check whether this predicate applies to our current projection - let cause = ObligationCause::new( - span, - self.body_id, - ObligationCauseCode::MiscObligation, - ); - match self.infcx.at(&cause, self.param_env).eq(outlives.0, ty) { - Ok(ok) => Ok((ok, outlives.1)), - Err(_) => Err(()), - } - }) - .map(|(ok, result)| { - self.register_infer_ok_obligations(ok); - result - }); - - debug!("projection_bounds: region_result={:?}", region_result); - - region_result.ok() - }) - .collect() + let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); + for r in &mut bounds { + *r = r.subst(self.tcx(), projection_ty.substs); + } + bounds } - fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'tcx, T>) -> T { - let InferOk { value, obligations } = infer_ok; - self.obligations.extend(obligations); - value + /// Given the def-id of an associated item, returns any region + /// bounds attached to that associated item from the trait definition. + /// + /// For example: + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// If we were given the def-id of `Foo::Bar`, we would return + /// `'a`. You could then apply the substitutions from the + /// projection to convert this into your namespace. This also + /// works if the user writes `where >::Bar: 'a` on + /// the trait. In fact, it works by searching for just such a + /// where-clause. + /// + /// It will not, however, work for higher-ranked bounds like: + /// + /// ```rust + /// trait Foo<'a, 'b> + /// where for<'x> >::Bar: 'x + /// { + /// type Bar; + /// } + /// ``` + /// + /// This is for simplicity, and because we are not really smart + /// enough to cope with such bounds anywhere. + fn region_bounds_declared_on_associated_item( + &self, + assoc_item_def_id: DefId, + ) -> Vec> { + let tcx = self.tcx(); + let assoc_item = tcx.associated_item(assoc_item_def_id); + let trait_def_id = assoc_item.container.assert_trait(); + let trait_predicates = tcx.predicates_of(trait_def_id); + let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); + let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); + traits::elaborate_predicates(tcx, trait_predicates.predicates) + .filter_map(|p| p.to_opt_type_outlives()) + .filter_map(|p| tcx.no_late_bound_regions(&p)) + .filter(|p| p.0 == identity_proj) + .map(|p| p.1) + .collect() } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 0deababd21829..913532b514af9 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -144,6 +144,15 @@ pub enum AssociatedItemContainer { } impl AssociatedItemContainer { + /// Asserts that this is the def-id of an associated item declared + /// in a trait, and returns the trait def-id. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self) + } + } + pub fn id(&self) -> DefId { match *self { TraitContainer(id) => id, @@ -1200,6 +1209,25 @@ impl<'tcx> Predicate<'tcx> { } } } + + pub fn to_opt_type_outlives(&self) -> Option> { + match *self { + Predicate::TypeOutlives(data) => { + Some(data) + } + Predicate::Trait(..) | + Predicate::Projection(..) | + Predicate::Equate(..) | + Predicate::Subtype(..) | + Predicate::RegionOutlives(..) | + Predicate::WellFormed(..) | + Predicate::ObjectSafe(..) | + Predicate::ClosureKind(..) | + Predicate::ConstEvaluatable(..) => { + None + } + } + } } /// Represents the bounds declared on a particular set of type diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 20487dda20190..7f1547c0c44d3 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -91,7 +91,7 @@ use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, InferOk, GenericKind}; +use rustc::infer::{self, GenericKind}; use rustc::ty::adjustment; use rustc::ty::outlives::Component; use rustc::ty::wf; @@ -357,21 +357,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // obligations. So make sure we process those. self.select_all_obligations_or_error(); - let InferOk { value: (), obligations } = - self.infcx.process_registered_region_obligations( - &self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - self.body_id); - - // TODO -- It feels like we ought to loop here; these new - // obligations, when selected, could cause the list of region - // obligations to grow further. Fortunately, I believe that if - // that happens it will at least lead to an ICE today, because - // `resolve_regions_and_report_errors` (which runs after *all* - // obligations have been selected) will assert that there are - // no unsolved region obligations. - self.register_predicates(obligations); + self.infcx.process_registered_region_obligations( + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + self.body_id); } /// This method populates the region map's `free_region_map`. It walks over the transformed @@ -1137,14 +1127,12 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ty: Ty<'tcx>, region: ty::Region<'tcx>) { - let infer_ok = self.infcx.type_must_outlive(&self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - self.body_id, - origin, - ty, - region); - self.register_infer_ok_obligations(infer_ok) + self.infcx.type_must_outlive(&self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + origin, + ty, + region); } /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the From c811e07fe177fd007b53e088c3c274fab0c70a27 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 05:37:18 -0400 Subject: [PATCH 08/68] do not invoke `required_region_bounds` in `region_obligations` Instead, just search the param env predicates directly. This is equivalent to what we were doing before but more efficient. --- src/librustc/infer/region_obligations.rs | 40 ++++++++++++++++++++---- src/librustc/ty/mod.rs | 6 ++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/librustc/infer/region_obligations.rs b/src/librustc/infer/region_obligations.rs index 4cd9c3f9ae39c..05e14daa2813f 100644 --- a/src/librustc/infer/region_obligations.rs +++ b/src/librustc/infer/region_obligations.rs @@ -470,9 +470,16 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { ) -> Vec> { let tcx = self.tcx(); - // To start, collect bounds from user: + // To start, collect bounds from user environment. Note that + // parameter environments are already elaborated, so we don't + // have to worry about that. Comparing using `==` is a bit + // dubious for projections, but it will work for simple cases + // like `T` and `T::Item`. It may not work as well for things + // like `>::Item`. let mut param_bounds = - tcx.required_region_bounds(generic.to_ty(tcx), self.param_env.caller_bounds.to_vec()); + self.collect_outlives_from_predicate_list( + generic.to_ty(tcx), + self.param_env.caller_bounds); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -559,10 +566,31 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { let trait_predicates = tcx.predicates_of(trait_def_id); let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); - traits::elaborate_predicates(tcx, trait_predicates.predicates) - .filter_map(|p| p.to_opt_type_outlives()) - .filter_map(|p| tcx.no_late_bound_regions(&p)) - .filter(|p| p.0 == identity_proj) + self.collect_outlives_from_predicate_list( + identity_proj, + traits::elaborate_predicates(tcx, trait_predicates.predicates)) + } + + /// Searches through a predicate list for a predicate `T: 'a`. + /// + /// Careful: does not elaborate predicates, and just uses `==` + /// when comparing `ty` for equality, so `ty` must be something + /// that does not involve inference variables and where you + /// otherwise want a precise match. + fn collect_outlives_from_predicate_list( + &self, + ty: Ty<'tcx>, + predicates: I, + ) -> Vec> + where + I: IntoIterator, + P: AsRef>, + { + predicates + .into_iter() + .filter_map(|p| p.as_ref().to_opt_type_outlives()) + .filter_map(|p| self.tcx().no_late_bound_regions(&p)) + .filter(|p| p.0 == ty) .map(|p| p.1) .collect() } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 913532b514af9..a9f6f763393e1 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -904,6 +904,12 @@ pub enum Predicate<'tcx> { ConstEvaluatable(DefId, &'tcx Substs<'tcx>), } +impl<'tcx> AsRef> for Predicate<'tcx> { + fn as_ref(&self) -> &Predicate<'tcx> { + self + } +} + impl<'a, 'gcx, 'tcx> Predicate<'tcx> { /// Performs a substitution suitable for going from a /// poly-trait-ref to supertraits that must hold if that From 100a3471e92fa920a1ad77bd1cc912c4f6beab08 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 05:58:57 -0400 Subject: [PATCH 09/68] regionck: only add implied bounds from root fn to `free_region_map` --- src/librustc_typeck/check/regionck.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 7f1547c0c44d3..06e0e6ccdb59b 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -144,6 +144,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.resolve_regions_and_report_errors(); } + /// Region check a function body. Not invoked on closures, but + /// only on the "root" fn item (in which closures may be + /// embedded). Walks the function body and adds various add'l + /// constraints that are needed for region inference. This is + /// separated both to isolate "pure" region constraints from the + /// rest of type check and because sometimes we need type + /// inference to have completed before we can determine which + /// constraints to add. pub fn regionck_fn(&self, fn_id: ast::NodeId, body: &'gcx hir::Body) { @@ -414,7 +422,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // 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); + if body_id == self.fcx.body_id { + // Only modify `free_region_map` if these + // are parameters from the root + // function. That's because this data + // struture is shared across all functions + // and hence we don't want to take implied + // bounds from one closure and use them + // outside. + self.free_region_map.relate_regions(r_a, r_b); + } } } } From 3d1b78bef94baf6d52c31b3de6adbfaefdb12899 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 07:13:46 -0400 Subject: [PATCH 10/68] extract out the implied bounds code from `regionck` --- src/librustc/traits/fulfill.rs | 7 +- src/librustc_typeck/check/mod.rs | 1 + src/librustc_typeck/check/regionck.rs | 339 ++++-------------- .../check/regionck_implied_bounds.rs | 280 +++++++++++++++ 4 files changed, 357 insertions(+), 270 deletions(-) create mode 100644 src/librustc_typeck/check/regionck_implied_bounds.rs diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 6767fbae3d8b5..297feead61760 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -139,9 +139,10 @@ impl<'a, 'gcx, 'tcx> FulfillmentContext<'tcx> { }); } - pub fn register_predicate_obligations(&mut self, - infcx: &InferCtxt<'a, 'gcx, 'tcx>, - obligations: Vec>) + pub fn register_predicate_obligations(&mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + obligations: I) + where I: IntoIterator> { for obligation in obligations { self.register_predicate_obligation(infcx, obligation); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c8b2032a49871..7d98a1c9246b6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -137,6 +137,7 @@ pub mod dropck; pub mod _match; pub mod writeback; mod regionck; +mod regionck_implied_bounds; pub mod coercion; pub mod demand; pub mod method; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 06e0e6ccdb59b..5954a0f2ecdce 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -84,17 +84,14 @@ use check::dropck; use check::FnCtxt; -use middle::free_region::FreeRegionMap; use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{self, GenericKind}; +use rustc::ty::{self, Ty}; +use rustc::infer; use rustc::ty::adjustment; -use rustc::ty::outlives::Component; -use rustc::ty::wf; use std::mem; use std::ops::Deref; @@ -104,6 +101,8 @@ use syntax_pos::Span; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, PatKind}; +use super::regionck_implied_bounds::OutlivesEnvironment; + // a variation on try that just returns unit macro_rules! ignore_err { ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () }) @@ -116,7 +115,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn regionck_expr(&self, body: &'gcx hir::Body) { let subject = self.tcx.hir.body_owner_def_id(body.id()); let id = body.value.id; - let mut rcx = RegionCtxt::new(self, RepeatingScope(id), id, Subject(subject)); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(id), + id, + Subject(subject), + self.param_env); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_body(body); @@ -125,7 +128,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.resolve_regions_and_report_errors(); assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.free_region_map; + self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); } /// Region checking during the WF phase for items. `wf_tys` are the @@ -136,10 +139,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { wf_tys: &[Ty<'tcx>]) { debug!("regionck_item(item.id={:?}, wf_tys={:?}", item_id, wf_tys); let subject = self.tcx.hir.local_def_id(item_id); - let mut rcx = RegionCtxt::new(self, RepeatingScope(item_id), item_id, Subject(subject)); - rcx.free_region_map.relate_free_regions_from_predicates( - &self.param_env.caller_bounds); - rcx.relate_free_regions(wf_tys, item_id, span); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(item_id), + item_id, + Subject(subject), + self.param_env); + rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); rcx.visit_region_obligations(item_id); rcx.resolve_regions_and_report_errors(); } @@ -158,23 +163,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { debug!("regionck_fn(id={})", fn_id); let subject = self.tcx.hir.body_owner_def_id(body.id()); let node_id = body.value.id; - let mut rcx = RegionCtxt::new(self, RepeatingScope(node_id), node_id, Subject(subject)); + let mut rcx = RegionCtxt::new(self, + RepeatingScope(node_id), + node_id, + Subject(subject), + self.param_env); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.free_region_map.relate_free_regions_from_predicates( - &self.param_env.caller_bounds); - rcx.resolve_regions_and_report_errors(); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes // (e.g., `regionck_item`), we don't have an enclosing tables. assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.free_region_map; + self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); } } @@ -184,11 +190,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, - pub region_scope_tree: Rc, - free_region_map: FreeRegionMap<'tcx>, + outlives_environment: OutlivesEnvironment<'tcx>, // id of innermost fn body id body_id: ast::NodeId, @@ -204,24 +208,6 @@ pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } -/// Implied bounds are region relationships that we deduce -/// automatically. The idea is that (e.g.) a caller must check that a -/// function's argument types are well-formed immediately before -/// calling that fn, and hence the *callee* can assume that its -/// argument types are well-formed. This may imply certain relationships -/// between generic parameters. For example: -/// -/// fn foo<'a,T>(x: &'a T) -/// -/// can only be called with a `'a` and `T` such that `&'a T` is WF. -/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. -#[derive(Debug)] -enum ImpliedBound<'tcx> { - RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>), - RegionSubParam(ty::Region<'tcx>, ty::ParamTy), - RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>), -} - impl<'a, 'gcx, 'tcx> Deref for RegionCtxt<'a, 'gcx, 'tcx> { type Target = FnCtxt<'a, 'gcx, 'tcx>; fn deref(&self) -> &Self::Target { @@ -236,8 +222,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { pub fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, RepeatingScope(initial_repeating_scope): RepeatingScope, initial_body_id: ast::NodeId, - Subject(subject): Subject) -> RegionCtxt<'a, 'gcx, 'tcx> { + Subject(subject): Subject, + param_env: ty::ParamEnv<'tcx>) + -> RegionCtxt<'a, 'gcx, 'tcx> { let region_scope_tree = fcx.tcx.region_scope_tree(subject); + let outlives_environment = OutlivesEnvironment::new(param_env); RegionCtxt { fcx, region_scope_tree, @@ -245,20 +234,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body_id: initial_body_id, call_site_scope: None, subject_def_id: subject, - region_bound_pairs: Vec::new(), - free_region_map: FreeRegionMap::new(), + outlives_environment, } } - fn set_call_site_scope(&mut self, call_site_scope: Option) - -> Option { - mem::replace(&mut self.call_site_scope, call_site_scope) - } - - fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId { - mem::replace(&mut self.body_id, body_id) - } - fn set_repeating_scope(&mut self, scope: ast::NodeId) -> ast::NodeId { mem::replace(&mut self.repeating_scope, scope) } @@ -302,6 +281,18 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.resolve_type(ty) } + /// This is the "main" function when region-checking a function item or a closure + /// within a function item. It begins by updating various fields (e.g., `call_site_scope` + /// and `outlives_environment`) to be appropriate to the function and then adds constraints + /// derived from the function body. + /// + /// Note that it does **not** restore the state of the fields that + /// it updates! This is intentional, since -- for the main + /// function -- we wish to be able to read the final + /// `outlives_environment` and other fields from the caller. For + /// closures, however, we save and restore any "scoped state" + /// before we invoke this function. (See `visit_fn` in the + /// `intravisit::Visitor` impl below.) fn visit_fn_body(&mut self, id: ast::NodeId, // the id of the fn itself body: &'gcx hir::Body, @@ -311,9 +302,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { debug!("visit_fn_body(id={})", id); let body_id = body.id(); + self.body_id = body_id.node_id; let call_site = region::Scope::CallSite(body.value.hir_id.local_id); - let old_call_site_scope = self.set_call_site_scope(Some(call_site)); + self.call_site_scope = Some(call_site); let fn_sig = { let fn_hir_id = self.tcx.hir.node_to_hir_id(id); @@ -325,8 +317,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } }; - let old_region_bounds_pairs_len = self.region_bound_pairs.len(); - // Collect the types from which we create inferred bounds. // For the return type, if diverging, substitute `bool` just // because it will have no effect. @@ -335,8 +325,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let fn_sig_tys: Vec<_> = fn_sig.inputs().iter().cloned().chain(Some(fn_sig.output())).collect(); - let old_body_id = self.set_body_id(body_id.node_id); - self.relate_free_regions(&fn_sig_tys[..], body_id.node_id, span); + self.outlives_environment.add_implied_bounds( + self.fcx, + &fn_sig_tys[..], + body_id.node_id, + span); self.link_fn_args(region::Scope::Node(body.value.hir_id.local_id), &body.arguments); self.visit_body(body); self.visit_region_obligations(body_id.node_id); @@ -349,11 +342,6 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.type_of_node_must_outlive(infer::CallReturn(span), body_hir_id, call_site_region); - - self.region_bound_pairs.truncate(old_region_bounds_pairs_len); - - self.set_body_id(old_body_id); - self.set_call_site_scope(old_call_site_scope); } fn visit_region_obligations(&mut self, node_id: ast::NodeId) @@ -366,217 +354,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.select_all_obligations_or_error(); self.infcx.process_registered_region_obligations( - &self.region_bound_pairs, + self.outlives_environment.region_bound_pairs(), self.implicit_region_bound, self.param_env, self.body_id); } - /// This method populates the region map's `free_region_map`. It walks over the transformed - /// argument and return types for each function just before we check the body of that function, - /// looking for types where you have a borrowed pointer to other borrowed data (e.g., `&'a &'b - /// [usize]`. We do not allow references to outlive the things they point at, so we can assume - /// that `'a <= 'b`. This holds for both the argument and return types, basically because, on - /// the caller side, the caller is responsible for checking that the type of every expression - /// (including the actual values for the arguments, as well as the return type of the fn call) - /// is well-formed. - /// - /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` - fn relate_free_regions(&mut self, - fn_sig_tys: &[Ty<'tcx>], - body_id: ast::NodeId, - span: Span) { - debug!("relate_free_regions >>"); - - for &ty in fn_sig_tys { - let ty = self.resolve_type(ty); - debug!("relate_free_regions(t={:?})", ty); - let implied_bounds = self.implied_bounds(body_id, ty, span); - - // But also record other relationships, such as `T:'x`, - // that don't go into the free-region-map but which we use - // here. - for implication in implied_bounds { - debug!("implication: {:?}", implication); - match implication { - ImpliedBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), - &ty::ReVar(vid_b)) | - ImpliedBound::RegionSubRegion(r_a @ &ty::ReFree(_), - &ty::ReVar(vid_b)) => { - self.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.) - if body_id == self.fcx.body_id { - // Only modify `free_region_map` if these - // are parameters from the root - // function. That's because this data - // struture is shared across all functions - // and hence we don't want to take implied - // bounds from one closure and use them - // outside. - self.free_region_map.relate_regions(r_a, r_b); - } - } - } - } - } - - debug!("<< relate_free_regions"); - } - - /// Compute the implied bounds that a callee/impl can assume based on - /// the fact that caller/projector has ensured that `ty` is WF. See - /// the `ImpliedBound` type for more details. - fn implied_bounds(&mut self, body_id: ast::NodeId, ty: Ty<'tcx>, span: Span) - -> Vec> { - // Sometimes when we ask what it takes for T: WF, we get back that - // U: WF is required; in that case, we push U onto this stack and - // process it next. Currently (at least) these resulting - // predicates are always guaranteed to be a subset of the original - // type, so we need not fear non-termination. - let mut wf_types = vec![ty]; - - let mut implied_bounds = vec![]; - - while let Some(ty) = wf_types.pop() { - // Compute the obligations for `ty` to be well-formed. If `ty` is - // an unresolved inference variable, just substituted an empty set - // -- because the return type here is going to be things we *add* - // to the environment, it's always ok for this set to be smaller - // than the ultimate set. (Note: normally there won't be - // unresolved inference variables here anyway, but there might be - // during typeck under some circumstances.) - let obligations = - wf::obligations(self, self.fcx.param_env, body_id, ty, span) - .unwrap_or(vec![]); - - // 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. - self.fcx.register_predicates( - 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 self.tcx.no_late_bound_regions(data) { - None => - vec![], - Some(ty::OutlivesPredicate(r_a, r_b)) => - vec![ImpliedBound::RegionSubRegion(r_b, r_a)], - }, - - ty::Predicate::TypeOutlives(ref data) => - match self.tcx.no_late_bound_regions(data) { - None => vec![], - Some(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = self.resolve_type_vars_if_possible(&ty_a); - let components = self.tcx.outlives_components(ty_a); - self.implied_bounds_from_components(r_b, components) - } - }, - }})); - } - - implied_bounds - } - - /// When we have an implied bound that `T: 'a`, we can further break - /// this down to determine what relationships would have to hold for - /// `T: 'a` to hold. We get to assume that the caller has validated - /// those relationships. - fn implied_bounds_from_components(&self, - sub_region: ty::Region<'tcx>, - sup_components: Vec>) - -> 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![], - } - }) - .collect() - } - fn resolve_regions_and_report_errors(&self) { self.fcx.resolve_regions_and_report_errors(self.subject_def_id, &self.region_scope_tree, - &self.free_region_map); + self.outlives_environment.free_region_map()); } fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { @@ -632,10 +419,28 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { NestedVisitorMap::None } - fn visit_fn(&mut self, _fk: intravisit::FnKind<'gcx>, _: &'gcx hir::FnDecl, - b: hir::BodyId, span: Span, id: ast::NodeId) { - let body = self.tcx.hir.body(b); - self.visit_fn_body(id, body, span) + fn visit_fn(&mut self, + fk: intravisit::FnKind<'gcx>, + _: &'gcx hir::FnDecl, + body_id: hir::BodyId, + span: Span, + id: ast::NodeId) { + assert!(match fk { intravisit::FnKind::Closure(..) => true, _ => false }, + "visit_fn invoked for something other than a closure"); + + // Save state of current function before invoking + // `visit_fn_body`. We will restore afterwards. + let outlives_environment = self.outlives_environment.clone(); + let old_body_id = self.body_id; + let old_call_site_scope = self.call_site_scope; + + let body = self.tcx.hir.body(body_id); + self.visit_fn_body(id, body, span); + + // Restore state from previous function. + self.call_site_scope = old_call_site_scope; + self.body_id = old_body_id; + self.outlives_environment = outlives_environment; } //visit_pat: visit_pat, // (..) see above @@ -1144,7 +949,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ty: Ty<'tcx>, region: ty::Region<'tcx>) { - self.infcx.type_must_outlive(&self.region_bound_pairs, + self.infcx.type_must_outlive(self.outlives_environment.region_bound_pairs(), self.implicit_region_bound, self.param_env, origin, diff --git a/src/librustc_typeck/check/regionck_implied_bounds.rs b/src/librustc_typeck/check/regionck_implied_bounds.rs new file mode 100644 index 0000000000000..f84e0dd880fcc --- /dev/null +++ b/src/librustc_typeck/check/regionck_implied_bounds.rs @@ -0,0 +1,280 @@ +use middle::free_region::FreeRegionMap; +use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::infer::{InferCtxt, GenericKind}; +use rustc::traits::FulfillmentContext; +use rustc::ty::outlives::Component; +use rustc::ty::wf; + +use syntax::ast; +use syntax_pos::Span; + +#[derive(Clone)] +pub struct OutlivesEnvironment<'tcx> { + param_env: ty::ParamEnv<'tcx>, + free_region_map: FreeRegionMap<'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 { + param_env, + free_region_map, + region_bound_pairs: vec![], + } + } + + /// Borrows current value of the `free_region_map`. + pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { + &self.free_region_map + } + + /// Borrows current value of the `region_bound_pairs`. + pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] { + &self.region_bound_pairs + } + + /// Returns ownership of the `free_region_map`. + pub fn into_free_region_map(self) -> FreeRegionMap<'tcx> { + self.free_region_map + } + + /// This method adds "implied bounds" into the outlives environment. + /// Implied bounds are outlives relationships that we can deduce + /// on the basis that certain types must be well-formed -- these are + /// either the types that appear in the function signature or else + /// the input types to an impl. For example, if you have a function + /// like + /// + /// ``` + /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } + /// ``` + /// + /// we can assume in the caller's body that `'b: 'a` and that `T: + /// 'b` (and hence, transitively, that `T: 'a`). This method would + /// add those assumptions into the outlives-environment. + /// + /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + pub fn add_implied_bounds( + &mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + fn_sig_tys: &[Ty<'tcx>], + body_id: ast::NodeId, + span: Span, + ) { + debug!("add_implied_bounds()"); + + 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); + } + } + } + } + } + + /// Compute the implied bounds that a callee/impl can assume based on + /// the fact that caller/projector has ensured that `ty` is WF. See + /// the `ImpliedBound` type for more details. + fn implied_bounds( + &mut self, + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + ty: Ty<'tcx>, + span: Span, + ) -> Vec> { + 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) + } + } + } + } + })); + } + + // 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![], + } + }) + .collect() + } +} From f1d95ed559927f5d06672916a2013fa0e95fc7b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 07:23:21 -0400 Subject: [PATCH 11/68] rename mod `region_obligations` to `outlives::obligations` --- src/librustc/infer/mod.rs | 2 +- src/librustc/infer/outlives/mod.rs | 1 + .../infer/{region_obligations.rs => outlives/obligations.rs} | 0 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 src/librustc/infer/outlives/mod.rs rename src/librustc/infer/{region_obligations.rs => outlives/obligations.rs} (100%) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 41e1bf303b384..e3db08c0adff1 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -55,7 +55,7 @@ mod higher_ranked; pub mod lattice; mod lub; pub mod region_inference; -mod region_obligations; +mod outlives; pub mod resolve; mod freshen; mod sub; diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs new file mode 100644 index 0000000000000..321c4b87fb30f --- /dev/null +++ b/src/librustc/infer/outlives/mod.rs @@ -0,0 +1 @@ +mod obligations; diff --git a/src/librustc/infer/region_obligations.rs b/src/librustc/infer/outlives/obligations.rs similarity index 100% rename from src/librustc/infer/region_obligations.rs rename to src/librustc/infer/outlives/obligations.rs From dc93c9d8dc80c1ece515c9035e0ce54c0ed9cf23 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 07:36:00 -0400 Subject: [PATCH 12/68] move the `OutlivesEnvironment` into `infer` so that `nll` can use it Unquestionably there is more cleanup to be done, but I'm not sure what it should look like yet, so leaving it roughly as is. --- src/librustc/infer/mod.rs | 2 + .../infer/outlives/env.rs} | 39 ++++++++++++++++--- src/librustc/infer/outlives/mod.rs | 1 + src/librustc/infer/outlives/obligations.rs | 10 +++++ src/librustc_typeck/check/mod.rs | 1 - src/librustc_typeck/check/regionck.rs | 4 +- 6 files changed, 48 insertions(+), 9 deletions(-) rename src/{librustc_typeck/check/regionck_implied_bounds.rs => librustc/infer/outlives/env.rs} (87%) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index e3db08c0adff1..e73d74c22ba7d 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -62,6 +62,8 @@ 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, diff --git a/src/librustc_typeck/check/regionck_implied_bounds.rs b/src/librustc/infer/outlives/env.rs similarity index 87% rename from src/librustc_typeck/check/regionck_implied_bounds.rs rename to src/librustc/infer/outlives/env.rs index f84e0dd880fcc..ef09479f75131 100644 --- a/src/librustc_typeck/check/regionck_implied_bounds.rs +++ b/src/librustc/infer/outlives/env.rs @@ -1,13 +1,42 @@ +// 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 middle::free_region::FreeRegionMap; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::infer::{InferCtxt, GenericKind}; -use rustc::traits::FulfillmentContext; -use rustc::ty::outlives::Component; -use rustc::ty::wf; +use infer::{InferCtxt, GenericKind}; +use traits::FulfillmentContext; +use ty::{self, Ty, TypeFoldable}; +use ty::outlives::Component; +use ty::wf; use syntax::ast; use syntax_pos::Span; +/// The `OutlivesEnvironment` collects information about what outlives +/// what in a given type-checking setting. For example, if we have a +/// where-clause like `where T: 'a` in scope, then the +/// `OutlivesEnvironment` would record that (in its +/// `region_bound_pairs` field). Similarly, it contains methods for +/// processing and adding implied bounds into the outlives +/// environment. +/// +/// Other code at present does not typically take a +/// `&OutlivesEnvironment`, but rather takes some of its fields (e.g., +/// `process_registered_region_obligations` wants the +/// region-bound-pairs). There is no mistaking it: the current setup +/// of tracking region information is quite scattered! The +/// `OutlivesEnvironment`, for example, needs to sometimes be combined +/// with the `middle::RegionRelations`, to yield a full picture of how +/// (lexical) lifetimes interact. However, I'm reluctant to do more +/// refactoring here, since the setup with NLL is quite different. +/// For example, NLL has no need of `RegionRelations`, and is solely +/// interested in the `OutlivesEnvironment`. -nmatsakis #[derive(Clone)] pub struct OutlivesEnvironment<'tcx> { param_env: ty::ParamEnv<'tcx>, diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index 321c4b87fb30f..ae2fb5e2580e0 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -1 +1,2 @@ +pub mod env; mod obligations; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 05e14daa2813f..2fb085bc1d86f 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -1,3 +1,13 @@ +// 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. + //! Code that handles "type-outlives" constraints like `T: 'a`. This //! is based on the `outlives_components` function defined on the tcx, //! but it adds a bit of heuristics on top, in particular to deal with diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7d98a1c9246b6..c8b2032a49871 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -137,7 +137,6 @@ pub mod dropck; pub mod _match; pub mod writeback; mod regionck; -mod regionck_implied_bounds; pub mod coercion; pub mod demand; pub mod method; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 5954a0f2ecdce..932cb12e81dfb 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -90,7 +90,7 @@ use middle::region; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; -use rustc::infer; +use rustc::infer::{self, OutlivesEnvironment}; use rustc::ty::adjustment; use std::mem; @@ -101,8 +101,6 @@ use syntax_pos::Span; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::{self, PatKind}; -use super::regionck_implied_bounds::OutlivesEnvironment; - // a variation on try that just returns unit macro_rules! ignore_err { ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () }) From 23f80c9ecde295e055467849196448e0f8a0a7d5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 09:08:11 -0400 Subject: [PATCH 13/68] thread location info through mir typeck (but do not use) --- src/librustc/mir/mod.rs | 36 ++++++- src/librustc_mir/transform/type_check.rs | 121 ++++++++++++++--------- 2 files changed, 107 insertions(+), 50 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 2fc27de137faa..4aec6764223b0 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -555,6 +555,15 @@ pub struct UpvarDecl { newtype_index!(BasicBlock { DEBUG_FORMAT = "bb{}" }); +impl BasicBlock { + pub fn start_location(self) -> Location { + Location { + block: self, + statement_index: 0, + } + } +} + /////////////////////////////////////////////////////////////////////////// // BasicBlockData and Terminator @@ -638,7 +647,32 @@ pub enum TerminatorKind<'tcx> { unwind: Option }, - /// Drop the Lvalue and assign the new value over it + /// Drop the Lvalue and assign the new value over it. This ensures + /// that the assignment to LV occurs *even if* the destructor for + /// lvalue unwinds. Its semantics are best explained by by the + /// elaboration: + /// + /// ``` + /// BB0 { + /// DropAndReplace(LV <- RV, goto BB1, unwind BB2) + /// } + /// ``` + /// + /// becomes + /// + /// ``` + /// BB0 { + /// Drop(LV, goto BB1, unwind BB2) + /// } + /// BB1 { + /// // LV is now unitialized + /// LV <- RV + /// } + /// BB2 { + /// // LV is now unitialized -- its dtor panicked + /// LV <- RV + /// } + /// ``` DropAndReplace { location: Lvalue<'tcx>, value: Operand<'tcx>, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index b07e818ee8752..9fcde82c87692 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -139,8 +139,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { Lvalue::Static(box Static { def_id, ty: sty }) => { let sty = self.sanitize_type(lvalue, sty); let ty = self.tcx().type_of(def_id); - let ty = self.cx.normalize(&ty); - if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty) { + let ty = self.cx.normalize(&ty, location); + if let Err(terr) = self.cx.eq_types(self.last_span, ty, sty, location) { span_mirbug!( self, lvalue, "bad static type ({:?}: {:?}): {:?}", ty, sty, terr); @@ -165,7 +165,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { base: LvalueTy<'tcx>, pi: &LvalueElem<'tcx>, lvalue: &Lvalue<'tcx>, - _: Location) + location: Location) -> LvalueTy<'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); let tcx = self.tcx(); @@ -254,9 +254,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(lvalue, fty); - match self.field_ty(lvalue, base, field) { + match self.field_ty(lvalue, base, field, location) { Ok(ty) => { - if let Err(terr) = self.cx.eq_types(span, ty, fty) { + if let Err(terr) = self.cx.eq_types(span, ty, fty, location) { span_mirbug!( self, lvalue, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); @@ -281,7 +281,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn field_ty(&mut self, parent: &fmt::Debug, base_ty: LvalueTy<'tcx>, - field: Field) + field: Field, + location: Location) -> Result, FieldAccessError> { let tcx = self.tcx(); @@ -329,7 +330,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { }; if let Some(field) = variant.fields.get(field.index()) { - Ok(self.cx.normalize(&field.ty(tcx, substs))) + Ok(self.cx.normalize(&field.ty(tcx, substs), location)) } else { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) } @@ -371,7 +372,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infer_ok.value } - fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>) + fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, _at_location: Location) -> infer::UnitResult<'tcx> { self.infcx.at(&self.misc(self.last_span), self.param_env) @@ -379,7 +380,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { .map(|ok| self.register_infer_ok_obligations(ok)) } - fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>) + fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>, _at_location: Location) -> infer::UnitResult<'tcx> { self.infcx.at(&self.misc(span), self.param_env) @@ -391,14 +392,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.infcx.tcx } - fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) { + fn check_stmt(&mut self, + mir: &Mir<'tcx>, + stmt: &Statement<'tcx>, + location: Location) { debug!("check_stmt: {:?}", stmt); let tcx = self.tcx(); match stmt.kind { StatementKind::Assign(ref lv, ref rv) => { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { + if let Err(terr) = self.sub_types(rv_ty, lv_ty, location.successor_within_block()) { span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", lv_ty, rv_ty, terr); } @@ -432,7 +436,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn check_terminator(&mut self, mir: &Mir<'tcx>, - term: &Terminator<'tcx>) { + term: &Terminator<'tcx>, + location: Location) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { @@ -450,18 +455,30 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { TerminatorKind::DropAndReplace { ref location, ref value, - .. + target, + unwind, } => { let lv_ty = location.ty(mir, tcx).to_ty(tcx); let rv_ty = value.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { + + if let Err(terr) = self.sub_types(rv_ty, lv_ty, target.start_location()) { span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", lv_ty, rv_ty, terr); } + + // Subtle: this assignment occurs at the start of + // *both* blocks, so we need to ensure that it holds + // at both locations. + if let Some(unwind) = unwind { + if let Err(terr) = self.sub_types(rv_ty, lv_ty, unwind.start_location()) { + span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", + lv_ty, rv_ty, terr); + } + } } TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { let discr_ty = discr.ty(mir, tcx); - if let Err(terr) = self.sub_types(discr_ty, switch_ty) { + if let Err(terr) = self.sub_types(discr_ty, switch_ty, location) { span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", switch_ty, discr_ty, terr); } @@ -483,13 +500,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } }; let sig = tcx.erase_late_bound_regions(&sig); - let sig = self.normalize(&sig); + let sig = self.normalize(&sig, location); self.check_call_dest(mir, term, &sig, destination); if self.is_box_free(func) { - self.check_box_free_inputs(mir, term, &sig, args); + self.check_box_free_inputs(mir, term, &sig, args, location); } else { - self.check_call_inputs(mir, term, &sig, args); + self.check_call_inputs(mir, term, &sig, args, location); } } TerminatorKind::Assert { ref cond, ref msg, .. } => { @@ -512,7 +529,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { match mir.yield_ty { None => span_mirbug!(self, term, "yield in non-generator"), Some(ty) => { - if let Err(terr) = self.sub_types(value_ty, ty) { + if let Err(terr) = self.sub_types(value_ty, ty, location) { span_mirbug!(self, term, "type of yield value is {:?}, but the yield type is {:?}: {:?}", @@ -533,9 +550,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { destination: &Option<(Lvalue<'tcx>, BasicBlock)>) { let tcx = self.tcx(); match *destination { - Some((ref dest, _)) => { + Some((ref dest, target_block)) => { let dest_ty = dest.ty(mir, tcx).to_ty(tcx); - if let Err(terr) = self.sub_types(sig.output(), dest_ty) { + if let Err(terr) = self.sub_types(sig.output(), + dest_ty, + target_block.start_location()) { span_mirbug!(self, term, "call dest mismatch ({:?} <- {:?}): {:?}", dest_ty, sig.output(), terr); @@ -554,7 +573,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { mir: &Mir<'tcx>, term: &Terminator<'tcx>, sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>]) + args: &[Operand<'tcx>], + location: Location) { debug!("check_call_inputs({:?}, {:?})", sig, args); if args.len() < sig.inputs().len() || @@ -563,7 +583,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(mir, self.tcx()); - if let Err(terr) = self.sub_types(op_arg_ty, fn_arg) { + if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, location) { span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}", n, fn_arg, op_arg_ty, terr); } @@ -587,7 +607,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { mir: &Mir<'tcx>, term: &Terminator<'tcx>, sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>]) + args: &[Operand<'tcx>], + location: Location) { debug!("check_box_free_inputs"); @@ -621,69 +642,69 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } }; - if let Err(terr) = self.sub_types(arg_ty, pointee_ty) { + if let Err(terr) = self.sub_types(arg_ty, pointee_ty, location) { span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}", pointee_ty, arg_ty, terr); } } - fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>) + fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) { - let is_cleanup = block.is_cleanup; - self.last_span = block.terminator().source_info.span; - match block.terminator().kind { + let is_cleanup = block_data.is_cleanup; + self.last_span = block_data.terminator().source_info.span; + match block_data.terminator().kind { TerminatorKind::Goto { target } => - self.assert_iscleanup(mir, block, target, is_cleanup), + self.assert_iscleanup(mir, block_data, target, is_cleanup), TerminatorKind::SwitchInt { ref targets, .. } => { for target in targets { - self.assert_iscleanup(mir, block, *target, is_cleanup); + self.assert_iscleanup(mir, block_data, *target, is_cleanup); } } TerminatorKind::Resume => { if !is_cleanup { - span_mirbug!(self, block, "resume on non-cleanup block!") + span_mirbug!(self, block_data, "resume on non-cleanup block!") } } TerminatorKind::Return => { if is_cleanup { - span_mirbug!(self, block, "return on cleanup block") + span_mirbug!(self, block_data, "return on cleanup block") } } TerminatorKind::GeneratorDrop { .. } => { if is_cleanup { - span_mirbug!(self, block, "generator_drop in cleanup block") + span_mirbug!(self, block_data, "generator_drop in cleanup block") } } TerminatorKind::Yield { resume, drop, .. } => { if is_cleanup { - span_mirbug!(self, block, "yield in cleanup block") + span_mirbug!(self, block_data, "yield in cleanup block") } - self.assert_iscleanup(mir, block, resume, is_cleanup); + self.assert_iscleanup(mir, block_data, resume, is_cleanup); if let Some(drop) = drop { - self.assert_iscleanup(mir, block, drop, is_cleanup); + self.assert_iscleanup(mir, block_data, drop, is_cleanup); } } TerminatorKind::Unreachable => {} TerminatorKind::Drop { target, unwind, .. } | TerminatorKind::DropAndReplace { target, unwind, .. } | TerminatorKind::Assert { target, cleanup: unwind, .. } => { - self.assert_iscleanup(mir, block, target, is_cleanup); + self.assert_iscleanup(mir, block_data, target, is_cleanup); if let Some(unwind) = unwind { if is_cleanup { - span_mirbug!(self, block, "unwind on cleanup block") + span_mirbug!(self, block_data, "unwind on cleanup block") } - self.assert_iscleanup(mir, block, unwind, true); + self.assert_iscleanup(mir, block_data, unwind, true); } } TerminatorKind::Call { ref destination, cleanup, .. } => { if let &Some((_, target)) = destination { - self.assert_iscleanup(mir, block, target, is_cleanup); + self.assert_iscleanup(mir, block_data, target, is_cleanup); } if let Some(cleanup) = cleanup { if is_cleanup { - span_mirbug!(self, block, "cleanup on cleanup block") + span_mirbug!(self, block_data, "cleanup on cleanup block") } - self.assert_iscleanup(mir, block, cleanup, true); + self.assert_iscleanup(mir, block_data, cleanup, true); } } TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => { @@ -744,21 +765,23 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_local(mir, local, local_decl); } - for block in mir.basic_blocks() { - for stmt in &block.statements { + for (block, block_data) in mir.basic_blocks().iter_enumerated() { + let mut location = Location { block, statement_index: 0 }; + for stmt in &block_data.statements { if stmt.source_info.span != DUMMY_SP { self.last_span = stmt.source_info.span; } - self.check_stmt(mir, stmt); + self.check_stmt(mir, stmt, location); + location.statement_index += 1; } - self.check_terminator(mir, block.terminator()); - self.check_iscleanup(mir, block); + self.check_terminator(mir, block_data.terminator(), location); + self.check_iscleanup(mir, block_data); } } - fn normalize(&mut self, value: &T) -> T + fn normalize(&mut self, value: &T, _location: Location) -> T where T: fmt::Debug + TypeFoldable<'tcx> { let mut selcx = traits::SelectionContext::new(self.infcx); From bdfaff57659f286ef47233bb0b02909993a7c8c4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 17:56:27 -0400 Subject: [PATCH 14/68] apply rustfmt to `type_check` --- src/librustc_mir/transform/type_check.rs | 615 +++++++++++++---------- 1 file changed, 360 insertions(+), 255 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 9fcde82c87692..e10f7a59b5417 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -51,7 +51,7 @@ macro_rules! span_mirbug_and_err { } enum FieldAccessError { - OutOfRange { field_count: usize } + OutOfRange { field_count: usize }, } /// Verifies that MIR types are sane to not crash further checks. @@ -59,12 +59,12 @@ enum FieldAccessError { /// The sanitize_XYZ methods here take an MIR object and compute its /// type, calling `span_mirbug` and returning an error type if there /// is a problem. -struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> { +struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b + 'tcx, 'tcx: 'b> { cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, last_span: Span, body_id: ast::NodeId, - errors_reported: bool + errors_reported: bool, } impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { @@ -74,10 +74,12 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn visit_lvalue(&mut self, - lvalue: &Lvalue<'tcx>, - _context: visit::LvalueContext, - location: Location) { + fn visit_lvalue( + &mut self, + lvalue: &Lvalue<'tcx>, + _context: visit::LvalueContext, + location: Location, + ) { self.sanitize_lvalue(lvalue, location); } @@ -116,7 +118,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { body_id: cx.body_id, cx, last_span: mir.span, - errors_reported: false + errors_reported: false, } } @@ -135,25 +137,33 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>, location: Location) -> LvalueTy<'tcx> { debug!("sanitize_lvalue: {:?}", lvalue); match *lvalue { - Lvalue::Local(index) => LvalueTy::Ty { ty: self.mir.local_decls[index].ty }, + Lvalue::Local(index) => LvalueTy::Ty { + ty: self.mir.local_decls[index].ty, + }, Lvalue::Static(box Static { def_id, ty: sty }) => { let sty = self.sanitize_type(lvalue, 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) { span_mirbug!( - self, lvalue, "bad static type ({:?}: {:?}): {:?}", - ty, sty, terr); + self, + lvalue, + "bad static type ({:?}: {:?}): {:?}", + ty, + sty, + terr + ); } LvalueTy::Ty { ty: sty } - - }, + } Lvalue::Projection(ref proj) => { let base_ty = self.sanitize_lvalue(&proj.base, location); if let LvalueTy::Ty { ty } = base_ty { if ty.references_error() { assert!(self.errors_reported); - return LvalueTy::Ty { ty: self.tcx().types.err }; + return LvalueTy::Ty { + ty: self.tcx().types.err, + }; } } self.sanitize_projection(base_ty, &proj.elem, lvalue, location) @@ -161,12 +171,13 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } - fn sanitize_projection(&mut self, - base: LvalueTy<'tcx>, - pi: &LvalueElem<'tcx>, - lvalue: &Lvalue<'tcx>, - location: Location) - -> LvalueTy<'tcx> { + fn sanitize_projection( + &mut self, + base: LvalueTy<'tcx>, + pi: &LvalueElem<'tcx>, + lvalue: &Lvalue<'tcx>, + location: Location, + ) -> LvalueTy<'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); @@ -176,23 +187,21 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); LvalueTy::Ty { ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "deref of non-pointer {:?}", base_ty) - }) + span_mirbug_and_err!(self, lvalue, "deref of non-pointer {:?}", base_ty) + }), } } ProjectionElem::Index(i) => { let index_ty = Lvalue::Local(i).ty(self.mir, tcx).to_ty(tcx); if index_ty != tcx.types.usize { LvalueTy::Ty { - ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i) + ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i), } } else { LvalueTy::Ty { ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "index of non-array {:?}", base_ty) - }) + span_mirbug_and_err!(self, lvalue, "index of non-array {:?}", base_ty) + }), } } } @@ -200,73 +209,80 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { // consider verifying in-bounds LvalueTy::Ty { ty: base_ty.builtin_index().unwrap_or_else(|| { - span_mirbug_and_err!( - self, lvalue, "index of non-array {:?}", base_ty) - }) + span_mirbug_and_err!(self, lvalue, "index of non-array {:?}", base_ty) + }), } } - ProjectionElem::Subslice { from, to } => { - LvalueTy::Ty { - ty: match base_ty.sty { - ty::TyArray(inner, size) => { - let size = size.val.to_const_int().unwrap().to_u64().unwrap(); - let min_size = (from as u64) + (to as u64); - if let Some(rest_size) = size.checked_sub(min_size) { - tcx.mk_array(inner, rest_size) - } else { - span_mirbug_and_err!( - self, lvalue, "taking too-small slice of {:?}", base_ty) - } - } - ty::TySlice(..) => base_ty, - _ => { + ProjectionElem::Subslice { from, to } => LvalueTy::Ty { + ty: match base_ty.sty { + ty::TyArray(inner, size) => { + let size = size.val.to_const_int().unwrap().to_u64().unwrap(); + let min_size = (from as u64) + (to as u64); + if let Some(rest_size) = size.checked_sub(min_size) { + tcx.mk_array(inner, rest_size) + } else { span_mirbug_and_err!( - self, lvalue, "slice of non-array {:?}", base_ty) + self, + lvalue, + "taking too-small slice of {:?}", + base_ty + ) } } - } - } - ProjectionElem::Downcast(adt_def1, index) => - match base_ty.sty { - ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { - if index >= adt_def.variants.len() { - LvalueTy::Ty { - ty: span_mirbug_and_err!( - self, - lvalue, - "cast to variant #{:?} but enum only has {:?}", - index, - adt_def.variants.len()) - } - } else { - LvalueTy::Downcast { - adt_def, - substs, - variant_index: index - } + ty::TySlice(..) => base_ty, + _ => span_mirbug_and_err!(self, lvalue, "slice of non-array {:?}", base_ty), + }, + }, + ProjectionElem::Downcast(adt_def1, index) => match base_ty.sty { + ty::TyAdt(adt_def, substs) if adt_def.is_enum() && adt_def == adt_def1 => { + if index >= adt_def.variants.len() { + LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, + lvalue, + "cast to variant #{:?} but enum only has {:?}", + index, + adt_def.variants.len() + ), + } + } else { + LvalueTy::Downcast { + adt_def, + substs, + variant_index: index, } } - _ => LvalueTy::Ty { - ty: span_mirbug_and_err!( - self, lvalue, "can't downcast {:?} as {:?}", - base_ty, adt_def1) - } + } + _ => LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, + lvalue, + "can't downcast {:?} as {:?}", + base_ty, + adt_def1 + ), }, + }, ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(lvalue, fty); match self.field_ty(lvalue, base, field, location) { - Ok(ty) => { - if let Err(terr) = self.cx.eq_types(span, ty, fty, location) { - span_mirbug!( - self, lvalue, "bad field access ({:?}: {:?}): {:?}", - ty, fty, terr); - } - } - Err(FieldAccessError::OutOfRange { field_count }) => { + Ok(ty) => if let Err(terr) = self.cx.eq_types(span, ty, fty, location) { span_mirbug!( - self, lvalue, "accessed field #{} but variant only has {}", - field.index(), field_count) - } + self, + lvalue, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); + }, + Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( + self, + lvalue, + "accessed field #{} but variant only has {}", + field.index(), + field_count + ), } LvalueTy::Ty { ty: fty } } @@ -278,29 +294,31 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { self.tcx().types.err } - fn field_ty(&mut self, - parent: &fmt::Debug, - base_ty: LvalueTy<'tcx>, - field: Field, - location: Location) - -> Result, FieldAccessError> - { + fn field_ty( + &mut self, + parent: &fmt::Debug, + base_ty: LvalueTy<'tcx>, + field: Field, + location: Location, + ) -> Result, FieldAccessError> { let tcx = self.tcx(); let (variant, substs) = match base_ty { - LvalueTy::Downcast { adt_def, substs, variant_index } => { - (&adt_def.variants[variant_index], substs) - } + LvalueTy::Downcast { + adt_def, + substs, + variant_index, + } => (&adt_def.variants[variant_index], substs), LvalueTy::Ty { ty } => match ty.sty { ty::TyAdt(adt_def, substs) if adt_def.is_univariant() => { - (&adt_def.variants[0], substs) - } + (&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), None => Err(FieldAccessError::OutOfRange { - field_count: substs.upvar_tys(def_id, tcx).count() - }) + field_count: substs.upvar_tys(def_id, tcx).count(), + }), } } ty::TyGenerator(def_id, substs, _) => { @@ -312,32 +330,40 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { return match substs.field_tys(def_id, tcx).nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.field_tys(def_id, tcx).count() + 1 - }) - } + field_count: substs.field_tys(def_id, tcx).count() + 1, + }), + }; } ty::TyTuple(tys, _) => { return match tys.get(field.index()) { Some(&ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: tys.len() - }) + field_count: tys.len(), + }), } } - _ => return Ok(span_mirbug_and_err!( - self, parent, "can't project out of {:?}", base_ty)) - } + _ => { + return Ok(span_mirbug_and_err!( + self, + parent, + "can't project out of {:?}", + base_ty + )) + } + }, }; if let Some(field) = variant.fields.get(field.index()) { Ok(self.cx.normalize(&field.ty(tcx, substs), location)) } else { - Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + Err(FieldAccessError::OutOfRange { + field_count: variant.fields.len(), + }) } } } -pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, fulfillment_cx: traits::FulfillmentContext<'tcx>, @@ -347,10 +373,11 @@ pub struct TypeChecker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { } impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { - fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'gcx>) - -> Self { + fn new( + infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + ) -> Self { TypeChecker { infcx, fulfillment_cx: traits::FulfillmentContext::new(), @@ -367,35 +394,42 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { pub fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'tcx, T>) -> T { for obligation in infer_ok.obligations { - self.fulfillment_cx.register_predicate_obligation(self.infcx, obligation); + self.fulfillment_cx + .register_predicate_obligation(self.infcx, obligation); } infer_ok.value } - fn sub_types(&mut self, sub: Ty<'tcx>, sup: Ty<'tcx>, _at_location: Location) - -> infer::UnitResult<'tcx> - { - self.infcx.at(&self.misc(self.last_span), self.param_env) - .sup(sup, sub) - .map(|ok| self.register_infer_ok_obligations(ok)) + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + _at_location: Location, + ) -> infer::UnitResult<'tcx> { + self.infcx + .at(&self.misc(self.last_span), self.param_env) + .sup(sup, sub) + .map(|ok| self.register_infer_ok_obligations(ok)) } - fn eq_types(&mut self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>, _at_location: Location) - -> infer::UnitResult<'tcx> - { - self.infcx.at(&self.misc(span), self.param_env) - .eq(b, a) - .map(|ok| self.register_infer_ok_obligations(ok)) + fn eq_types( + &mut self, + span: Span, + a: Ty<'tcx>, + b: Ty<'tcx>, + _at_location: Location, + ) -> infer::UnitResult<'tcx> { + self.infcx + .at(&self.misc(span), self.param_env) + .eq(b, a) + .map(|ok| self.register_infer_ok_obligations(ok)) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.infcx.tcx } - fn check_stmt(&mut self, - mir: &Mir<'tcx>, - stmt: &Statement<'tcx>, - location: Location) { + fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>, location: Location) { debug!("check_stmt: {:?}", stmt); let tcx = self.tcx(); match stmt.kind { @@ -403,26 +437,39 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); if let Err(terr) = self.sub_types(rv_ty, lv_ty, location.successor_within_block()) { - span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); - } - } - StatementKind::SetDiscriminant{ ref lvalue, variant_index } => { + span_mirbug!( + self, + stmt, + "bad assignment ({:?} = {:?}): {:?}", + lv_ty, + rv_ty, + terr + ); + } + } + StatementKind::SetDiscriminant { + ref lvalue, + variant_index, + } => { let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx); let adt = match lvalue_type.sty { TypeVariants::TyAdt(adt, _) if adt.is_enum() => adt, _ => { - span_bug!(stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): lhs is not an enum", - lvalue, - variant_index); + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): lhs is not an enum", + lvalue, + variant_index + ); } }; if variant_index >= adt.variants.len() { - span_bug!(stmt.source_info.span, - "bad set discriminant ({:?} = {:?}): value of of range", - lvalue, - variant_index); + span_bug!( + stmt.source_info.span, + "bad set discriminant ({:?} = {:?}): value of of range", + lvalue, + variant_index + ); }; } StatementKind::StorageLive(_) | @@ -434,10 +481,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_terminator(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - location: Location) { + fn check_terminator(&mut self, mir: &Mir<'tcx>, term: &Terminator<'tcx>, location: Location) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { @@ -451,7 +495,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // no checks needed for these } - TerminatorKind::DropAndReplace { ref location, ref value, @@ -462,8 +505,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let rv_ty = value.ty(mir, tcx); if let Err(terr) = self.sub_types(rv_ty, lv_ty, target.start_location()) { - span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + lv_ty, + rv_ty, + terr + ); } // Subtle: this assignment occurs at the start of @@ -471,25 +520,44 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // at both locations. if let Some(unwind) = unwind { if let Err(terr) = self.sub_types(rv_ty, lv_ty, unwind.start_location()) { - span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); + span_mirbug!( + self, + term, + "bad DropAndReplace ({:?} = {:?}): {:?}", + lv_ty, + rv_ty, + terr + ); } } } - TerminatorKind::SwitchInt { ref discr, switch_ty, .. } => { + TerminatorKind::SwitchInt { + ref discr, + switch_ty, + .. + } => { let discr_ty = discr.ty(mir, tcx); if let Err(terr) = self.sub_types(discr_ty, switch_ty, location) { - span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", - switch_ty, discr_ty, terr); + span_mirbug!( + self, + term, + "bad SwitchInt ({:?} on {:?}): {:?}", + switch_ty, + discr_ty, + terr + ); } - if !switch_ty.is_integral() && !switch_ty.is_char() && - !switch_ty.is_bool() - { - span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty); + if !switch_ty.is_integral() && !switch_ty.is_char() && !switch_ty.is_bool() { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}", switch_ty); } // FIXME: check the values } - TerminatorKind::Call { ref func, ref args, ref destination, .. } => { + TerminatorKind::Call { + ref func, + ref args, + ref destination, + .. + } => { let func_ty = func.ty(mir, tcx); debug!("check_terminator: call, func_ty={:?}", func_ty); let sig = match func_ty.sty { @@ -509,7 +577,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_call_inputs(mir, term, &sig, args, location); } } - TerminatorKind::Assert { ref cond, ref msg, .. } => { + TerminatorKind::Assert { + ref cond, ref msg, .. + } => { let cond_ty = cond.ty(mir, tcx); if cond_ty != tcx.types.bool { span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); @@ -528,64 +598,78 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let value_ty = value.ty(mir, tcx); match mir.yield_ty { None => span_mirbug!(self, term, "yield in non-generator"), - Some(ty) => { - if let Err(terr) = self.sub_types(value_ty, ty, location) { - span_mirbug!(self, - term, - "type of yield value is {:?}, but the yield type is {:?}: {:?}", - value_ty, - ty, - terr); - } - } + Some(ty) => if let Err(terr) = self.sub_types(value_ty, ty, location) { + span_mirbug!( + self, + term, + "type of yield value is {:?}, but the yield type is {:?}: {:?}", + value_ty, + ty, + terr + ); + }, } } } } - fn check_call_dest(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - destination: &Option<(Lvalue<'tcx>, BasicBlock)>) { + fn check_call_dest( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + destination: &Option<(Lvalue<'tcx>, BasicBlock)>, + ) { let tcx = self.tcx(); match *destination { Some((ref dest, target_block)) => { let dest_ty = dest.ty(mir, tcx).to_ty(tcx); - if let Err(terr) = self.sub_types(sig.output(), - dest_ty, - target_block.start_location()) { - span_mirbug!(self, term, - "call dest mismatch ({:?} <- {:?}): {:?}", - dest_ty, sig.output(), terr); + if let Err(terr) = + self.sub_types(sig.output(), dest_ty, target_block.start_location()) + { + span_mirbug!( + self, + term, + "call dest mismatch ({:?} <- {:?}): {:?}", + dest_ty, + sig.output(), + terr + ); } - }, + } None => { // FIXME(canndrew): This is_never should probably be an is_uninhabited if !sig.output().is_never() { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } - }, + } } } - fn check_call_inputs(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>], - location: Location) - { + fn check_call_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + location: Location, + ) { debug!("check_call_inputs({:?}, {:?})", sig, args); - if args.len() < sig.inputs().len() || - (args.len() > sig.inputs().len() && !sig.variadic) { + if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) { span_mirbug!(self, term, "call to {:?} with wrong # of args", sig); } for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(mir, self.tcx()); if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, location) { - span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}", - n, fn_arg, op_arg_ty, terr); + span_mirbug!( + self, + term, + "bad arg #{:?} ({:?} <- {:?}): {:?}", + n, + fn_arg, + op_arg_ty, + terr + ); } } } @@ -593,23 +677,29 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn is_box_free(&self, operand: &Operand<'tcx>) -> bool { match operand { &Operand::Constant(box Constant { - literal: Literal::Value { - value: &ty::Const { val: ConstVal::Function(def_id, _), .. }, .. - }, .. - }) => { - Some(def_id) == self.tcx().lang_items().box_free_fn() - } + literal: + Literal::Value { + value: + &ty::Const { + val: ConstVal::Function(def_id, _), + .. + }, + .. + }, + .. + }) => Some(def_id) == self.tcx().lang_items().box_free_fn(), _ => false, } } - fn check_box_free_inputs(&mut self, - mir: &Mir<'tcx>, - term: &Terminator<'tcx>, - sig: &ty::FnSig<'tcx>, - args: &[Operand<'tcx>], - location: Location) - { + fn check_box_free_inputs( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + sig: &ty::FnSig<'tcx>, + args: &[Operand<'tcx>], + location: Location, + ) { debug!("check_box_free_inputs"); // box_free takes a Box as a pointer. Allow for that. @@ -643,38 +733,36 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }; if let Err(terr) = self.sub_types(arg_ty, pointee_ty, location) { - span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}", - pointee_ty, arg_ty, terr); + span_mirbug!( + self, + term, + "bad box_free arg ({:?} <- {:?}): {:?}", + pointee_ty, + arg_ty, + terr + ); } } - fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) - { + fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>) { let is_cleanup = block_data.is_cleanup; self.last_span = block_data.terminator().source_info.span; match block_data.terminator().kind { - TerminatorKind::Goto { target } => - self.assert_iscleanup(mir, block_data, target, is_cleanup), - TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { - self.assert_iscleanup(mir, block_data, *target, is_cleanup); - } - } - TerminatorKind::Resume => { - if !is_cleanup { - span_mirbug!(self, block_data, "resume on non-cleanup block!") - } - } - TerminatorKind::Return => { - if is_cleanup { - span_mirbug!(self, block_data, "return on cleanup block") - } - } - TerminatorKind::GeneratorDrop { .. } => { - if is_cleanup { - span_mirbug!(self, block_data, "generator_drop in cleanup block") - } + TerminatorKind::Goto { target } => { + self.assert_iscleanup(mir, block_data, target, is_cleanup) } + TerminatorKind::SwitchInt { ref targets, .. } => for target in targets { + self.assert_iscleanup(mir, block_data, *target, is_cleanup); + }, + TerminatorKind::Resume => if !is_cleanup { + span_mirbug!(self, block_data, "resume on non-cleanup block!") + }, + TerminatorKind::Return => if is_cleanup { + span_mirbug!(self, block_data, "return on cleanup block") + }, + TerminatorKind::GeneratorDrop { .. } => if is_cleanup { + span_mirbug!(self, block_data, "generator_drop in cleanup block") + }, TerminatorKind::Yield { resume, drop, .. } => { if is_cleanup { span_mirbug!(self, block_data, "yield in cleanup block") @@ -687,7 +775,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { TerminatorKind::Unreachable => {} TerminatorKind::Drop { target, unwind, .. } | TerminatorKind::DropAndReplace { target, unwind, .. } | - TerminatorKind::Assert { target, cleanup: unwind, .. } => { + TerminatorKind::Assert { + target, + cleanup: unwind, + .. + } => { self.assert_iscleanup(mir, block_data, target, is_cleanup); if let Some(unwind) = unwind { if is_cleanup { @@ -696,7 +788,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.assert_iscleanup(mir, block_data, unwind, true); } } - TerminatorKind::Call { ref destination, cleanup, .. } => { + TerminatorKind::Call { + ref destination, + cleanup, + .. + } => { if let &Some((_, target)) = destination { self.assert_iscleanup(mir, block_data, target, is_cleanup); } @@ -716,15 +812,21 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn assert_iscleanup(&mut self, - mir: &Mir<'tcx>, - ctxt: &fmt::Debug, - bb: BasicBlock, - iscleanuppad: bool) - { + fn assert_iscleanup( + &mut self, + mir: &Mir<'tcx>, + ctxt: &fmt::Debug, + bb: BasicBlock, + iscleanuppad: bool, + ) { if mir[bb].is_cleanup != iscleanuppad { - span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", - bb, iscleanuppad); + span_mirbug!( + self, + ctxt, + "cleanuppad mismatch: {:?} should be {:?}", + bb, + iscleanuppad + ); } } @@ -737,7 +839,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // // Unbound parts of arguments were never required to be Sized // - maybe we should make that a warning. - return + return; } LocalKind::Var | LocalKind::Temp => {} } @@ -750,9 +852,14 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // slot or local, so to find all unsized rvalues it is enough // to check all temps, return slots and locals. if let None = self.reported_errors.replace((ty, span)) { - span_err!(self.tcx().sess, span, E0161, - "cannot move a value of type {0}: the size of {0} \ - cannot be statically determined", ty); + span_err!( + self.tcx().sess, + span, + E0161, + "cannot move a value of type {0}: the size of {0} \ + cannot be statically determined", + ty + ); } } } @@ -766,7 +873,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } for (block, block_data) in mir.basic_blocks().iter_enumerated() { - let mut location = Location { block, statement_index: 0 }; + let mut location = Location { + block, + statement_index: 0, + }; for stmt in &block_data.statements { if stmt.source_info.span != DUMMY_SP { self.last_span = stmt.source_info.span; @@ -782,16 +892,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn normalize(&mut self, value: &T, _location: Location) -> T - where T: fmt::Debug + TypeFoldable<'tcx> + where + T: fmt::Debug + TypeFoldable<'tcx>, { let mut selcx = traits::SelectionContext::new(self.infcx); let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID); let traits::Normalized { value, obligations } = traits::normalize(&mut selcx, self.param_env, cause, value); - debug!("normalize: value={:?} obligations={:?}", - value, - obligations); + debug!("normalize: value={:?} obligations={:?}", value, obligations); let fulfill_cx = &mut self.fulfillment_cx; for obligation in obligations { @@ -804,8 +913,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn verify_obligations(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) { - span_mirbug!(self, "", "errors selecting obligation: {:?}", - e); + span_mirbug!(self, "", "errors selecting obligation: {:?}", e); } } } @@ -813,10 +921,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { pub struct TypeckMir; impl MirPass for TypeckMir { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &mut Mir<'tcx>) { + fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { let item_id = src.item_id(); let def_id = tcx.hir.local_def_id(item_id); debug!("run_pass: {:?}", def_id); From 72791b3b09dabe0a715027920a2a7e557da744a4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 20:21:28 -0400 Subject: [PATCH 15/68] modify MIR type-checker to process obligations as they are incurred --- src/librustc_mir/transform/type_check.rs | 81 +++++++++++------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index e10f7a59b5417..d016f3253539b 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,8 +11,9 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::infer::{self, InferCtxt, InferOk}; -use rustc::traits; +use rustc::infer::{InferCtxt, InferOk, InferResult, UnitResult}; +use rustc::traits::{self, FulfillmentContext}; +use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, Ty, TyCtxt, TypeVariants}; use rustc::middle::const_val::ConstVal; @@ -366,7 +367,6 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, - fulfillment_cx: traits::FulfillmentContext<'tcx>, last_span: Span, body_id: ast::NodeId, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, @@ -380,7 +380,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ) -> Self { TypeChecker { infcx, - fulfillment_cx: traits::FulfillmentContext::new(), last_span: DUMMY_SP, body_id, param_env, @@ -392,37 +391,45 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { traits::ObligationCause::misc(span, self.body_id) } - pub fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'tcx, T>) -> T { - for obligation in infer_ok.obligations { - self.fulfillment_cx - .register_predicate_obligation(self.infcx, obligation); - } - infer_ok.value + fn fully_perform_op(&self, + op: OP) + -> Result> + where OP: FnOnce() -> InferResult<'tcx, R> + { + let mut fulfill_cx = FulfillmentContext::new(); + let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op())?; + fulfill_cx.register_predicate_obligations(self.infcx, obligations); + if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { + span_mirbug!(self, "", "errors selecting obligation: {:?}", e); + } // FIXME propagate + Ok(value) } fn sub_types( - &mut self, + &self, sub: Ty<'tcx>, sup: Ty<'tcx>, _at_location: Location, - ) -> infer::UnitResult<'tcx> { - self.infcx - .at(&self.misc(self.last_span), self.param_env) - .sup(sup, sub) - .map(|ok| self.register_infer_ok_obligations(ok)) + ) -> UnitResult<'tcx> { + self.fully_perform_op(|| { + self.infcx + .at(&self.misc(self.last_span), self.param_env) + .sup(sup, sub) + }) } fn eq_types( - &mut self, - span: Span, + &self, + _span: Span, a: Ty<'tcx>, b: Ty<'tcx>, _at_location: Location, - ) -> infer::UnitResult<'tcx> { - self.infcx - .at(&self.misc(span), self.param_env) - .eq(b, a) - .map(|ok| self.register_infer_ok_obligations(ok)) + ) -> UnitResult<'tcx> { + self.fully_perform_op(|| { + self.infcx + .at(&self.misc(self.last_span), self.param_env) + .eq(b, a) + }) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { @@ -895,26 +902,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { where T: fmt::Debug + TypeFoldable<'tcx>, { - let mut selcx = traits::SelectionContext::new(self.infcx); - let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID); - let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, self.param_env, cause, value); - - debug!("normalize: value={:?} obligations={:?}", value, obligations); - - let fulfill_cx = &mut self.fulfillment_cx; - for obligation in obligations { - fulfill_cx.register_predicate_obligation(self.infcx, obligation); - } - - value - } - - fn verify_obligations(&mut self, mir: &Mir<'tcx>) { - self.last_span = mir.span; - if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) { - span_mirbug!(self, "", "errors selecting obligation: {:?}", e); - } + self.fully_perform_op(|| { + let mut selcx = traits::SelectionContext::new(self.infcx); + let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID); + let traits::Normalized { value, obligations } = + traits::normalize(&mut selcx, self.param_env, cause, value); + Ok(InferOk { value, obligations }) + }).unwrap() } } @@ -943,7 +937,6 @@ impl MirPass for TypeckMir { } } checker.typeck_mir(mir); - checker.verify_obligations(mir); }); } } From 0bdc009982df630d6c31c2d57c75b3f27819a72a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:05:31 -0500 Subject: [PATCH 16/68] extract lexical region resolution into its own sub-module --- .../infer/region_inference/lexical_resolve.rs | 730 ++++++++++++++++++ src/librustc/infer/region_inference/mod.rs | 701 +---------------- 2 files changed, 743 insertions(+), 688 deletions(-) create mode 100644 src/librustc/infer/region_inference/lexical_resolve.rs diff --git a/src/librustc/infer/region_inference/lexical_resolve.rs b/src/librustc/infer/region_inference/lexical_resolve.rs new file mode 100644 index 0000000000000..f32cd45a7409a --- /dev/null +++ b/src/librustc/infer/region_inference/lexical_resolve.rs @@ -0,0 +1,730 @@ +// 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. + +//! The code to do lexical region resolution. + +use infer::SubregionOrigin; +use infer::region_inference::graphviz; +use infer::region_inference::Constraint; +use infer::region_inference::Constraint::*; +use infer::region_inference::RegionVarBindings; +use infer::region_inference::RegionResolutionError; +use infer::region_inference::VarValue; +use infer::region_inference::VerifyBound; +use middle::free_region::RegionRelations; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; +use std::fmt; +use std::u32; +use ty::{self, TyCtxt}; +use ty::{Region, RegionVid}; +use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; +use ty::{ReLateBound, ReScope, ReVar, ReSkolemized}; + +struct RegionAndOrigin<'tcx> { + region: Region<'tcx>, + origin: SubregionOrigin<'tcx>, +} + +type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; + +impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { + /// This function performs the actual region resolution. It must be + /// called after all constraints have been added. It performs a + /// fixed-point iteration to find region values which satisfy all + /// constraints, assuming such values can be found; if they cannot, + /// errors are reported. + pub fn resolve_regions( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + ) -> Vec> { + debug!("RegionVarBindings: resolve_regions()"); + let mut errors = vec![]; + let v = self.infer_variable_values(region_rels, &mut errors); + *self.values.borrow_mut() = Some(v); + errors + } + + pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { + match *self.values.borrow() { + None => span_bug!( + (*self.var_origins.borrow())[rid.index as usize].span(), + "attempt to resolve region variable before values have \ + been computed!" + ), + Some(ref values) => { + let r = lookup(self.tcx, values, rid); + debug!("resolve_var({:?}) = {:?}", rid, r); + r + } + } + } + + fn lub_concrete_regions( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + match (a, b) { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + bug!("cannot relate region: LUB({:?}, {:?})", a, b); + } + + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + (&ReEmpty, r) | (r, &ReEmpty) => { + r // everything lives longer than empty + } + + (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { + span_bug!( + (*self.var_origins.borrow())[v_id.index as usize].span(), + "lub_concrete_regions invoked with non-concrete \ + regions: {:?}, {:?}", + a, + b + ); + } + + (&ReEarlyBound(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReEarlyBound(_)) | + (&ReFree(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReFree(_)) => { + // A "free" region can be interpreted as "some region + // at least as big as fr.scope". So, we can + // reasonably compare free regions and scopes: + let fr_scope = match (a, b) { + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { + region_rels.region_scope_tree.early_free_scope(self.tcx, br) + } + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { + region_rels.region_scope_tree.free_scope(self.tcx, fr) + } + _ => bug!(), + }; + let r_id = region_rels + .region_scope_tree + .nearest_common_ancestor(fr_scope, s_id); + if r_id == fr_scope { + // if the free region's scope `fr.scope` is bigger than + // the scope region `s_id`, then the LUB is the free + // region itself: + match (a, b) { + (_, &ReScope(_)) => return a, + (&ReScope(_), _) => return b, + _ => bug!(), + } + } + + // otherwise, we don't know what the free region is, + // so we must conservatively say the LUB is static: + self.tcx.types.re_static + } + + (&ReScope(a_id), &ReScope(b_id)) => { + // The region corresponding to an outer block is a + // subtype of the region corresponding to an inner + // block. + let lub = region_rels + .region_scope_tree + .nearest_common_ancestor(a_id, b_id); + self.tcx.mk_region(ReScope(lub)) + } + + (&ReEarlyBound(_), &ReEarlyBound(_)) | + (&ReFree(_), &ReEarlyBound(_)) | + (&ReEarlyBound(_), &ReFree(_)) | + (&ReFree(_), &ReFree(_)) => region_rels.lub_free_regions(a, b), + + // For these types, we cannot define any additional + // relationship: + (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b { + a + } else { + self.tcx.types.re_static + }, + } + } + + fn infer_variable_values( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + errors: &mut Vec>, + ) -> Vec> { + let mut var_data = self.construct_var_data(); + + // Dorky hack to cause `dump_constraints` to only get called + // if debug mode is enabled: + debug!( + "----() End constraint listing (context={:?}) {:?}---", + region_rels.context, + self.dump_constraints(region_rels) + ); + graphviz::maybe_print_constraints_for(self, region_rels); + + let graph = self.construct_graph(); + self.expand_givens(&graph); + self.expansion(region_rels, &mut var_data); + self.collect_errors(region_rels, &mut var_data, errors); + self.collect_var_errors(region_rels, &var_data, &graph, errors); + var_data + } + + fn construct_var_data(&self) -> Vec> { + (0..self.num_vars() as usize) + .map(|_| VarValue::Value(self.tcx.types.re_empty)) + .collect() + } + + fn dump_constraints(&self, free_regions: &RegionRelations<'a, 'gcx, 'tcx>) { + debug!( + "----() Start constraint listing (context={:?}) ()----", + free_regions.context + ); + for (idx, (constraint, _)) in self.constraints.borrow().iter().enumerate() { + debug!("Constraint {} => {:?}", idx, constraint); + } + } + + fn expand_givens(&self, graph: &RegionGraph) { + // Givens are a kind of horrible hack to account for + // constraints like 'c <= '0 that are known to hold due to + // closure signatures (see the comment above on the `givens` + // field). They should go away. But until they do, the role + // of this fn is to account for the transitive nature: + // + // Given 'c <= '0 + // and '0 <= '1 + // then 'c <= '1 + + let mut givens = self.givens.borrow_mut(); + let seeds: Vec<_> = givens.iter().cloned().collect(); + for (r, vid) in seeds { + let seed_index = NodeIndex(vid.index as usize); + for succ_index in graph.depth_traverse(seed_index, OUTGOING) { + let succ_index = succ_index.0 as u32; + if succ_index < self.num_vars() { + let succ_vid = RegionVid { index: succ_index }; + givens.insert((r, succ_vid)); + } + } + } + } + + fn expansion( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + var_values: &mut [VarValue<'tcx>], + ) { + self.iterate_until_fixed_point("Expansion", |constraint, origin| { + debug!("expansion: constraint={:?} origin={:?}", constraint, origin); + match *constraint { + ConstrainRegSubVar(a_region, b_vid) => { + let b_data = &mut var_values[b_vid.index as usize]; + self.expand_node(region_rels, a_region, b_vid, b_data) + } + ConstrainVarSubVar(a_vid, b_vid) => match var_values[a_vid.index as usize] { + VarValue::ErrorValue => false, + VarValue::Value(a_region) => { + let b_node = &mut var_values[b_vid.index as usize]; + self.expand_node(region_rels, a_region, b_vid, b_node) + } + }, + ConstrainRegSubReg(..) | ConstrainVarSubReg(..) => { + // These constraints are checked after expansion + // is done, in `collect_errors`. + false + } + } + }) + } + + fn expand_node( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + a_region: Region<'tcx>, + b_vid: RegionVid, + b_data: &mut VarValue<'tcx>, + ) -> bool { + debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data); + + // Check if this relationship is implied by a given. + match *a_region { + ty::ReEarlyBound(_) | ty::ReFree(_) => { + if self.givens.borrow().contains(&(a_region, b_vid)) { + debug!("given"); + return false; + } + } + _ => {} + } + + match *b_data { + VarValue::Value(cur_region) => { + let lub = self.lub_concrete_regions(region_rels, a_region, cur_region); + if lub == cur_region { + return false; + } + + debug!( + "Expanding value of {:?} from {:?} to {:?}", + b_vid, + cur_region, + lub + ); + + *b_data = VarValue::Value(lub); + return true; + } + + VarValue::ErrorValue => { + return false; + } + } + } + + /// After expansion is complete, go and check upper bounds (i.e., + /// cases where the region cannot grow larger than a fixed point) + /// and check that they are satisfied. + fn collect_errors( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + var_data: &mut Vec>, + errors: &mut Vec>, + ) { + let constraints = self.constraints.borrow(); + for (constraint, origin) in constraints.iter() { + debug!( + "collect_errors: constraint={:?} origin={:?}", + constraint, + origin + ); + match *constraint { + ConstrainRegSubVar(..) | ConstrainVarSubVar(..) => { + // Expansion will ensure that these constraints hold. Ignore. + } + + ConstrainRegSubReg(sub, sup) => { + if region_rels.is_subregion_of(sub, sup) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + origin, + sub, + sup + ); + + errors.push(RegionResolutionError::ConcreteFailure((*origin).clone(), sub, sup)); + } + + ConstrainVarSubReg(a_vid, b_region) => { + let a_data = &mut var_data[a_vid.index as usize]; + debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region); + + let a_region = match *a_data { + VarValue::ErrorValue => continue, + VarValue::Value(a_region) => a_region, + }; + + // Do not report these errors immediately: + // instead, set the variable value to error and + // collect them later. + if !region_rels.is_subregion_of(a_region, b_region) { + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?}={:?} <= {:?}", + origin, + a_vid, + a_region, + b_region + ); + *a_data = VarValue::ErrorValue; + } + } + } + } + + for verify in self.verifys.borrow().iter() { + debug!("collect_errors: verify={:?}", verify); + let sub = normalize(self.tcx, var_data, verify.region); + + // This was an inference variable which didn't get + // constrained, therefore it can be assume to hold. + if let ty::ReEmpty = *sub { + continue; + } + + if verify.bound.is_met(region_rels, var_data, sub) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + verify.origin, + verify.region, + verify.bound + ); + + errors.push(RegionResolutionError::GenericBoundFailure( + verify.origin.clone(), + verify.kind.clone(), + sub, + )); + } + } + + /// Go over the variables that were declared to be error variables + /// and create a `RegionResolutionError` for each of them. + fn collect_var_errors( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + var_data: &[VarValue<'tcx>], + graph: &RegionGraph<'tcx>, + errors: &mut Vec>, + ) { + debug!("collect_var_errors"); + + // This is the best way that I have found to suppress + // duplicate and related errors. Basically we keep a set of + // flags for every node. Whenever an error occurs, we will + // walk some portion of the graph looking to find pairs of + // conflicting regions to report to the user. As we walk, we + // trip the flags from false to true, and if we find that + // we've already reported an error involving any particular + // node we just stop and don't report the current error. The + // idea is to report errors that derive from independent + // regions of the graph, but not those that derive from + // overlapping locations. + let mut dup_vec = vec![u32::MAX; self.num_vars() as usize]; + + for idx in 0..self.num_vars() as usize { + match var_data[idx] { + VarValue::Value(_) => { /* Inference successful */ } + VarValue::ErrorValue => { + /* Inference impossible, this value contains + inconsistent constraints. + + I think that in this case we should report an + error now---unlike the case above, we can't + wait to see whether the user needs the result + of this variable. The reason is that the mere + existence of this variable implies that the + region graph is inconsistent, whether or not it + is used. + + For example, we may have created a region + variable that is the GLB of two other regions + which do not have a GLB. Even if that variable + is not used, it implies that those two regions + *should* have a GLB. + + At least I think this is true. It may be that + the mere existence of a conflict in a region variable + that is not used is not a problem, so if this rule + starts to create problems we'll have to revisit + this portion of the code and think hard about it. =) */ + + let node_vid = RegionVid { index: idx as u32 }; + self.collect_error_for_expanding_node( + region_rels, + graph, + &mut dup_vec, + node_vid, + errors, + ); + } + } + } + } + + fn construct_graph(&self) -> RegionGraph<'tcx> { + let num_vars = self.num_vars(); + + let constraints = self.constraints.borrow(); + + let mut graph = graph::Graph::new(); + + for _ in 0..num_vars { + graph.add_node(()); + } + + // Issue #30438: two distinct dummy nodes, one for incoming + // edges (dummy_source) and another for outgoing edges + // (dummy_sink). In `dummy -> a -> b -> dummy`, using one + // dummy node leads one to think (erroneously) there exists a + // path from `b` to `a`. Two dummy nodes sidesteps the issue. + let dummy_source = graph.add_node(()); + let dummy_sink = graph.add_node(()); + + for (constraint, _) in constraints.iter() { + match *constraint { + ConstrainVarSubVar(a_id, b_id) => { + graph.add_edge( + NodeIndex(a_id.index as usize), + NodeIndex(b_id.index as usize), + *constraint, + ); + } + ConstrainRegSubVar(_, b_id) => { + graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); + } + ConstrainVarSubReg(a_id, _) => { + graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); + } + ConstrainRegSubReg(..) => { + // this would be an edge from `dummy_source` to + // `dummy_sink`; just ignore it. + } + } + } + + return graph; + } + + fn collect_error_for_expanding_node( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + graph: &RegionGraph<'tcx>, + dup_vec: &mut [u32], + node_idx: RegionVid, + errors: &mut Vec>, + ) { + // Errors in expanding nodes result from a lower-bound that is + // not contained by an upper-bound. + let (mut lower_bounds, lower_dup) = + self.collect_concrete_regions(graph, node_idx, graph::INCOMING, dup_vec); + let (mut upper_bounds, upper_dup) = + self.collect_concrete_regions(graph, node_idx, graph::OUTGOING, dup_vec); + + if lower_dup || upper_dup { + return; + } + + // We place free regions first because we are special casing + // SubSupConflict(ReFree, ReFree) when reporting error, and so + // the user will more likely get a specific suggestion. + fn region_order_key(x: &RegionAndOrigin) -> u8 { + match *x.region { + ReEarlyBound(_) => 0, + ReFree(_) => 1, + _ => 2, + } + } + lower_bounds.sort_by_key(region_order_key); + upper_bounds.sort_by_key(region_order_key); + + for lower_bound in &lower_bounds { + for upper_bound in &upper_bounds { + if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { + let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone(); + debug!( + "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ + sup: {:?}", + origin, + node_idx, + lower_bound.region, + upper_bound.region + ); + errors.push(RegionResolutionError::SubSupConflict( + origin, + lower_bound.origin.clone(), + lower_bound.region, + upper_bound.origin.clone(), + upper_bound.region, + )); + return; + } + } + } + + span_bug!( + (*self.var_origins.borrow())[node_idx.index as usize].span(), + "collect_error_for_expanding_node() could not find \ + error for var {:?}, lower_bounds={:?}, \ + upper_bounds={:?}", + node_idx, + lower_bounds, + upper_bounds + ); + } + + fn collect_concrete_regions( + &self, + graph: &RegionGraph<'tcx>, + orig_node_idx: RegionVid, + dir: Direction, + dup_vec: &mut [u32], + ) -> (Vec>, bool) { + struct WalkState<'tcx> { + set: FxHashSet, + stack: Vec, + result: Vec>, + dup_found: bool, + } + let mut state = WalkState { + set: FxHashSet(), + stack: vec![orig_node_idx], + result: Vec::new(), + dup_found: false, + }; + state.set.insert(orig_node_idx); + + // to start off the process, walk the source node in the + // direction specified + process_edges(self, &mut state, graph, orig_node_idx, dir); + + while !state.stack.is_empty() { + let node_idx = state.stack.pop().unwrap(); + + // check whether we've visited this node on some previous walk + if dup_vec[node_idx.index as usize] == u32::MAX { + dup_vec[node_idx.index as usize] = orig_node_idx.index; + } else if dup_vec[node_idx.index as usize] != orig_node_idx.index { + state.dup_found = true; + } + + debug!( + "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", + orig_node_idx, + node_idx + ); + + process_edges(self, &mut state, graph, node_idx, dir); + } + + let WalkState { + result, dup_found, .. + } = state; + return (result, dup_found); + + fn process_edges<'a, 'gcx, 'tcx>( + this: &RegionVarBindings<'a, 'gcx, 'tcx>, + state: &mut WalkState<'tcx>, + graph: &RegionGraph<'tcx>, + source_vid: RegionVid, + dir: Direction, + ) { + debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); + + let source_node_index = NodeIndex(source_vid.index as usize); + for (_, edge) in graph.adjacent_edges(source_node_index, dir) { + match edge.data { + ConstrainVarSubVar(from_vid, to_vid) => { + let opp_vid = if from_vid == source_vid { + to_vid + } else { + from_vid + }; + if state.set.insert(opp_vid) { + state.stack.push(opp_vid); + } + } + + ConstrainRegSubVar(region, _) | ConstrainVarSubReg(_, region) => { + state.result.push(RegionAndOrigin { + region, + origin: this.constraints.borrow().get(&edge.data).unwrap().clone(), + }); + } + + ConstrainRegSubReg(..) => panic!( + "cannot reach reg-sub-reg edge in region inference \ + post-processing" + ), + } + } + } + } + + fn iterate_until_fixed_point(&self, tag: &str, mut body: F) + where + F: FnMut(&Constraint<'tcx>, &SubregionOrigin<'tcx>) -> bool, + { + let mut iteration = 0; + let mut changed = true; + while changed { + changed = false; + iteration += 1; + debug!("---- {} Iteration {}{}", "#", tag, iteration); + for (constraint, origin) in self.constraints.borrow().iter() { + let edge_changed = body(constraint, origin); + if edge_changed { + debug!("Updated due to constraint {:?}", constraint); + changed = true; + } + } + } + debug!("---- {} Complete after {} iteration(s)", tag, iteration); + } +} + +fn normalize<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + values: &Vec>, + r: ty::Region<'tcx>, +) -> ty::Region<'tcx> { + match *r { + ty::ReVar(rid) => lookup(tcx, values, rid), + _ => r, + } +} + +fn lookup<'a, 'gcx, 'tcx>( + tcx: TyCtxt<'a, 'gcx, 'tcx>, + values: &Vec>, + rid: ty::RegionVid, +) -> ty::Region<'tcx> { + match values[rid.index as usize] { + VarValue::Value(r) => r, + VarValue::ErrorValue => tcx.types.re_static, // Previously reported error. + } +} + +impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) + } +} + + +impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { + fn is_met( + &self, + region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + var_values: &Vec>, + min: ty::Region<'tcx>, + ) -> bool { + let tcx = region_rels.tcx; + match self { + &VerifyBound::AnyRegion(ref rs) => rs.iter() + .map(|&r| normalize(tcx, var_values, r)) + .any(|r| region_rels.is_subregion_of(min, r)), + + &VerifyBound::AllRegions(ref rs) => rs.iter() + .map(|&r| normalize(tcx, var_values, r)) + .all(|r| region_rels.is_subregion_of(min, r)), + + &VerifyBound::AnyBound(ref bs) => { + bs.iter().any(|b| b.is_met(region_rels, var_values, min)) + } + + &VerifyBound::AllBounds(ref bs) => { + bs.iter().all(|b| b.is_met(region_rels, var_values, min)) + } + } + } +} diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 8351be490767a..a658e842f4c0f 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -20,19 +20,18 @@ use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable}; use super::unify_key; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; use rustc_data_structures::unify::{self, UnificationTable}; -use middle::free_region::RegionRelations; use ty::{self, Ty, TyCtxt}; use ty::{Region, RegionVid}; -use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; -use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh}; +use ty::ReStatic; +use ty::{ReLateBound, ReVar, ReSkolemized, BrFresh}; use std::cell::{Cell, RefCell}; use std::fmt; use std::mem; use std::u32; +mod lexical_resolve; mod graphviz; /// A constraint that influences the inference process. @@ -62,10 +61,10 @@ pub enum Constraint<'tcx> { /// step doesn't influence inference). #[derive(Debug)] pub struct Verify<'tcx> { - kind: GenericKind<'tcx>, - origin: SubregionOrigin<'tcx>, - region: Region<'tcx>, - bound: VerifyBound<'tcx>, + pub kind: GenericKind<'tcx>, + pub origin: SubregionOrigin<'tcx>, + pub region: Region<'tcx>, + pub bound: VerifyBound<'tcx>, } #[derive(Copy, Clone, PartialEq, Eq)] @@ -177,6 +176,12 @@ pub enum ProcessedErrorOrigin<'tcx> { VariableFailure(RegionVariableOrigin), } +#[derive(Copy, Clone, Debug)] +pub enum VarValue<'tcx> { + Value(Region<'tcx>), + ErrorValue, +} + pub type CombineMap<'tcx> = FxHashMap, RegionVid>; pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { @@ -792,21 +797,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - match *self.values.borrow() { - None => { - span_bug!((*self.var_origins.borrow())[rid.index as usize].span(), - "attempt to resolve region variable before values have \ - been computed!") - } - Some(ref values) => { - let r = lookup(self.tcx, values, rid); - debug!("resolve_var({:?}) = {:?}", rid, r); - r - } - } - } - pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { let vid = self.unification_table.borrow_mut().find_value(rid).min_vid; self.tcx.mk_region(ty::ReVar(vid)) @@ -881,644 +871,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { debug!("tainted: result={:?}", taint_set.regions); return taint_set.into_set(); } - - /// This function performs the actual region resolution. It must be - /// called after all constraints have been added. It performs a - /// fixed-point iteration to find region values which satisfy all - /// constraints, assuming such values can be found; if they cannot, - /// errors are reported. - pub fn resolve_regions(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>) - -> Vec> { - debug!("RegionVarBindings: resolve_regions()"); - let mut errors = vec![]; - let v = self.infer_variable_values(region_rels, &mut errors); - *self.values.borrow_mut() = Some(v); - errors - } - - fn lub_concrete_regions(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { - match (a, b) { - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) | - (&ReErased, _) | - (_, &ReErased) => { - bug!("cannot relate region: LUB({:?}, {:?})", a, b); - } - - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - (&ReEmpty, r) | (r, &ReEmpty) => { - r // everything lives longer than empty - } - - (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { - span_bug!((*self.var_origins.borrow())[v_id.index as usize].span(), - "lub_concrete_regions invoked with non-concrete \ - regions: {:?}, {:?}", - a, - b); - } - - (&ReEarlyBound(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReEarlyBound(_)) | - (&ReFree(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReFree(_)) => { - // A "free" region can be interpreted as "some region - // at least as big as fr.scope". So, we can - // reasonably compare free regions and scopes: - let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - region_rels.region_scope_tree.early_free_scope(self.tcx, br) - } - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - region_rels.region_scope_tree.free_scope(self.tcx, fr) - } - _ => bug!() - }; - let r_id = region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id); - if r_id == fr_scope { - // if the free region's scope `fr.scope` is bigger than - // the scope region `s_id`, then the LUB is the free - // region itself: - match (a, b) { - (_, &ReScope(_)) => return a, - (&ReScope(_), _) => return b, - _ => bug!() - } - } - - // otherwise, we don't know what the free region is, - // so we must conservatively say the LUB is static: - self.tcx.types.re_static - } - - (&ReScope(a_id), &ReScope(b_id)) => { - // The region corresponding to an outer block is a - // subtype of the region corresponding to an inner - // block. - let lub = region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); - self.tcx.mk_region(ReScope(lub)) - } - - (&ReEarlyBound(_), &ReEarlyBound(_)) | - (&ReFree(_), &ReEarlyBound(_)) | - (&ReEarlyBound(_), &ReFree(_)) | - (&ReFree(_), &ReFree(_)) => { - region_rels.lub_free_regions(a, b) - } - - // For these types, we cannot define any additional - // relationship: - (&ReSkolemized(..), _) | - (_, &ReSkolemized(..)) => { - if a == b { - a - } else { - self.tcx.types.re_static - } - } - } - } -} - -// ______________________________________________________________________ - -#[derive(Copy, Clone, Debug)] -pub enum VarValue<'tcx> { - Value(Region<'tcx>), - ErrorValue, -} - -struct RegionAndOrigin<'tcx> { - region: Region<'tcx>, - origin: SubregionOrigin<'tcx>, -} - -type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; - -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { - fn infer_variable_values(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - errors: &mut Vec>) - -> Vec> { - let mut var_data = self.construct_var_data(); - - // Dorky hack to cause `dump_constraints` to only get called - // if debug mode is enabled: - debug!("----() End constraint listing (context={:?}) {:?}---", - region_rels.context, - self.dump_constraints(region_rels)); - graphviz::maybe_print_constraints_for(self, region_rels); - - let graph = self.construct_graph(); - self.expand_givens(&graph); - self.expansion(region_rels, &mut var_data); - self.collect_errors(region_rels, &mut var_data, errors); - self.collect_var_errors(region_rels, &var_data, &graph, errors); - var_data - } - - fn construct_var_data(&self) -> Vec> { - (0..self.num_vars() as usize) - .map(|_| Value(self.tcx.types.re_empty)) - .collect() - } - - fn dump_constraints(&self, free_regions: &RegionRelations<'a, 'gcx, 'tcx>) { - debug!("----() Start constraint listing (context={:?}) ()----", - free_regions.context); - for (idx, (constraint, _)) in self.constraints.borrow().iter().enumerate() { - debug!("Constraint {} => {:?}", idx, constraint); - } - } - - fn expand_givens(&self, graph: &RegionGraph) { - // Givens are a kind of horrible hack to account for - // constraints like 'c <= '0 that are known to hold due to - // closure signatures (see the comment above on the `givens` - // field). They should go away. But until they do, the role - // of this fn is to account for the transitive nature: - // - // Given 'c <= '0 - // and '0 <= '1 - // then 'c <= '1 - - let mut givens = self.givens.borrow_mut(); - let seeds: Vec<_> = givens.iter().cloned().collect(); - for (r, vid) in seeds { - let seed_index = NodeIndex(vid.index as usize); - for succ_index in graph.depth_traverse(seed_index, OUTGOING) { - let succ_index = succ_index.0 as u32; - if succ_index < self.num_vars() { - let succ_vid = RegionVid { index: succ_index }; - givens.insert((r, succ_vid)); - } - } - } - } - - fn expansion(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &mut [VarValue<'tcx>]) { - self.iterate_until_fixed_point("Expansion", |constraint, origin| { - debug!("expansion: constraint={:?} origin={:?}", - constraint, origin); - match *constraint { - ConstrainRegSubVar(a_region, b_vid) => { - let b_data = &mut var_values[b_vid.index as usize]; - self.expand_node(region_rels, a_region, b_vid, b_data) - } - ConstrainVarSubVar(a_vid, b_vid) => { - match var_values[a_vid.index as usize] { - ErrorValue => false, - Value(a_region) => { - let b_node = &mut var_values[b_vid.index as usize]; - self.expand_node(region_rels, a_region, b_vid, b_node) - } - } - } - ConstrainRegSubReg(..) | - ConstrainVarSubReg(..) => { - // These constraints are checked after expansion - // is done, in `collect_errors`. - false - } - } - }) - } - - fn expand_node(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - a_region: Region<'tcx>, - b_vid: RegionVid, - b_data: &mut VarValue<'tcx>) - -> bool { - debug!("expand_node({:?}, {:?} == {:?})", - a_region, - b_vid, - b_data); - - // Check if this relationship is implied by a given. - match *a_region { - ty::ReEarlyBound(_) | - ty::ReFree(_) => { - if self.givens.borrow().contains(&(a_region, b_vid)) { - debug!("given"); - return false; - } - } - _ => {} - } - - match *b_data { - Value(cur_region) => { - let lub = self.lub_concrete_regions(region_rels, a_region, cur_region); - if lub == cur_region { - return false; - } - - debug!("Expanding value of {:?} from {:?} to {:?}", - b_vid, - cur_region, - lub); - - *b_data = Value(lub); - return true; - } - - ErrorValue => { - return false; - } - } - } - - /// After expansion is complete, go and check upper bounds (i.e., - /// cases where the region cannot grow larger than a fixed point) - /// and check that they are satisfied. - fn collect_errors(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &mut Vec>, - errors: &mut Vec>) { - let constraints = self.constraints.borrow(); - for (constraint, origin) in constraints.iter() { - debug!("collect_errors: constraint={:?} origin={:?}", - constraint, origin); - match *constraint { - ConstrainRegSubVar(..) | - ConstrainVarSubVar(..) => { - // Expansion will ensure that these constraints hold. Ignore. - } - - ConstrainRegSubReg(sub, sup) => { - if region_rels.is_subregion_of(sub, sup) { - continue; - } - - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?} <= {:?}", - origin, - sub, - sup); - - errors.push(ConcreteFailure((*origin).clone(), sub, sup)); - } - - ConstrainVarSubReg(a_vid, b_region) => { - let a_data = &mut var_data[a_vid.index as usize]; - debug!("contraction: {:?} == {:?}, {:?}", - a_vid, - a_data, - b_region); - - let a_region = match *a_data { - ErrorValue => continue, - Value(a_region) => a_region, - }; - - // Do not report these errors immediately: - // instead, set the variable value to error and - // collect them later. - if !region_rels.is_subregion_of(a_region, b_region) { - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?}={:?} <= {:?}", - origin, - a_vid, - a_region, - b_region); - *a_data = ErrorValue; - } - } - } - } - - for verify in self.verifys.borrow().iter() { - debug!("collect_errors: verify={:?}", verify); - let sub = normalize(self.tcx, var_data, verify.region); - - // This was an inference variable which didn't get - // constrained, therefore it can be assume to hold. - if let ty::ReEmpty = *sub { - continue; - } - - if verify.bound.is_met(region_rels, var_data, sub) { - continue; - } - - debug!("collect_errors: region error at {:?}: \ - cannot verify that {:?} <= {:?}", - verify.origin, - verify.region, - verify.bound); - - errors.push(GenericBoundFailure(verify.origin.clone(), - verify.kind.clone(), - sub)); - } - } - - /// Go over the variables that were declared to be error variables - /// and create a `RegionResolutionError` for each of them. - fn collect_var_errors(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &[VarValue<'tcx>], - graph: &RegionGraph<'tcx>, - errors: &mut Vec>) { - debug!("collect_var_errors"); - - // This is the best way that I have found to suppress - // duplicate and related errors. Basically we keep a set of - // flags for every node. Whenever an error occurs, we will - // walk some portion of the graph looking to find pairs of - // conflicting regions to report to the user. As we walk, we - // trip the flags from false to true, and if we find that - // we've already reported an error involving any particular - // node we just stop and don't report the current error. The - // idea is to report errors that derive from independent - // regions of the graph, but not those that derive from - // overlapping locations. - let mut dup_vec = vec![u32::MAX; self.num_vars() as usize]; - - for idx in 0..self.num_vars() as usize { - match var_data[idx] { - Value(_) => { - /* Inference successful */ - } - ErrorValue => { - /* Inference impossible, this value contains - inconsistent constraints. - - I think that in this case we should report an - error now---unlike the case above, we can't - wait to see whether the user needs the result - of this variable. The reason is that the mere - existence of this variable implies that the - region graph is inconsistent, whether or not it - is used. - - For example, we may have created a region - variable that is the GLB of two other regions - which do not have a GLB. Even if that variable - is not used, it implies that those two regions - *should* have a GLB. - - At least I think this is true. It may be that - the mere existence of a conflict in a region variable - that is not used is not a problem, so if this rule - starts to create problems we'll have to revisit - this portion of the code and think hard about it. =) */ - - let node_vid = RegionVid { index: idx as u32 }; - self.collect_error_for_expanding_node(region_rels, - graph, - &mut dup_vec, - node_vid, - errors); - } - } - } - } - - fn construct_graph(&self) -> RegionGraph<'tcx> { - let num_vars = self.num_vars(); - - let constraints = self.constraints.borrow(); - - let mut graph = graph::Graph::new(); - - for _ in 0..num_vars { - graph.add_node(()); - } - - // Issue #30438: two distinct dummy nodes, one for incoming - // edges (dummy_source) and another for outgoing edges - // (dummy_sink). In `dummy -> a -> b -> dummy`, using one - // dummy node leads one to think (erroneously) there exists a - // path from `b` to `a`. Two dummy nodes sidesteps the issue. - let dummy_source = graph.add_node(()); - let dummy_sink = graph.add_node(()); - - for (constraint, _) in constraints.iter() { - match *constraint { - ConstrainVarSubVar(a_id, b_id) => { - graph.add_edge(NodeIndex(a_id.index as usize), - NodeIndex(b_id.index as usize), - *constraint); - } - ConstrainRegSubVar(_, b_id) => { - graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); - } - ConstrainVarSubReg(a_id, _) => { - graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); - } - ConstrainRegSubReg(..) => { - // this would be an edge from `dummy_source` to - // `dummy_sink`; just ignore it. - } - } - } - - return graph; - } - - fn collect_error_for_expanding_node(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - graph: &RegionGraph<'tcx>, - dup_vec: &mut [u32], - node_idx: RegionVid, - errors: &mut Vec>) { - // Errors in expanding nodes result from a lower-bound that is - // not contained by an upper-bound. - let (mut lower_bounds, lower_dup) = self.collect_concrete_regions(graph, - node_idx, - graph::INCOMING, - dup_vec); - let (mut upper_bounds, upper_dup) = self.collect_concrete_regions(graph, - node_idx, - graph::OUTGOING, - dup_vec); - - if lower_dup || upper_dup { - return; - } - - // We place free regions first because we are special casing - // SubSupConflict(ReFree, ReFree) when reporting error, and so - // the user will more likely get a specific suggestion. - fn region_order_key(x: &RegionAndOrigin) -> u8 { - match *x.region { - ReEarlyBound(_) => 0, - ReFree(_) => 1, - _ => 2 - } - } - lower_bounds.sort_by_key(region_order_key); - upper_bounds.sort_by_key(region_order_key); - - for lower_bound in &lower_bounds { - for upper_bound in &upper_bounds { - if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { - let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone(); - debug!("region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ - sup: {:?}", - origin, - node_idx, - lower_bound.region, - upper_bound.region); - errors.push(SubSupConflict(origin, - lower_bound.origin.clone(), - lower_bound.region, - upper_bound.origin.clone(), - upper_bound.region)); - return; - } - } - } - - span_bug!((*self.var_origins.borrow())[node_idx.index as usize].span(), - "collect_error_for_expanding_node() could not find \ - error for var {:?}, lower_bounds={:?}, \ - upper_bounds={:?}", - node_idx, - lower_bounds, - upper_bounds); - } - - fn collect_concrete_regions(&self, - graph: &RegionGraph<'tcx>, - orig_node_idx: RegionVid, - dir: Direction, - dup_vec: &mut [u32]) - -> (Vec>, bool) { - struct WalkState<'tcx> { - set: FxHashSet, - stack: Vec, - result: Vec>, - dup_found: bool, - } - let mut state = WalkState { - set: FxHashSet(), - stack: vec![orig_node_idx], - result: Vec::new(), - dup_found: false, - }; - state.set.insert(orig_node_idx); - - // to start off the process, walk the source node in the - // direction specified - process_edges(self, &mut state, graph, orig_node_idx, dir); - - while !state.stack.is_empty() { - let node_idx = state.stack.pop().unwrap(); - - // check whether we've visited this node on some previous walk - if dup_vec[node_idx.index as usize] == u32::MAX { - dup_vec[node_idx.index as usize] = orig_node_idx.index; - } else if dup_vec[node_idx.index as usize] != orig_node_idx.index { - state.dup_found = true; - } - - debug!("collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", - orig_node_idx, - node_idx); - - process_edges(self, &mut state, graph, node_idx, dir); - } - - let WalkState {result, dup_found, ..} = state; - return (result, dup_found); - - fn process_edges<'a, 'gcx, 'tcx>(this: &RegionVarBindings<'a, 'gcx, 'tcx>, - state: &mut WalkState<'tcx>, - graph: &RegionGraph<'tcx>, - source_vid: RegionVid, - dir: Direction) { - debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); - - let source_node_index = NodeIndex(source_vid.index as usize); - for (_, edge) in graph.adjacent_edges(source_node_index, dir) { - match edge.data { - ConstrainVarSubVar(from_vid, to_vid) => { - let opp_vid = if from_vid == source_vid { - to_vid - } else { - from_vid - }; - if state.set.insert(opp_vid) { - state.stack.push(opp_vid); - } - } - - ConstrainRegSubVar(region, _) | - ConstrainVarSubReg(_, region) => { - state.result.push(RegionAndOrigin { - region, - origin: this.constraints.borrow().get(&edge.data).unwrap().clone(), - }); - } - - ConstrainRegSubReg(..) => { - panic!("cannot reach reg-sub-reg edge in region inference \ - post-processing") - } - } - } - } - } - - fn iterate_until_fixed_point(&self, tag: &str, mut body: F) - where F: FnMut(&Constraint<'tcx>, &SubregionOrigin<'tcx>) -> bool - { - let mut iteration = 0; - let mut changed = true; - while changed { - changed = false; - iteration += 1; - debug!("---- {} Iteration {}{}", "#", tag, iteration); - for (constraint, origin) in self.constraints.borrow().iter() { - let edge_changed = body(constraint, origin); - if edge_changed { - debug!("Updated due to constraint {:?}", constraint); - changed = true; - } - } - } - debug!("---- {} Complete after {} iteration(s)", tag, iteration); - } - -} - -fn normalize<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - r: ty::Region<'tcx>) - -> ty::Region<'tcx> { - match *r { - ty::ReVar(rid) => lookup(tcx, values, rid), - _ => r, - } -} - -fn lookup<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - rid: ty::RegionVid) - -> ty::Region<'tcx> { - match values[rid.index as usize] { - Value(r) => r, - ErrorValue => tcx.types.re_static, // Previously reported error. - } -} - -impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) - } } impl fmt::Debug for RegionSnapshot { @@ -1607,31 +959,4 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { VerifyBound::AllBounds(vec![self, vb]) } } - - fn is_met(&self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &Vec>, - min: ty::Region<'tcx>) - -> bool { - let tcx = region_rels.tcx; - match self { - &VerifyBound::AnyRegion(ref rs) => - rs.iter() - .map(|&r| normalize(tcx, var_values, r)) - .any(|r| region_rels.is_subregion_of(min, r)), - - &VerifyBound::AllRegions(ref rs) => - rs.iter() - .map(|&r| normalize(tcx, var_values, r)) - .all(|r| region_rels.is_subregion_of(min, r)), - - &VerifyBound::AnyBound(ref bs) => - bs.iter() - .any(|b| b.is_met(region_rels, var_values, min)), - - &VerifyBound::AllBounds(ref bs) => - bs.iter() - .all(|b| b.is_met(region_rels, var_values, min)), - } - } } From 48099865a90ec486d614cff9932350cd08cc776e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:05:47 -0500 Subject: [PATCH 17/68] on_disk_cache: silence warnings --- src/librustc/ty/maps/on_disk_cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 26581501234af..24ce8fb299598 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -165,7 +165,7 @@ impl<'a> CacheDecoder<'a> { fn find_filemap_prev_bytepos(&self, prev_bytepos: BytePos) -> Option<(BytePos, StableFilemapId)> { - for (start, id) in self.prev_filemap_starts.range(BytePos(0) ... prev_bytepos).rev() { + for (start, id) in self.prev_filemap_starts.range(BytePos(0) ..= prev_bytepos).rev() { return Some((*start, *id)) } From 9baef898e8490208907d0ddc7fc6802cdc07d447 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:10:38 -0500 Subject: [PATCH 18/68] move region resolution to be a sibling of `region_inference` Temporary make various fields public. --- .../graphviz.rs | 0 .../mod.rs} | 3 ++- src/librustc/infer/mod.rs | 1 + src/librustc/infer/region_inference/mod.rs | 15 ++++++--------- 4 files changed, 9 insertions(+), 10 deletions(-) rename src/librustc/infer/{region_inference => lexical_region_resolve}/graphviz.rs (100%) rename src/librustc/infer/{region_inference/lexical_resolve.rs => lexical_region_resolve/mod.rs} (99%) diff --git a/src/librustc/infer/region_inference/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs similarity index 100% rename from src/librustc/infer/region_inference/graphviz.rs rename to src/librustc/infer/lexical_region_resolve/graphviz.rs diff --git a/src/librustc/infer/region_inference/lexical_resolve.rs b/src/librustc/infer/lexical_region_resolve/mod.rs similarity index 99% rename from src/librustc/infer/region_inference/lexical_resolve.rs rename to src/librustc/infer/lexical_region_resolve/mod.rs index f32cd45a7409a..8c5d7a7fc9b02 100644 --- a/src/librustc/infer/region_inference/lexical_resolve.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -11,7 +11,6 @@ //! The code to do lexical region resolution. use infer::SubregionOrigin; -use infer::region_inference::graphviz; use infer::region_inference::Constraint; use infer::region_inference::Constraint::*; use infer::region_inference::RegionVarBindings; @@ -28,6 +27,8 @@ use ty::{Region, RegionVid}; use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; use ty::{ReLateBound, ReScope, ReVar, ReSkolemized}; +mod graphviz; + struct RegionAndOrigin<'tcx> { region: Region<'tcx>, origin: SubregionOrigin<'tcx>, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index e73d74c22ba7d..cf32113343df3 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -55,6 +55,7 @@ mod higher_ranked; pub mod lattice; mod lub; pub mod region_inference; +mod lexical_region_resolve; mod outlives; pub mod resolve; mod freshen; diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index a658e842f4c0f..193a791f561bc 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -31,9 +31,6 @@ use std::fmt; use std::mem; use std::u32; -mod lexical_resolve; -mod graphviz; - /// A constraint that influences the inference process. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum Constraint<'tcx> { @@ -185,20 +182,20 @@ pub enum VarValue<'tcx> { pub type CombineMap<'tcx> = FxHashMap, RegionVid>; pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - var_origins: RefCell>, + pub(in infer) tcx: TyCtxt<'a, 'gcx, 'tcx>, + pub(in infer) var_origins: RefCell>, /// Constraints of the form `A <= B` introduced by the region /// checker. Here at least one of `A` and `B` must be a region /// variable. - constraints: RefCell, SubregionOrigin<'tcx>>>, + pub(in infer) constraints: RefCell, SubregionOrigin<'tcx>>>, /// A "verify" is something that we need to verify after inference is /// done, but which does not directly affect inference in any way. /// /// An example is a `A <= B` where neither `A` nor `B` are /// inference variables. - verifys: RefCell>>, + pub(in infer) verifys: RefCell>>, /// A "given" is a relationship that is known to hold. In particular, /// we often know from closure fn signatures that a particular free @@ -217,7 +214,7 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// record the fact that `'a <= 'b` is implied by the fn signature, /// and then ignore the constraint when solving equations. This is /// a bit of a hack but seems to work. - givens: RefCell, ty::RegionVid)>>, + pub(in infer) givens: RefCell, ty::RegionVid)>>, lubs: RefCell>, glbs: RefCell>, @@ -239,7 +236,7 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// This contains the results of inference. It begins as an empty /// option and only acquires a value after inference is complete. - values: RefCell>>>, + pub(in infer) values: RefCell>>>, } pub struct RegionSnapshot { From 9741596d681c9a550146a256e21c7831214a612b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:23:30 -0500 Subject: [PATCH 19/68] region_inference: tighten up `pub`, stop re-exporting enum variants --- src/librustc/infer/error_reporting/mod.rs | 61 ++++++++++--------- .../infer/lexical_region_resolve/graphviz.rs | 8 +-- .../infer/lexical_region_resolve/mod.rs | 27 ++++---- src/librustc/infer/region_inference/mod.rs | 61 ++++++++----------- 4 files changed, 75 insertions(+), 82 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index c262e966576bd..072f76eb6aa8d 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -57,8 +57,7 @@ use infer; use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs}; -use super::region_inference::{RegionResolutionError, ConcreteFailure, SubSupConflict, - GenericBoundFailure, GenericKind}; +use super::region_inference::{RegionResolutionError, GenericKind}; use std::fmt; use hir; @@ -293,33 +292,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { debug!("report_region_errors: error = {:?}", error); if !self.try_report_named_anon_conflict(&error) && - !self.try_report_anon_anon_conflict(&error) { - - match error.clone() { - // These errors could indicate all manner of different - // problems with many different solutions. Rather - // than generate a "one size fits all" error, what we - // attempt to do is go through a number of specific - // scenarios and try to find the best way to present - // the error. If all of these fails, we fall back to a rather - // general bit of code that displays the error information - ConcreteFailure(origin, sub, sup) => { - self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit(); - } - - GenericBoundFailure(kind, param_ty, sub) => { - self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub); - } - - SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => { + !self.try_report_anon_anon_conflict(&error) + { + match error.clone() { + // These errors could indicate all manner of different + // problems with many different solutions. Rather + // than generate a "one size fits all" error, what we + // attempt to do is go through a number of specific + // scenarios and try to find the best way to present + // the error. If all of these fails, we fall back to a rather + // general bit of code that displays the error information + RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit(); + } + + RegionResolutionError::GenericBoundFailure(kind, param_ty, sub) => { + self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub); + } + + RegionResolutionError::SubSupConflict(var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r) => { self.report_sub_sup_conflict(region_scope_tree, var_origin, sub_origin, sub_r, sup_origin, sup_r); - } - } + } + } } } } @@ -351,9 +354,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // the only thing in the list. let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - ConcreteFailure(..) => false, - SubSupConflict(..) => false, - GenericBoundFailure(..) => true, + RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::ConcreteFailure(..) | + RegionResolutionError::SubSupConflict(..) => false, }; @@ -365,9 +368,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // sort the errors by span, for better error message stability. errors.sort_by_key(|u| match *u { - ConcreteFailure(ref sro, _, _) => sro.span(), - GenericBoundFailure(ref sro, _, _) => sro.span(), - SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), }); errors } diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index 49f57d9aef50e..90d6211597a86 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -211,13 +211,13 @@ impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { fn constraint_to_nodes(c: &Constraint) -> (Node, Node) { match *c { - Constraint::ConstrainVarSubVar(rv_1, rv_2) => + Constraint::VarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1), Node::RegionVid(rv_2)), - Constraint::ConstrainRegSubVar(r_1, rv_2) => + Constraint::RegSubVar(r_1, rv_2) => (Node::Region(*r_1), Node::RegionVid(rv_2)), - Constraint::ConstrainVarSubReg(rv_1, r_2) => + Constraint::VarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1), Node::Region(*r_2)), - Constraint::ConstrainRegSubReg(r_1, r_2) => + Constraint::RegSubReg(r_1, r_2) => (Node::Region(*r_1), Node::Region(*r_2)), } } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 8c5d7a7fc9b02..682f5743e4bff 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -12,7 +12,6 @@ use infer::SubregionOrigin; use infer::region_inference::Constraint; -use infer::region_inference::Constraint::*; use infer::region_inference::RegionVarBindings; use infer::region_inference::RegionResolutionError; use infer::region_inference::VarValue; @@ -230,18 +229,18 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { self.iterate_until_fixed_point("Expansion", |constraint, origin| { debug!("expansion: constraint={:?} origin={:?}", constraint, origin); match *constraint { - ConstrainRegSubVar(a_region, b_vid) => { + Constraint::RegSubVar(a_region, b_vid) => { let b_data = &mut var_values[b_vid.index as usize]; self.expand_node(region_rels, a_region, b_vid, b_data) } - ConstrainVarSubVar(a_vid, b_vid) => match var_values[a_vid.index as usize] { + Constraint::VarSubVar(a_vid, b_vid) => match var_values[a_vid.index as usize] { VarValue::ErrorValue => false, VarValue::Value(a_region) => { let b_node = &mut var_values[b_vid.index as usize]; self.expand_node(region_rels, a_region, b_vid, b_node) } }, - ConstrainRegSubReg(..) | ConstrainVarSubReg(..) => { + Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { // These constraints are checked after expansion // is done, in `collect_errors`. false @@ -311,11 +310,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { origin ); match *constraint { - ConstrainRegSubVar(..) | ConstrainVarSubVar(..) => { + Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { // Expansion will ensure that these constraints hold. Ignore. } - ConstrainRegSubReg(sub, sup) => { + Constraint::RegSubReg(sub, sup) => { if region_rels.is_subregion_of(sub, sup) { continue; } @@ -331,7 +330,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { errors.push(RegionResolutionError::ConcreteFailure((*origin).clone(), sub, sup)); } - ConstrainVarSubReg(a_vid, b_region) => { + Constraint::VarSubReg(a_vid, b_region) => { let a_data = &mut var_data[a_vid.index as usize]; debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region); @@ -473,20 +472,20 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { for (constraint, _) in constraints.iter() { match *constraint { - ConstrainVarSubVar(a_id, b_id) => { + Constraint::VarSubVar(a_id, b_id) => { graph.add_edge( NodeIndex(a_id.index as usize), NodeIndex(b_id.index as usize), *constraint, ); } - ConstrainRegSubVar(_, b_id) => { + Constraint::RegSubVar(_, b_id) => { graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); } - ConstrainVarSubReg(a_id, _) => { + Constraint::VarSubReg(a_id, _) => { graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); } - ConstrainRegSubReg(..) => { + Constraint::RegSubReg(..) => { // this would be an edge from `dummy_source` to // `dummy_sink`; just ignore it. } @@ -624,7 +623,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { let source_node_index = NodeIndex(source_vid.index as usize); for (_, edge) in graph.adjacent_edges(source_node_index, dir) { match edge.data { - ConstrainVarSubVar(from_vid, to_vid) => { + Constraint::VarSubVar(from_vid, to_vid) => { let opp_vid = if from_vid == source_vid { to_vid } else { @@ -635,14 +634,14 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - ConstrainRegSubVar(region, _) | ConstrainVarSubReg(_, region) => { + Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { state.result.push(RegionAndOrigin { region, origin: this.constraints.borrow().get(&edge.data).unwrap().clone(), }); } - ConstrainRegSubReg(..) => panic!( + Constraint::RegSubReg(..) => panic!( "cannot reach reg-sub-reg edge in region inference \ post-processing" ), diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 193a791f561bc..97dc7dc7c3d88 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -10,11 +10,8 @@ //! See README.md -pub use self::Constraint::*; -pub use self::UndoLogEntry::*; -pub use self::CombineMapType::*; -pub use self::RegionResolutionError::*; -pub use self::VarValue::*; +use self::UndoLogEntry::*; +use self::CombineMapType::*; use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable}; use super::unify_key; @@ -35,20 +32,20 @@ use std::u32; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum Constraint<'tcx> { /// One region variable is subregion of another - ConstrainVarSubVar(RegionVid, RegionVid), + VarSubVar(RegionVid, RegionVid), /// Concrete region is subregion of region variable - ConstrainRegSubVar(Region<'tcx>, RegionVid), + RegSubVar(Region<'tcx>, RegionVid), /// Region variable is subregion of concrete region. This does not /// directly affect inference, but instead is checked after /// inference is complete. - ConstrainVarSubReg(RegionVid, Region<'tcx>), + VarSubReg(RegionVid, Region<'tcx>), /// A constraint where neither side is a variable. This does not /// directly affect inference, but instead is checked after /// inference is complete. - ConstrainRegSubReg(Region<'tcx>, Region<'tcx>), + RegSubReg(Region<'tcx>, Region<'tcx>), } /// VerifyGenericBound(T, _, R, RS): The parameter type `T` (or @@ -97,13 +94,13 @@ pub enum VerifyBound<'tcx> { } #[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct TwoRegions<'tcx> { +struct TwoRegions<'tcx> { a: Region<'tcx>, b: Region<'tcx>, } #[derive(Copy, Clone, PartialEq)] -pub enum UndoLogEntry<'tcx> { +enum UndoLogEntry<'tcx> { /// Pushed when we start a snapshot. OpenSnapshot, @@ -137,7 +134,7 @@ pub enum UndoLogEntry<'tcx> { } #[derive(Copy, Clone, PartialEq)] -pub enum CombineMapType { +enum CombineMapType { Lub, Glb, } @@ -167,19 +164,13 @@ pub enum RegionResolutionError<'tcx> { Region<'tcx>), } -#[derive(Clone, Debug)] -pub enum ProcessedErrorOrigin<'tcx> { - ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - VariableFailure(RegionVariableOrigin), -} - #[derive(Copy, Clone, Debug)] pub enum VarValue<'tcx> { Value(Region<'tcx>), ErrorValue, } -pub type CombineMap<'tcx> = FxHashMap, RegionVid>; +type CombineMap<'tcx> = FxHashMap, RegionVid>; pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub(in infer) tcx: TyCtxt<'a, 'gcx, 'tcx>, @@ -419,7 +410,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { .rollback_to(snapshot.region_snapshot); } - pub fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) { + fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) { match undo_entry { OpenSnapshot => { panic!("Failure to observe stack discipline"); @@ -571,13 +562,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { undo_entry: &UndoLogEntry<'tcx>) -> bool { match undo_entry { - &AddConstraint(ConstrainVarSubVar(..)) => + &AddConstraint(Constraint::VarSubVar(..)) => false, - &AddConstraint(ConstrainRegSubVar(a, _)) => + &AddConstraint(Constraint::RegSubVar(a, _)) => skols.contains(&a), - &AddConstraint(ConstrainVarSubReg(_, b)) => + &AddConstraint(Constraint::VarSubReg(_, b)) => skols.contains(&b), - &AddConstraint(ConstrainRegSubReg(a, b)) => + &AddConstraint(Constraint::RegSubReg(a, b)) => skols.contains(&a) || skols.contains(&b), &AddGiven(..) => false, @@ -714,16 +705,16 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // all regions are subregions of static, so we can ignore this } (&ReVar(sub_id), &ReVar(sup_id)) => { - self.add_constraint(ConstrainVarSubVar(sub_id, sup_id), origin); + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin); } (_, &ReVar(sup_id)) => { - self.add_constraint(ConstrainRegSubVar(sub, sup_id), origin); + self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin); } (&ReVar(sub_id), _) => { - self.add_constraint(ConstrainVarSubReg(sub_id, sup), origin); + self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin); } _ => { - self.add_constraint(ConstrainRegSubReg(sub, sup), origin); + self.add_constraint(Constraint::RegSubReg(sub, sup), origin); } } } @@ -806,13 +797,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - pub fn combine_vars(&self, - t: CombineMapType, - a: Region<'tcx>, - b: Region<'tcx>, - origin: SubregionOrigin<'tcx>, - mut relate: F) - -> Region<'tcx> + fn combine_vars(&self, + t: CombineMapType, + a: Region<'tcx>, + b: Region<'tcx>, + origin: SubregionOrigin<'tcx>, + mut relate: F) + -> Region<'tcx> where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>) { let vars = TwoRegions { a: a, b: b }; From ab75caf7f071eb9003a5d273afe32d80dc93efef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:23:53 -0500 Subject: [PATCH 20/68] region_inference: extract taint into a sub-module --- src/librustc/infer/region_inference/mod.rs | 89 +----------------- src/librustc/infer/region_inference/taint.rs | 96 ++++++++++++++++++++ 2 files changed, 100 insertions(+), 85 deletions(-) create mode 100644 src/librustc/infer/region_inference/taint.rs diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 97dc7dc7c3d88..173b86efc6737 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -28,6 +28,8 @@ use std::fmt; use std::mem; use std::u32; +mod taint; + /// A constraint that influences the inference process. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum Constraint<'tcx> { @@ -261,89 +263,6 @@ impl TaintDirections { } } -struct TaintSet<'tcx> { - directions: TaintDirections, - regions: FxHashSet> -} - -impl<'a, 'gcx, 'tcx> TaintSet<'tcx> { - fn new(directions: TaintDirections, - initial_region: ty::Region<'tcx>) - -> Self { - let mut regions = FxHashSet(); - regions.insert(initial_region); - TaintSet { directions: directions, regions: regions } - } - - fn fixed_point(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - undo_log: &[UndoLogEntry<'tcx>], - verifys: &[Verify<'tcx>]) { - let mut prev_len = 0; - while prev_len < self.len() { - debug!("tainted: prev_len = {:?} new_len = {:?}", - prev_len, self.len()); - - prev_len = self.len(); - - for undo_entry in undo_log { - match undo_entry { - &AddConstraint(ConstrainVarSubVar(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), - tcx.mk_region(ReVar(b))); - } - &AddConstraint(ConstrainRegSubVar(a, b)) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddConstraint(ConstrainVarSubReg(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), b); - } - &AddConstraint(ConstrainRegSubReg(a, b)) => { - self.add_edge(a, b); - } - &AddGiven(a, b) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddVerify(i) => { - verifys[i].bound.for_each_region(&mut |b| { - self.add_edge(verifys[i].region, b); - }); - } - &Purged | - &AddCombination(..) | - &AddVar(..) | - &OpenSnapshot | - &CommitedSnapshot => {} - } - } - } - } - - fn into_set(self) -> FxHashSet> { - self.regions - } - - fn len(&self) -> usize { - self.regions.len() - } - - fn add_edge(&mut self, - source: ty::Region<'tcx>, - target: ty::Region<'tcx>) { - if self.directions.incoming { - if self.regions.contains(&target) { - self.regions.insert(source); - } - } - - if self.directions.outgoing { - if self.regions.contains(&source) { - self.regions.insert(target); - } - } - } -} - impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> { RegionVarBindings { @@ -852,11 +771,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // `result_set` acts as a worklist: we explore all outgoing // edges and add any new regions we find to result_set. This // is not a terribly efficient implementation. - let mut taint_set = TaintSet::new(directions, r0); + let mut taint_set = taint::TaintSet::new(directions, r0); taint_set.fixed_point(self.tcx, &self.undo_log.borrow()[mark.length..], &self.verifys.borrow()); - debug!("tainted: result={:?}", taint_set.regions); + debug!("tainted: result={:?}", taint_set); return taint_set.into_set(); } } diff --git a/src/librustc/infer/region_inference/taint.rs b/src/librustc/infer/region_inference/taint.rs new file mode 100644 index 0000000000000..acc930bfa3b11 --- /dev/null +++ b/src/librustc/infer/region_inference/taint.rs @@ -0,0 +1,96 @@ +// 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 super::*; + +#[derive(Debug)] +pub(super) struct TaintSet<'tcx> { + directions: TaintDirections, + regions: FxHashSet> +} + +impl<'a, 'gcx, 'tcx> TaintSet<'tcx> { + pub(super) fn new(directions: TaintDirections, + initial_region: ty::Region<'tcx>) + -> Self { + let mut regions = FxHashSet(); + regions.insert(initial_region); + TaintSet { directions: directions, regions: regions } + } + + pub(super) fn fixed_point(&mut self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + undo_log: &[UndoLogEntry<'tcx>], + verifys: &[Verify<'tcx>]) { + let mut prev_len = 0; + while prev_len < self.len() { + debug!("tainted: prev_len = {:?} new_len = {:?}", + prev_len, self.len()); + + prev_len = self.len(); + + for undo_entry in undo_log { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(a, b)) => { + self.add_edge(tcx.mk_region(ReVar(a)), + tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::RegSubVar(a, b)) => { + self.add_edge(a, tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::VarSubReg(a, b)) => { + self.add_edge(tcx.mk_region(ReVar(a)), b); + } + &AddConstraint(Constraint::RegSubReg(a, b)) => { + self.add_edge(a, b); + } + &AddGiven(a, b) => { + self.add_edge(a, tcx.mk_region(ReVar(b))); + } + &AddVerify(i) => { + verifys[i].bound.for_each_region(&mut |b| { + self.add_edge(verifys[i].region, b); + }); + } + &Purged | + &AddCombination(..) | + &AddVar(..) | + &OpenSnapshot | + &CommitedSnapshot => {} + } + } + } + } + + pub(super) fn into_set(self) -> FxHashSet> { + self.regions + } + + fn len(&self) -> usize { + self.regions.len() + } + + fn add_edge(&mut self, + source: ty::Region<'tcx>, + target: ty::Region<'tcx>) { + if self.directions.incoming { + if self.regions.contains(&target) { + self.regions.insert(source); + } + } + + if self.directions.outgoing { + if self.regions.contains(&source) { + self.regions.insert(target); + } + } + } +} + From a51b29fdf45aa46705b2abd7f45a04b58ebd6177 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:28:22 -0500 Subject: [PATCH 21/68] move `RegionResolutionError` into `lexical_region_resolve` --- .../error_reporting/different_lifetimes.rs | 4 +- src/librustc/infer/error_reporting/mod.rs | 3 +- .../error_reporting/named_anon_conflict.rs | 4 +- .../infer/lexical_region_resolve/mod.rs | 40 +++++++++++++++++-- src/librustc/infer/region_inference/mod.rs | 25 ------------ 5 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs index d7e0877d95c28..abe8b66788fa7 100644 --- a/src/librustc/infer/error_reporting/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/different_lifetimes.rs @@ -13,8 +13,8 @@ use hir; use infer::InferCtxt; use ty::{self, Region}; -use infer::region_inference::RegionResolutionError::*; -use infer::region_inference::RegionResolutionError; +use infer::lexical_region_resolve::RegionResolutionError::*; +use infer::lexical_region_resolve::RegionResolutionError; use hir::map as hir_map; use middle::resolve_lifetime as rl; use hir::intravisit::{self, Visitor, NestedVisitorMap}; diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 072f76eb6aa8d..9ce0e503280ea 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -57,7 +57,8 @@ use infer; use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs}; -use super::region_inference::{RegionResolutionError, GenericKind}; +use super::region_inference::GenericKind; +use super::lexical_region_resolve::RegionResolutionError; use std::fmt; use hir; diff --git a/src/librustc/infer/error_reporting/named_anon_conflict.rs b/src/librustc/infer/error_reporting/named_anon_conflict.rs index 6d3b950784097..b06d6dad5ed46 100644 --- a/src/librustc/infer/error_reporting/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/named_anon_conflict.rs @@ -11,8 +11,8 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. use infer::InferCtxt; -use infer::region_inference::RegionResolutionError::*; -use infer::region_inference::RegionResolutionError; +use infer::lexical_region_resolve::RegionResolutionError::*; +use infer::lexical_region_resolve::RegionResolutionError; use ty; impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 682f5743e4bff..217387befd89f 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -11,9 +11,10 @@ //! The code to do lexical region resolution. use infer::SubregionOrigin; +use infer::RegionVariableOrigin; use infer::region_inference::Constraint; +use infer::region_inference::GenericKind; use infer::region_inference::RegionVarBindings; -use infer::region_inference::RegionResolutionError; use infer::region_inference::VarValue; use infer::region_inference::VerifyBound; use middle::free_region::RegionRelations; @@ -23,11 +24,38 @@ use std::fmt; use std::u32; use ty::{self, TyCtxt}; use ty::{Region, RegionVid}; -use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased}; -use ty::{ReLateBound, ReScope, ReVar, ReSkolemized}; +use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; mod graphviz; +#[derive(Clone, Debug)] +pub enum RegionResolutionError<'tcx> { + /// `ConcreteFailure(o, a, b)`: + /// + /// `o` requires that `a <= b`, but this does not hold + ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), + + /// `GenericBoundFailure(p, s, a) + /// + /// The parameter/associated-type `p` must be known to outlive the lifetime + /// `a` (but none of the known bounds are sufficient). + GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), + + /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: + /// + /// Could not infer a value for `v` because `sub_r <= v` (due to + /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and + /// `sub_r <= sup_r` does not hold. + SubSupConflict( + RegionVariableOrigin, + SubregionOrigin<'tcx>, + Region<'tcx>, + SubregionOrigin<'tcx>, + Region<'tcx>, + ), +} + struct RegionAndOrigin<'tcx> { region: Region<'tcx>, origin: SubregionOrigin<'tcx>, @@ -327,7 +355,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { sup ); - errors.push(RegionResolutionError::ConcreteFailure((*origin).clone(), sub, sup)); + errors.push(RegionResolutionError::ConcreteFailure( + (*origin).clone(), + sub, + sup, + )); } Constraint::VarSubReg(a_vid, b_region) => { diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 173b86efc6737..0b7acac6c13fa 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -141,31 +141,6 @@ enum CombineMapType { Glb, } -#[derive(Clone, Debug)] -pub enum RegionResolutionError<'tcx> { - /// `ConcreteFailure(o, a, b)`: - /// - /// `o` requires that `a <= b`, but this does not hold - ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - - /// `GenericBoundFailure(p, s, a) - /// - /// The parameter/associated-type `p` must be known to outlive the lifetime - /// `a` (but none of the known bounds are sufficient). - GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), - - /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: - /// - /// Could not infer a value for `v` because `sub_r <= v` (due to - /// `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and - /// `sub_r <= sup_r` does not hold. - SubSupConflict(RegionVariableOrigin, - SubregionOrigin<'tcx>, - Region<'tcx>, - SubregionOrigin<'tcx>, - Region<'tcx>), -} - #[derive(Copy, Clone, Debug)] pub enum VarValue<'tcx> { Value(Region<'tcx>), From 6ff84cf94707f0a22b47620baa83fb75aa59d308 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:48:16 -0500 Subject: [PATCH 22/68] extract storage of region values from `RegionVarBindings` --- .../infer/lexical_region_resolve/mod.rs | 142 +++++++++--------- src/librustc/infer/mod.rs | 10 +- src/librustc/infer/region_inference/mod.rs | 27 ---- src/librustc/infer/resolve.rs | 6 +- src/librustc/lib.rs | 2 + 5 files changed, 86 insertions(+), 101 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 217387befd89f..7dbb5b1ff11ae 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -15,20 +15,30 @@ use infer::RegionVariableOrigin; use infer::region_inference::Constraint; use infer::region_inference::GenericKind; use infer::region_inference::RegionVarBindings; -use infer::region_inference::VarValue; use infer::region_inference::VerifyBound; use middle::free_region::RegionRelations; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; use std::fmt; use std::u32; -use ty::{self, TyCtxt}; +use ty; use ty::{Region, RegionVid}; use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; mod graphviz; +pub struct LexicalRegionResolutions<'tcx> { + values: Vec>, + error_region: ty::Region<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +enum VarValue<'tcx> { + Value(Region<'tcx>), + ErrorValue, +} + #[derive(Clone, Debug)] pub enum RegionResolutionError<'tcx> { /// `ConcreteFailure(o, a, b)`: @@ -72,27 +82,14 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { pub fn resolve_regions( &self, region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - ) -> Vec> { + ) -> ( + LexicalRegionResolutions<'tcx>, + Vec>, + ) { debug!("RegionVarBindings: resolve_regions()"); let mut errors = vec![]; - let v = self.infer_variable_values(region_rels, &mut errors); - *self.values.borrow_mut() = Some(v); - errors - } - - pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - match *self.values.borrow() { - None => span_bug!( - (*self.var_origins.borrow())[rid.index as usize].span(), - "attempt to resolve region variable before values have \ - been computed!" - ), - Some(ref values) => { - let r = lookup(self.tcx, values, rid); - debug!("resolve_var({:?}) = {:?}", rid, r); - r - } - } + let values = self.infer_variable_values(region_rels, &mut errors); + (values, errors) } fn lub_concrete_regions( @@ -188,7 +185,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { &self, region_rels: &RegionRelations<'a, 'gcx, 'tcx>, errors: &mut Vec>, - ) -> Vec> { + ) -> LexicalRegionResolutions<'tcx> { let mut var_data = self.construct_var_data(); // Dorky hack to cause `dump_constraints` to only get called @@ -208,10 +205,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { var_data } - fn construct_var_data(&self) -> Vec> { - (0..self.num_vars() as usize) - .map(|_| VarValue::Value(self.tcx.types.re_empty)) - .collect() + fn construct_var_data(&self) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + error_region: self.tcx.types.re_static, + values: (0..self.num_vars() as usize) + .map(|_| VarValue::Value(self.tcx.types.re_empty)) + .collect(), + } } fn dump_constraints(&self, free_regions: &RegionRelations<'a, 'gcx, 'tcx>) { @@ -252,19 +252,19 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn expansion( &self, region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &mut [VarValue<'tcx>], + var_values: &mut LexicalRegionResolutions<'tcx>, ) { self.iterate_until_fixed_point("Expansion", |constraint, origin| { debug!("expansion: constraint={:?} origin={:?}", constraint, origin); match *constraint { Constraint::RegSubVar(a_region, b_vid) => { - let b_data = &mut var_values[b_vid.index as usize]; + let b_data = var_values.value_mut(b_vid); self.expand_node(region_rels, a_region, b_vid, b_data) } - Constraint::VarSubVar(a_vid, b_vid) => match var_values[a_vid.index as usize] { + Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) { VarValue::ErrorValue => false, VarValue::Value(a_region) => { - let b_node = &mut var_values[b_vid.index as usize]; + let b_node = var_values.value_mut(b_vid); self.expand_node(region_rels, a_region, b_vid, b_node) } }, @@ -327,7 +327,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn collect_errors( &self, region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &mut Vec>, + var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec>, ) { let constraints = self.constraints.borrow(); @@ -363,7 +363,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } Constraint::VarSubReg(a_vid, b_region) => { - let a_data = &mut var_data[a_vid.index as usize]; + let a_data = var_data.value_mut(a_vid); debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region); let a_region = match *a_data { @@ -391,7 +391,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { for verify in self.verifys.borrow().iter() { debug!("collect_errors: verify={:?}", verify); - let sub = normalize(self.tcx, var_data, verify.region); + let sub = var_data.normalize(verify.region); // This was an inference variable which didn't get // constrained, therefore it can be assume to hold. @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn collect_var_errors( &self, region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_data: &[VarValue<'tcx>], + var_data: &LexicalRegionResolutions<'tcx>, graph: &RegionGraph<'tcx>, errors: &mut Vec>, ) { @@ -443,8 +443,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // overlapping locations. let mut dup_vec = vec![u32::MAX; self.num_vars() as usize]; - for idx in 0..self.num_vars() as usize { - match var_data[idx] { + for index in 0..self.num_vars() { + let node_vid = RegionVid { index }; + match var_data.value(node_vid) { VarValue::Value(_) => { /* Inference successful */ } VarValue::ErrorValue => { /* Inference impossible, this value contains @@ -469,8 +470,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { that is not used is not a problem, so if this rule starts to create problems we'll have to revisit this portion of the code and think hard about it. =) */ - - let node_vid = RegionVid { index: idx as u32 }; self.collect_error_for_expanding_node( region_rels, graph, @@ -704,28 +703,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } -fn normalize<'a, 'gcx, 'tcx>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - r: ty::Region<'tcx>, -) -> ty::Region<'tcx> { - match *r { - ty::ReVar(rid) => lookup(tcx, values, rid), - _ => r, - } -} - -fn lookup<'a, 'gcx, 'tcx>( - tcx: TyCtxt<'a, 'gcx, 'tcx>, - values: &Vec>, - rid: ty::RegionVid, -) -> ty::Region<'tcx> { - match values[rid.index as usize] { - VarValue::Value(r) => r, - VarValue::ErrorValue => tcx.types.re_static, // Previously reported error. - } -} - impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) @@ -737,26 +714,47 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { fn is_met( &self, region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - var_values: &Vec>, + var_values: &LexicalRegionResolutions<'tcx>, min: ty::Region<'tcx>, ) -> bool { - let tcx = region_rels.tcx; match self { - &VerifyBound::AnyRegion(ref rs) => rs.iter() - .map(|&r| normalize(tcx, var_values, r)) + VerifyBound::AnyRegion(rs) => rs.iter() + .map(|&r| var_values.normalize(r)) .any(|r| region_rels.is_subregion_of(min, r)), - &VerifyBound::AllRegions(ref rs) => rs.iter() - .map(|&r| normalize(tcx, var_values, r)) + VerifyBound::AllRegions(rs) => rs.iter() + .map(|&r| var_values.normalize(r)) .all(|r| region_rels.is_subregion_of(min, r)), - &VerifyBound::AnyBound(ref bs) => { - bs.iter().any(|b| b.is_met(region_rels, var_values, min)) - } + VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.is_met(region_rels, var_values, min)), - &VerifyBound::AllBounds(ref bs) => { - bs.iter().all(|b| b.is_met(region_rels, var_values, min)) - } + VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.is_met(region_rels, var_values, min)), + } + } +} + +impl<'tcx> LexicalRegionResolutions<'tcx> { + fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(rid) => self.resolve_var(rid), + _ => r, } } + + fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { + &self.values[rid.index as usize] + } + + fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> { + &mut self.values[rid.index as usize] + } + + pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { + let result = match self.values[rid.index as usize] { + VarValue::Value(r) => r, + VarValue::ErrorValue => self.error_region, + }; + debug!("resolve_var({:?}) = {:?}", rid, result); + result + } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index cf32113343df3..d50d31d34bb8d 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -42,6 +42,7 @@ use arena::DroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; use self::region_inference::{RegionVarBindings, RegionSnapshot}; +use self::lexical_region_resolve::LexicalRegionResolutions; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -105,6 +106,9 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // For region variables. region_vars: RegionVarBindings<'a, 'gcx, 'tcx>, + // Once region inference is done, the values for each variable. + lexical_region_resolutions: RefCell>>, + /// Caches the results of trait selection. This cache is used /// for things that have to do with the parameters in scope. pub selection_cache: traits::SelectionCache<'tcx>, @@ -421,6 +425,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), region_vars: RegionVarBindings::new(tcx), + lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), reported_trait_errors: RefCell::new(FxHashMap()), @@ -1123,7 +1128,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context, region_map, free_regions); - let errors = self.region_vars.resolve_regions(®ion_rels); + let (lexical_region_resolutions, errors) = self.region_vars.resolve_regions(®ion_rels); + + let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + assert!(old_value.is_none()); if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 0b7acac6c13fa..75593c3ffcc8f 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -141,12 +141,6 @@ enum CombineMapType { Glb, } -#[derive(Copy, Clone, Debug)] -pub enum VarValue<'tcx> { - Value(Region<'tcx>), - ErrorValue, -} - type CombineMap<'tcx> = FxHashMap, RegionVid>; pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { @@ -201,10 +195,6 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { undo_log: RefCell>>, unification_table: RefCell>, - - /// This contains the results of inference. It begins as an empty - /// option and only acquires a value after inference is complete. - pub(in infer) values: RefCell>>>, } pub struct RegionSnapshot { @@ -243,7 +233,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { RegionVarBindings { tcx, var_origins: RefCell::new(Vec::new()), - values: RefCell::new(None), constraints: RefCell::new(FxHashMap()), verifys: RefCell::new(Vec::new()), givens: RefCell::new(FxHashSet()), @@ -510,14 +499,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { self.tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) } - fn values_are_none(&self) -> bool { - self.values.borrow().is_none() - } - fn add_constraint(&self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - debug!("RegionVarBindings: add_constraint({:?})", constraint); if self.constraints.borrow_mut().insert(constraint, origin).is_none() { @@ -529,8 +512,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn add_verify(&self, verify: Verify<'tcx>) { // cannot add verifys once regions are resolved - assert!(self.values_are_none()); - debug!("RegionVarBindings: add_verify({:?})", verify); // skip no-op cases known to be satisfied @@ -549,8 +530,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { pub fn add_given(&self, sub: Region<'tcx>, sup: ty::RegionVid) { // cannot add givens once regions are resolved - assert!(self.values_are_none()); - let mut givens = self.givens.borrow_mut(); if givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); @@ -580,8 +559,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { sub: Region<'tcx>, sup: Region<'tcx>) { // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - debug!("RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}", sub, sup, @@ -633,8 +610,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { b: Region<'tcx>) -> Region<'tcx> { // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); match (a, b) { (r @ &ReStatic, _) | (_, r @ &ReStatic) => { @@ -659,8 +634,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { b: Region<'tcx>) -> Region<'tcx> { // cannot add constraints once regions are resolved - assert!(self.values_are_none()); - debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); match (a, b) { (&ReStatic, r) | (r, &ReStatic) => { diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 10899e42afb81..f01be0cd9e4a9 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -185,7 +185,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for FullTypeResolver<'a, 'gcx, 'tcx> fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.resolve_var(rid), + ty::ReVar(rid) => self.infcx.lexical_region_resolutions + .borrow() + .as_ref() + .expect("region resolution not performed") + .resolve_var(rid), _ => r, } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 498e1aa3520d5..31c09c4cd5756 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -46,12 +46,14 @@ #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(i128_type)] +#![feature(match_default_bindings)] #![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] #![feature(macro_vis_matcher)] #![feature(never_type)] #![feature(nonzero)] #![feature(quote)] +#![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] #![feature(specialization)] From e0840728f5d9c68dc81ac244c6cea5f8dec21dbc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:52:59 -0500 Subject: [PATCH 23/68] region_inference: rustfmt --- src/librustc/infer/region_inference/mod.rs | 326 ++++++++++++--------- 1 file changed, 184 insertions(+), 142 deletions(-) diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 75593c3ffcc8f..f57ec7f7c4312 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -13,7 +13,7 @@ use self::UndoLogEntry::*; use self::CombineMapType::*; -use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable}; +use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; use super::unify_key; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -21,7 +21,7 @@ use rustc_data_structures::unify::{self, UnificationTable}; use ty::{self, Ty, TyCtxt}; use ty::{Region, RegionVid}; use ty::ReStatic; -use ty::{ReLateBound, ReVar, ReSkolemized, BrFresh}; +use ty::{BrFresh, ReLateBound, ReSkolemized, ReVar}; use std::cell::{Cell, RefCell}; use std::fmt; @@ -143,7 +143,7 @@ enum CombineMapType { type CombineMap<'tcx> = FxHashMap, RegionVid>; -pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +pub struct RegionVarBindings<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { pub(in infer) tcx: TyCtxt<'a, 'gcx, 'tcx>, pub(in infer) var_origins: RefCell>, @@ -216,15 +216,24 @@ pub struct TaintDirections { impl TaintDirections { pub fn incoming() -> Self { - TaintDirections { incoming: true, outgoing: false } + TaintDirections { + incoming: true, + outgoing: false, + } } pub fn outgoing() -> Self { - TaintDirections { incoming: false, outgoing: true } + TaintDirections { + incoming: false, + outgoing: true, + } } pub fn both() -> Self { - TaintDirections { incoming: true, outgoing: true } + TaintDirections { + incoming: true, + outgoing: true, + } } } @@ -264,10 +273,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { debug!("RegionVarBindings: commit({})", snapshot.length); assert!(self.undo_log.borrow().len() > snapshot.length); assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot); - assert!(self.skolemization_count.get() == snapshot.skolemization_count, - "failed to pop skolemized regions: {} now vs {} at start", - self.skolemization_count.get(), - snapshot.skolemization_count); + assert!( + self.skolemization_count.get() == snapshot.skolemization_count, + "failed to pop skolemized regions: {} now vs {} at start", + self.skolemization_count.get(), + snapshot.skolemization_count + ); let mut undo_log = self.undo_log.borrow_mut(); if snapshot.length == 0 { @@ -275,7 +286,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } else { (*undo_log)[snapshot.length] = CommitedSnapshot; } - self.unification_table.borrow_mut().commit(snapshot.region_snapshot); + self.unification_table + .borrow_mut() + .commit(snapshot.region_snapshot); } pub fn rollback_to(&self, snapshot: RegionSnapshot) { @@ -289,7 +302,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { let c = undo_log.pop().unwrap(); assert!(c == OpenSnapshot); self.skolemization_count.set(snapshot.skolemization_count); - self.unification_table.borrow_mut() + self.unification_table + .borrow_mut() .rollback_to(snapshot.region_snapshot); } @@ -333,19 +347,23 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid { - let vid = RegionVid { index: self.num_vars() }; + let vid = RegionVid { + index: self.num_vars(), + }; self.var_origins.borrow_mut().push(origin.clone()); - let u_vid = self.unification_table.borrow_mut().new_key( - unify_key::RegionVidKey { min_vid: vid } - ); + let u_vid = self.unification_table + .borrow_mut() + .new_key(unify_key::RegionVidKey { min_vid: vid }); assert_eq!(vid, u_vid); if self.in_snapshot() { self.undo_log.borrow_mut().push(AddVar(vid)); } - debug!("created new region variable {:?} with origin {:?}", - vid, - origin); + debug!( + "created new region variable {:?} with origin {:?}", + vid, + origin + ); return vid; } @@ -372,42 +390,44 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// The `snapshot` argument to this function is not really used; /// it's just there to make it explicit which snapshot bounds the /// skolemized region that results. It should always be the top-most snapshot. - pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) - -> Region<'tcx> { + pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) -> Region<'tcx> { assert!(self.in_snapshot()); assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); let sc = self.skolemization_count.get(); self.skolemization_count.set(sc + 1); - self.tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) + self.tcx + .mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) } /// Removes all the edges to/from the skolemized regions that are /// in `skols`. This is used after a higher-ranked operation /// completes to remove all trace of the skolemized regions /// created in that time. - pub fn pop_skolemized(&self, - skols: &FxHashSet>, - snapshot: &RegionSnapshot) { + pub fn pop_skolemized(&self, skols: &FxHashSet>, snapshot: &RegionSnapshot) { debug!("pop_skolemized_regions(skols={:?})", skols); assert!(self.in_snapshot()); assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); - assert!(self.skolemization_count.get() as usize >= skols.len(), - "popping more skolemized variables than actually exist, \ - sc now = {}, skols.len = {}", - self.skolemization_count.get(), - skols.len()); + assert!( + self.skolemization_count.get() as usize >= skols.len(), + "popping more skolemized variables than actually exist, \ + sc now = {}, skols.len = {}", + self.skolemization_count.get(), + skols.len() + ); let last_to_pop = self.skolemization_count.get(); let first_to_pop = last_to_pop - (skols.len() as u32); - assert!(first_to_pop >= snapshot.skolemization_count, - "popping more regions than snapshot contains, \ - sc now = {}, sc then = {}, skols.len = {}", - self.skolemization_count.get(), - snapshot.skolemization_count, - skols.len()); + assert!( + first_to_pop >= snapshot.skolemization_count, + "popping more regions than snapshot contains, \ + sc now = {}, sc then = {}, skols.len = {}", + self.skolemization_count.get(), + snapshot.skolemization_count, + skols.len() + ); debug_assert! { skols.iter() .all(|&k| match *k { @@ -425,13 +445,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { let mut undo_log = self.undo_log.borrow_mut(); - let constraints_to_kill: Vec = - undo_log.iter() - .enumerate() - .rev() - .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry)) - .map(|(index, _)| index) - .collect(); + let constraints_to_kill: Vec = undo_log + .iter() + .enumerate() + .rev() + .filter(|&(_, undo_entry)| kill_constraint(skols, undo_entry)) + .map(|(index, _)| index) + .collect(); for index in constraints_to_kill { let undo_entry = mem::replace(&mut undo_log[index], Purged); @@ -441,33 +461,25 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { self.skolemization_count.set(snapshot.skolemization_count); return; - fn kill_constraint<'tcx>(skols: &FxHashSet>, - undo_entry: &UndoLogEntry<'tcx>) - -> bool { + fn kill_constraint<'tcx>( + skols: &FxHashSet>, + undo_entry: &UndoLogEntry<'tcx>, + ) -> bool { match undo_entry { - &AddConstraint(Constraint::VarSubVar(..)) => - false, - &AddConstraint(Constraint::RegSubVar(a, _)) => - skols.contains(&a), - &AddConstraint(Constraint::VarSubReg(_, b)) => - skols.contains(&b), - &AddConstraint(Constraint::RegSubReg(a, b)) => - skols.contains(&a) || skols.contains(&b), - &AddGiven(..) => - false, - &AddVerify(_) => - false, - &AddCombination(_, ref two_regions) => - skols.contains(&two_regions.a) || - skols.contains(&two_regions.b), - &AddVar(..) | - &OpenSnapshot | - &Purged | - &CommitedSnapshot => - false, + &AddConstraint(Constraint::VarSubVar(..)) => false, + &AddConstraint(Constraint::RegSubVar(a, _)) => skols.contains(&a), + &AddConstraint(Constraint::VarSubReg(_, b)) => skols.contains(&b), + &AddConstraint(Constraint::RegSubReg(a, b)) => { + skols.contains(&a) || skols.contains(&b) + } + &AddGiven(..) => false, + &AddVerify(_) => false, + &AddCombination(_, ref two_regions) => { + skols.contains(&two_regions.a) || skols.contains(&two_regions.b) + } + &AddVar(..) | &OpenSnapshot | &Purged | &CommitedSnapshot => false, } } - } pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region<'tcx> { @@ -503,7 +515,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // cannot add constraints once regions are resolved debug!("RegionVarBindings: add_constraint({:?})", constraint); - if self.constraints.borrow_mut().insert(constraint, origin).is_none() { + if self.constraints + .borrow_mut() + .insert(constraint, origin) + .is_none() + { if self.in_snapshot() { self.undo_log.borrow_mut().push(AddConstraint(constraint)); } @@ -516,8 +532,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // skip no-op cases known to be satisfied match verify.bound { - VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { return; } - _ => { } + VerifyBound::AllBounds(ref bs) if bs.len() == 0 => { + return; + } + _ => {} } let mut verifys = self.verifys.borrow_mut(); @@ -538,10 +556,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - pub fn make_eqregion(&self, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>) { + pub fn make_eqregion( + &self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { if sub != sup { // Eventually, it would be nice to add direct support for // equating regions. @@ -554,23 +574,28 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - pub fn make_subregion(&self, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>) { + pub fn make_subregion( + &self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { // cannot add constraints once regions are resolved - debug!("RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}", - sub, - sup, - origin); + debug!( + "RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}", + sub, + sup, + origin + ); match (sub, sup) { - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) => { - span_bug!(origin.span(), - "cannot relate bound region: {:?} <= {:?}", - sub, - sup); + (&ReLateBound(..), _) | (_, &ReLateBound(..)) => { + span_bug!( + origin.span(), + "cannot relate bound region: {:?} <= {:?}", + sub, + sup + ); } (_, &ReStatic) => { // all regions are subregions of static, so we can ignore this @@ -591,11 +616,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } /// See `Verify::VerifyGenericBound` - pub fn verify_generic_bound(&self, - origin: SubregionOrigin<'tcx>, - kind: GenericKind<'tcx>, - sub: Region<'tcx>, - bound: VerifyBound<'tcx>) { + pub fn verify_generic_bound( + &self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + sub: Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { self.add_verify(Verify { kind, origin, @@ -604,11 +631,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { }); } - pub fn lub_regions(&self, - origin: SubregionOrigin<'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { + pub fn lub_regions( + &self, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { // cannot add constraints once regions are resolved debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); match (a, b) { @@ -620,19 +648,22 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { a // LUB(a,a) = a } - _ => { - self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| { - this.make_subregion(origin.clone(), old_r, new_r) - }) - } + _ => self.combine_vars( + Lub, + a, + b, + origin.clone(), + |this, old_r, new_r| this.make_subregion(origin.clone(), old_r, new_r), + ), } } - pub fn glb_regions(&self, - origin: SubregionOrigin<'tcx>, - a: Region<'tcx>, - b: Region<'tcx>) - -> Region<'tcx> { + pub fn glb_regions( + &self, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { // cannot add constraints once regions are resolved debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); match (a, b) { @@ -644,11 +675,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { a // GLB(a,a) = a } - _ => { - self.combine_vars(Glb, a, b, origin.clone(), |this, old_r, new_r| { - this.make_subregion(origin.clone(), new_r, old_r) - }) - } + _ => self.combine_vars( + Glb, + a, + b, + origin.clone(), + |this, old_r, new_r| this.make_subregion(origin.clone(), new_r, old_r), + ), } } @@ -664,14 +697,16 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - fn combine_vars(&self, - t: CombineMapType, - a: Region<'tcx>, - b: Region<'tcx>, - origin: SubregionOrigin<'tcx>, - mut relate: F) - -> Region<'tcx> - where F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>) + fn combine_vars( + &self, + t: CombineMapType, + a: Region<'tcx>, + b: Region<'tcx>, + origin: SubregionOrigin<'tcx>, + mut relate: F, + ) -> Region<'tcx> + where + F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>), { let vars = TwoRegions { a: a, b: b }; if let Some(&c) = self.combine_map(t).borrow().get(&vars) { @@ -691,11 +726,9 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { self.undo_log.borrow()[mark.length..] .iter() - .filter_map(|&elt| { - match elt { - AddVar(vid) => Some(vid), - _ => None, - } + .filter_map(|&elt| match elt { + AddVar(vid) => Some(vid), + _ => None, }) .collect() } @@ -708,21 +741,28 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// get the set of regions `{r|r <= r0}`. This is used when /// checking whether skolemized regions are being improperly /// related to other regions. - pub fn tainted(&self, - mark: &RegionSnapshot, - r0: Region<'tcx>, - directions: TaintDirections) - -> FxHashSet> { - debug!("tainted(mark={:?}, r0={:?}, directions={:?})", - mark, r0, directions); + pub fn tainted( + &self, + mark: &RegionSnapshot, + r0: Region<'tcx>, + directions: TaintDirections, + ) -> FxHashSet> { + debug!( + "tainted(mark={:?}, r0={:?}, directions={:?})", + mark, + r0, + directions + ); // `result_set` acts as a worklist: we explore all outgoing // edges and add any new regions we find to result_set. This // is not a terribly efficient implementation. let mut taint_set = taint::TaintSet::new(directions, r0); - taint_set.fixed_point(self.tcx, - &self.undo_log.borrow()[mark.length..], - &self.verifys.borrow()); + taint_set.fixed_point( + self.tcx, + &self.undo_log.borrow()[mark.length..], + &self.verifys.borrow(), + ); debug!("tainted: result={:?}", taint_set); return taint_set.into_set(); } @@ -730,8 +770,12 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { impl fmt::Debug for RegionSnapshot { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionSnapshot(length={},skolemization={})", - self.length, self.skolemization_count) + write!( + f, + "RegionSnapshot(length={},skolemization={})", + self.length, + self.skolemization_count + ) } } @@ -765,13 +809,11 @@ impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { fn for_each_region(&self, f: &mut FnMut(ty::Region<'tcx>)) { match self { - &VerifyBound::AnyRegion(ref rs) | - &VerifyBound::AllRegions(ref rs) => for &r in rs { + &VerifyBound::AnyRegion(ref rs) | &VerifyBound::AllRegions(ref rs) => for &r in rs { f(r); }, - &VerifyBound::AnyBound(ref bs) | - &VerifyBound::AllBounds(ref bs) => for b in bs { + &VerifyBound::AnyBound(ref bs) | &VerifyBound::AllBounds(ref bs) => for b in bs { b.for_each_region(f); }, } From 4861c79add268911495805981539c44276d64d41 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:07:22 -0500 Subject: [PATCH 24/68] extract the `tcx` out from `RegionVarBindings` --- src/librustc/infer/glb.rs | 2 +- src/librustc/infer/higher_ranked/mod.rs | 8 +- .../infer/lexical_region_resolve/graphviz.rs | 7 +- .../infer/lexical_region_resolve/mod.rs | 51 +++++------ src/librustc/infer/lub.rs | 2 +- src/librustc/infer/mod.rs | 8 +- src/librustc/infer/region_inference/mod.rs | 84 ++++++++++--------- src/librustc/infer/region_inference/taint.rs | 4 +- src/librustc/infer/resolve.rs | 2 +- src/librustc/lib.rs | 1 + 10 files changed, 89 insertions(+), 80 deletions(-) diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index d7afeba7dc96b..9b32c1ff385bd 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.glb_regions(origin, a, b)) + Ok(self.fields.infcx.region_vars.glb_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 6736751a5a2c2..bf0694976ca98 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -427,7 +427,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - infcx.region_vars.new_bound(debruijn) + infcx.region_vars.new_bound(infcx.tcx, debruijn) } } } @@ -481,7 +481,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r: ty::Region<'tcx>, directions: TaintDirections) -> FxHashSet> { - self.region_vars.tainted(&snapshot.region_vars_snapshot, r, directions) + self.region_vars.tainted(self.tcx, &snapshot.region_vars_snapshot, r, directions) } fn region_vars_confined_to_snapshot(&self, @@ -581,7 +581,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { where T : TypeFoldable<'tcx> { let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.region_vars.push_skolemized(br, &snapshot.region_vars_snapshot) + self.region_vars.push_skolemized(self.tcx, br, &snapshot.region_vars_snapshot) }); debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})", @@ -766,7 +766,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { debug!("pop_skolemized({:?})", skol_map); let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect(); - self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot); + self.region_vars.pop_skolemized(self.tcx, &skol_regions, &snapshot.region_vars_snapshot); if !skol_map.is_empty() { self.projection_cache.borrow_mut().rollback_skolemized( &snapshot.projection_cache_snapshot); diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index 90d6211597a86..6ad451c15fbaf 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -56,12 +56,13 @@ graphs will be printed. \n\ } pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( - region_vars: &RegionVarBindings<'a, 'gcx, 'tcx>, + region_vars: &RegionVarBindings<'tcx>, region_rels: &RegionRelations<'a, 'gcx, 'tcx>) { + let tcx = region_rels.tcx; let context = region_rels.context; - if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph { + if !tcx.sess.opts.debugging_opts.print_region_graph { return; } @@ -116,7 +117,7 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( Ok(()) => {} Err(e) => { let msg = format!("io error dumping region constraints: {}", e); - region_vars.tcx.sess.err(&msg) + tcx.sess.err(&msg) } } } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 7dbb5b1ff11ae..f15d4785da9c7 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -21,7 +21,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; use std::fmt; use std::u32; -use ty; +use ty::{self, TyCtxt}; use ty::{Region, RegionVid}; use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; @@ -73,7 +73,7 @@ struct RegionAndOrigin<'tcx> { type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { +impl<'tcx> RegionVarBindings<'tcx> { /// This function performs the actual region resolution. It must be /// called after all constraints have been added. It performs a /// fixed-point iteration to find region values which satisfy all @@ -81,7 +81,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// errors are reported. pub fn resolve_regions( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, ) -> ( LexicalRegionResolutions<'tcx>, Vec>, @@ -94,10 +94,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn lub_concrete_regions( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, a: Region<'tcx>, b: Region<'tcx>, ) -> Region<'tcx> { + let tcx = region_rels.tcx; match (a, b) { (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); @@ -130,10 +131,10 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // reasonably compare free regions and scopes: let fr_scope = match (a, b) { (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - region_rels.region_scope_tree.early_free_scope(self.tcx, br) + region_rels.region_scope_tree.early_free_scope(region_rels.tcx, br) } (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - region_rels.region_scope_tree.free_scope(self.tcx, fr) + region_rels.region_scope_tree.free_scope(region_rels.tcx, fr) } _ => bug!(), }; @@ -153,7 +154,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // otherwise, we don't know what the free region is, // so we must conservatively say the LUB is static: - self.tcx.types.re_static + tcx.types.re_static } (&ReScope(a_id), &ReScope(b_id)) => { @@ -163,7 +164,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { let lub = region_rels .region_scope_tree .nearest_common_ancestor(a_id, b_id); - self.tcx.mk_region(ReScope(lub)) + tcx.mk_region(ReScope(lub)) } (&ReEarlyBound(_), &ReEarlyBound(_)) | @@ -176,17 +177,17 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b { a } else { - self.tcx.types.re_static + tcx.types.re_static }, } } fn infer_variable_values( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, errors: &mut Vec>, ) -> LexicalRegionResolutions<'tcx> { - let mut var_data = self.construct_var_data(); + let mut var_data = self.construct_var_data(region_rels.tcx); // Dorky hack to cause `dump_constraints` to only get called // if debug mode is enabled: @@ -205,16 +206,18 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { var_data } - fn construct_var_data(&self) -> LexicalRegionResolutions<'tcx> { + /// Initially, the value for all variables is set to `'empty`, the + /// empty region. The `expansion` phase will grow this larger. + fn construct_var_data(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> LexicalRegionResolutions<'tcx> { LexicalRegionResolutions { - error_region: self.tcx.types.re_static, + error_region: tcx.types.re_static, values: (0..self.num_vars() as usize) - .map(|_| VarValue::Value(self.tcx.types.re_empty)) + .map(|_| VarValue::Value(tcx.types.re_empty)) .collect(), } } - fn dump_constraints(&self, free_regions: &RegionRelations<'a, 'gcx, 'tcx>) { + fn dump_constraints(&self, free_regions: &RegionRelations<'_, '_, 'tcx>) { debug!( "----() Start constraint listing (context={:?}) ()----", free_regions.context @@ -251,7 +254,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn expansion( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) { self.iterate_until_fixed_point("Expansion", |constraint, origin| { @@ -279,7 +282,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn expand_node( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, a_region: Region<'tcx>, b_vid: RegionVid, b_data: &mut VarValue<'tcx>, @@ -326,7 +329,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// and check that they are satisfied. fn collect_errors( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec>, ) { @@ -423,7 +426,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// and create a `RegionResolutionError` for each of them. fn collect_var_errors( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, var_data: &LexicalRegionResolutions<'tcx>, graph: &RegionGraph<'tcx>, errors: &mut Vec>, @@ -528,7 +531,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { fn collect_error_for_expanding_node( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, graph: &RegionGraph<'tcx>, dup_vec: &mut [u32], node_idx: RegionVid, @@ -642,8 +645,8 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } = state; return (result, dup_found); - fn process_edges<'a, 'gcx, 'tcx>( - this: &RegionVarBindings<'a, 'gcx, 'tcx>, + fn process_edges<'tcx>( + this: &RegionVarBindings<'tcx>, state: &mut WalkState<'tcx>, graph: &RegionGraph<'tcx>, source_vid: RegionVid, @@ -710,10 +713,10 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { } -impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { +impl<'tcx> VerifyBound<'tcx> { fn is_met( &self, - region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + region_rels: &RegionRelations<'_, '_, 'tcx>, var_values: &LexicalRegionResolutions<'tcx>, min: ty::Region<'tcx>, ) -> bool { diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 04b470b29fc5e..68cecf216d8a7 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.lub_regions(origin, a, b)) + Ok(self.fields.infcx.region_vars.lub_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d50d31d34bb8d..b176646ac5fb3 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -104,7 +104,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { float_unification_table: RefCell>, // For region variables. - region_vars: RegionVarBindings<'a, 'gcx, 'tcx>, + region_vars: RegionVarBindings<'tcx>, // Once region inference is done, the values for each variable. lexical_region_resolutions: RefCell>>, @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), - region_vars: RegionVarBindings::new(tcx), + region_vars: RegionVarBindings::new(), lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), @@ -1087,10 +1087,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }) } - pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - self.region_vars.new_bound(debruijn) - } - /// True if errors have been reported since this infcx was /// created. This is sometimes used as a heuristic to skip /// reporting errors that often occur as a result of earlier diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index f57ec7f7c4312..6085c6038a52b 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -143,8 +143,7 @@ enum CombineMapType { type CombineMap<'tcx> = FxHashMap, RegionVid>; -pub struct RegionVarBindings<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - pub(in infer) tcx: TyCtxt<'a, 'gcx, 'tcx>, +pub struct RegionVarBindings<'tcx> { pub(in infer) var_origins: RefCell>, /// Constraints of the form `A <= B` introduced by the region @@ -237,10 +236,9 @@ impl TaintDirections { } } -impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> RegionVarBindings<'a, 'gcx, 'tcx> { +impl<'tcx> RegionVarBindings<'tcx> { + pub fn new() -> RegionVarBindings<'tcx> { RegionVarBindings { - tcx, var_origins: RefCell::new(Vec::new()), constraints: RefCell::new(FxHashMap()), verifys: RefCell::new(Vec::new()), @@ -390,21 +388,30 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// The `snapshot` argument to this function is not really used; /// it's just there to make it explicit which snapshot bounds the /// skolemized region that results. It should always be the top-most snapshot. - pub fn push_skolemized(&self, br: ty::BoundRegion, snapshot: &RegionSnapshot) -> Region<'tcx> { + pub fn push_skolemized( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + br: ty::BoundRegion, + snapshot: &RegionSnapshot, + ) -> Region<'tcx> { assert!(self.in_snapshot()); assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); let sc = self.skolemization_count.get(); self.skolemization_count.set(sc + 1); - self.tcx - .mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) + tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) } /// Removes all the edges to/from the skolemized regions that are /// in `skols`. This is used after a higher-ranked operation /// completes to remove all trace of the skolemized regions /// created in that time. - pub fn pop_skolemized(&self, skols: &FxHashSet>, snapshot: &RegionSnapshot) { + pub fn pop_skolemized( + &self, + _tcx: TyCtxt<'_, '_, 'tcx>, + skols: &FxHashSet>, + snapshot: &RegionSnapshot, + ) { debug!("pop_skolemized_regions(skols={:?})", skols); assert!(self.in_snapshot()); @@ -482,7 +489,11 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - pub fn new_bound(&self, debruijn: ty::DebruijnIndex) -> Region<'tcx> { + pub fn new_bound( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + debruijn: ty::DebruijnIndex, + ) -> Region<'tcx> { // Creates a fresh bound variable for use in GLB computations. // See discussion of GLB computation in the large comment at // the top of this file for more details. @@ -508,7 +519,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { bug!("rollover in RegionInference new_bound()"); } - self.tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) + tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) } fn add_constraint(&self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { @@ -633,6 +644,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { pub fn lub_regions( &self, + tcx: TyCtxt<'_, '_, 'tcx>, origin: SubregionOrigin<'tcx>, a: Region<'tcx>, b: Region<'tcx>, @@ -648,18 +660,13 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { a // LUB(a,a) = a } - _ => self.combine_vars( - Lub, - a, - b, - origin.clone(), - |this, old_r, new_r| this.make_subregion(origin.clone(), old_r, new_r), - ), + _ => self.combine_vars(tcx, Lub, a, b, origin.clone()), } } pub fn glb_regions( &self, + tcx: TyCtxt<'_, '_, 'tcx>, origin: SubregionOrigin<'tcx>, a: Region<'tcx>, b: Region<'tcx>, @@ -675,19 +682,17 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { a // GLB(a,a) = a } - _ => self.combine_vars( - Glb, - a, - b, - origin.clone(), - |this, old_r, new_r| this.make_subregion(origin.clone(), new_r, old_r), - ), + _ => self.combine_vars(tcx, Glb, a, b, origin.clone()), } } - pub fn opportunistic_resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { + pub fn opportunistic_resolve_var( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + rid: RegionVid, + ) -> ty::Region<'tcx> { let vid = self.unification_table.borrow_mut().find_value(rid).min_vid; - self.tcx.mk_region(ty::ReVar(vid)) + tcx.mk_region(ty::ReVar(vid)) } fn combine_map(&self, t: CombineMapType) -> &RefCell> { @@ -697,30 +702,32 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { } } - fn combine_vars( + fn combine_vars( &self, + tcx: TyCtxt<'_, '_, 'tcx>, t: CombineMapType, a: Region<'tcx>, b: Region<'tcx>, origin: SubregionOrigin<'tcx>, - mut relate: F, - ) -> Region<'tcx> - where - F: FnMut(&RegionVarBindings<'a, 'gcx, 'tcx>, Region<'tcx>, Region<'tcx>), - { + ) -> Region<'tcx> { let vars = TwoRegions { a: a, b: b }; if let Some(&c) = self.combine_map(t).borrow().get(&vars) { - return self.tcx.mk_region(ReVar(c)); + return tcx.mk_region(ReVar(c)); } let c = self.new_region_var(MiscVariable(origin.span())); self.combine_map(t).borrow_mut().insert(vars, c); if self.in_snapshot() { self.undo_log.borrow_mut().push(AddCombination(t, vars)); } - relate(self, a, self.tcx.mk_region(ReVar(c))); - relate(self, b, self.tcx.mk_region(ReVar(c))); + let new_r = tcx.mk_region(ReVar(c)); + for &old_r in &[a, b] { + match t { + Glb => self.make_subregion(origin.clone(), new_r, old_r), + Lub => self.make_subregion(origin.clone(), old_r, new_r), + } + } debug!("combine_vars() c={:?}", c); - self.tcx.mk_region(ReVar(c)) + new_r } pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { @@ -743,6 +750,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { /// related to other regions. pub fn tainted( &self, + tcx: TyCtxt<'_, '_, 'tcx>, mark: &RegionSnapshot, r0: Region<'tcx>, directions: TaintDirections, @@ -759,7 +767,7 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // is not a terribly efficient implementation. let mut taint_set = taint::TaintSet::new(directions, r0); taint_set.fixed_point( - self.tcx, + tcx, &self.undo_log.borrow()[mark.length..], &self.verifys.borrow(), ); diff --git a/src/librustc/infer/region_inference/taint.rs b/src/librustc/infer/region_inference/taint.rs index acc930bfa3b11..ee45f7bd82801 100644 --- a/src/librustc/infer/region_inference/taint.rs +++ b/src/librustc/infer/region_inference/taint.rs @@ -16,7 +16,7 @@ pub(super) struct TaintSet<'tcx> { regions: FxHashSet> } -impl<'a, 'gcx, 'tcx> TaintSet<'tcx> { +impl<'tcx> TaintSet<'tcx> { pub(super) fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self { @@ -26,7 +26,7 @@ impl<'a, 'gcx, 'tcx> TaintSet<'tcx> { } pub(super) fn fixed_point(&mut self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, + tcx: TyCtxt<'_, '_, 'tcx>, undo_log: &[UndoLogEntry<'tcx>], verifys: &[Verify<'tcx>]) { let mut prev_len = 0; diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index f01be0cd9e4a9..b9501773e02b9 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -74,7 +74,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(rid), + ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(self.tcx(), rid), _ => r, } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 31c09c4cd5756..66113ffef3d65 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -58,6 +58,7 @@ #![feature(slice_patterns)] #![feature(specialization)] #![feature(unboxed_closures)] +#![feature(underscore_lifetimes)] #![feature(trace_macros)] #![feature(test)] #![feature(const_atomic_bool_new)] From 69da30c3b33a0deac85865e87f62a028c54c289b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:25:23 -0500 Subject: [PATCH 25/68] move refcells out from `RegionVarBindings` and up into `InferCtxt` --- src/librustc/infer/equate.rs | 2 +- src/librustc/infer/fudge.rs | 2 +- src/librustc/infer/glb.rs | 2 +- src/librustc/infer/higher_ranked/mod.rs | 20 +- .../infer/lexical_region_resolve/graphviz.rs | 3 +- .../infer/lexical_region_resolve/mod.rs | 34 ++- src/librustc/infer/lub.rs | 2 +- src/librustc/infer/mod.rs | 19 +- src/librustc/infer/region_inference/mod.rs | 211 +++++++++--------- src/librustc/infer/resolve.rs | 6 +- src/librustc/infer/sub.rs | 2 +- 11 files changed, 148 insertions(+), 155 deletions(-) diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index f9ffaee81f157..dbe3905a8c619 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -104,7 +104,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> a, b); let origin = Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.make_eqregion(origin, a, b); + self.fields.infcx.region_vars.borrow_mut().make_eqregion(origin, a, b); Ok(a) } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 9cad6ce6f9fad..b6637069408e0 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -78,7 +78,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.type_variables.borrow_mut().types_created_since_snapshot( &snapshot.type_snapshot); let region_vars = - self.region_vars.vars_created_since_snapshot( + self.region_vars.borrow().vars_created_since_snapshot( &snapshot.region_vars_snapshot); Ok((type_variables, region_vars, value)) diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index 9b32c1ff385bd..c0e1369410616 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.glb_regions(self.tcx(), origin, a, b)) + Ok(self.fields.infcx.region_vars.borrow_mut().glb_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index bf0694976ca98..e2a214ed0cbfe 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -176,9 +176,9 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { .filter(|&r| r != representative) { let origin = SubregionOrigin::Subtype(self.trace.clone()); - self.infcx.region_vars.make_eqregion(origin, - *representative, - *region); + self.infcx.region_vars.borrow_mut().make_eqregion(origin, + *representative, + *region); } } @@ -427,7 +427,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - infcx.region_vars.new_bound(infcx.tcx, debruijn) + infcx.region_vars.borrow_mut().new_bound(infcx.tcx, debruijn) } } } @@ -481,7 +481,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r: ty::Region<'tcx>, directions: TaintDirections) -> FxHashSet> { - self.region_vars.tainted(self.tcx, &snapshot.region_vars_snapshot, r, directions) + self.region_vars.borrow().tainted(self.tcx, &snapshot.region_vars_snapshot, r, directions) } fn region_vars_confined_to_snapshot(&self, @@ -539,7 +539,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { */ let mut region_vars = - self.region_vars.vars_created_since_snapshot(&snapshot.region_vars_snapshot); + self.region_vars.borrow().vars_created_since_snapshot(&snapshot.region_vars_snapshot); let escaping_types = self.type_variables.borrow_mut().types_escaping_snapshot(&snapshot.type_snapshot); @@ -581,7 +581,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { where T : TypeFoldable<'tcx> { let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.region_vars.push_skolemized(self.tcx, br, &snapshot.region_vars_snapshot) + self.region_vars.borrow_mut().push_skolemized(self.tcx, + br, + &snapshot.region_vars_snapshot) }); debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})", @@ -766,7 +768,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { debug!("pop_skolemized({:?})", skol_map); let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect(); - self.region_vars.pop_skolemized(self.tcx, &skol_regions, &snapshot.region_vars_snapshot); + self.region_vars.borrow_mut().pop_skolemized(self.tcx, + &skol_regions, + &snapshot.region_vars_snapshot); if !skol_map.is_empty() { self.projection_cache.borrow_mut().rollback_skolemized( &snapshot.projection_cache_snapshot); diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index 6ad451c15fbaf..f5a0812853355 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -112,8 +112,7 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( } }; - let constraints = &*region_vars.constraints.borrow(); - match dump_region_constraints_to(region_rels, constraints, &output_path) { + match dump_region_constraints_to(region_rels, ®ion_vars.constraints, &output_path) { Ok(()) => {} Err(e) => { let msg = format!("io error dumping region constraints: {}", e); diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index f15d4785da9c7..ac3ba57d980c1 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -80,7 +80,7 @@ impl<'tcx> RegionVarBindings<'tcx> { /// constraints, assuming such values can be found; if they cannot, /// errors are reported. pub fn resolve_regions( - &self, + &mut self, region_rels: &RegionRelations<'_, '_, 'tcx>, ) -> ( LexicalRegionResolutions<'tcx>, @@ -114,7 +114,7 @@ impl<'tcx> RegionVarBindings<'tcx> { (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { span_bug!( - (*self.var_origins.borrow())[v_id.index as usize].span(), + self.var_origins[v_id.index as usize].span(), "lub_concrete_regions invoked with non-concrete \ regions: {:?}, {:?}", a, @@ -183,7 +183,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } fn infer_variable_values( - &self, + &mut self, region_rels: &RegionRelations<'_, '_, 'tcx>, errors: &mut Vec>, ) -> LexicalRegionResolutions<'tcx> { @@ -222,12 +222,12 @@ impl<'tcx> RegionVarBindings<'tcx> { "----() Start constraint listing (context={:?}) ()----", free_regions.context ); - for (idx, (constraint, _)) in self.constraints.borrow().iter().enumerate() { + for (idx, (constraint, _)) in self.constraints.iter().enumerate() { debug!("Constraint {} => {:?}", idx, constraint); } } - fn expand_givens(&self, graph: &RegionGraph) { + fn expand_givens(&mut self, graph: &RegionGraph) { // Givens are a kind of horrible hack to account for // constraints like 'c <= '0 that are known to hold due to // closure signatures (see the comment above on the `givens` @@ -238,15 +238,14 @@ impl<'tcx> RegionVarBindings<'tcx> { // and '0 <= '1 // then 'c <= '1 - let mut givens = self.givens.borrow_mut(); - let seeds: Vec<_> = givens.iter().cloned().collect(); + let seeds: Vec<_> = self.givens.iter().cloned().collect(); for (r, vid) in seeds { let seed_index = NodeIndex(vid.index as usize); for succ_index in graph.depth_traverse(seed_index, OUTGOING) { let succ_index = succ_index.0 as u32; if succ_index < self.num_vars() { let succ_vid = RegionVid { index: succ_index }; - givens.insert((r, succ_vid)); + self.givens.insert((r, succ_vid)); } } } @@ -292,7 +291,7 @@ impl<'tcx> RegionVarBindings<'tcx> { // Check if this relationship is implied by a given. match *a_region { ty::ReEarlyBound(_) | ty::ReFree(_) => { - if self.givens.borrow().contains(&(a_region, b_vid)) { + if self.givens.contains(&(a_region, b_vid)) { debug!("given"); return false; } @@ -333,8 +332,7 @@ impl<'tcx> RegionVarBindings<'tcx> { var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec>, ) { - let constraints = self.constraints.borrow(); - for (constraint, origin) in constraints.iter() { + for (constraint, origin) in &self.constraints { debug!( "collect_errors: constraint={:?} origin={:?}", constraint, @@ -392,7 +390,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } } - for verify in self.verifys.borrow().iter() { + for verify in &self.verifys { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(verify.region); @@ -488,8 +486,6 @@ impl<'tcx> RegionVarBindings<'tcx> { fn construct_graph(&self) -> RegionGraph<'tcx> { let num_vars = self.num_vars(); - let constraints = self.constraints.borrow(); - let mut graph = graph::Graph::new(); for _ in 0..num_vars { @@ -504,7 +500,7 @@ impl<'tcx> RegionVarBindings<'tcx> { let dummy_source = graph.add_node(()); let dummy_sink = graph.add_node(()); - for (constraint, _) in constraints.iter() { + for (constraint, _) in &self.constraints { match *constraint { Constraint::VarSubVar(a_id, b_id) => { graph.add_edge( @@ -564,7 +560,7 @@ impl<'tcx> RegionVarBindings<'tcx> { for lower_bound in &lower_bounds { for upper_bound in &upper_bounds { if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { - let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone(); + let origin = self.var_origins[node_idx.index as usize].clone(); debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ sup: {:?}", @@ -586,7 +582,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } span_bug!( - (*self.var_origins.borrow())[node_idx.index as usize].span(), + self.var_origins[node_idx.index as usize].span(), "collect_error_for_expanding_node() could not find \ error for var {:?}, lower_bounds={:?}, \ upper_bounds={:?}", @@ -671,7 +667,7 @@ impl<'tcx> RegionVarBindings<'tcx> { Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { state.result.push(RegionAndOrigin { region, - origin: this.constraints.borrow().get(&edge.data).unwrap().clone(), + origin: this.constraints.get(&edge.data).unwrap().clone(), }); } @@ -694,7 +690,7 @@ impl<'tcx> RegionVarBindings<'tcx> { changed = false; iteration += 1; debug!("---- {} Iteration {}{}", "#", tag, iteration); - for (constraint, origin) in self.constraints.borrow().iter() { + for (constraint, origin) in &self.constraints { let edge_changed = body(constraint, origin); if edge_changed { debug!("Updated due to constraint {:?}", constraint); diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 68cecf216d8a7..e3a16ecb444ad 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.lub_regions(self.tcx(), origin, a, b)) + Ok(self.fields.infcx.region_vars.borrow_mut().lub_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index b176646ac5fb3..4aef09aa935a5 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -104,7 +104,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { float_unification_table: RefCell>, // For region variables. - region_vars: RegionVarBindings<'tcx>, + region_vars: RefCell>, // Once region inference is done, the values for each variable. lexical_region_resolutions: RefCell>>, @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), - region_vars: RegionVarBindings::new(), + region_vars: RefCell::new(RegionVarBindings::new()), lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), @@ -767,7 +767,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot: self.type_variables.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), - region_vars_snapshot: self.region_vars.start_snapshot(), + region_vars_snapshot: self.region_vars.borrow_mut().start_snapshot(), was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. @@ -802,6 +802,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .borrow_mut() .rollback_to(float_snapshot); self.region_vars + .borrow_mut() .rollback_to(region_vars_snapshot); } @@ -830,6 +831,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .borrow_mut() .commit(float_snapshot); self.region_vars + .borrow_mut() .commit(region_vars_snapshot); } @@ -885,7 +887,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub: ty::Region<'tcx>, sup: ty::RegionVid) { - self.region_vars.add_given(sub, sup); + self.region_vars.borrow_mut().add_given(sub, sup); } pub fn can_sub(&self, @@ -925,7 +927,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a: ty::Region<'tcx>, b: ty::Region<'tcx>) { debug!("sub_regions({:?} <: {:?})", a, b); - self.region_vars.make_subregion(origin, a, b); + self.region_vars.borrow_mut().make_subregion(origin, a, b); } pub fn equality_predicate(&self, @@ -1028,7 +1030,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { - self.tcx.mk_region(ty::ReVar(self.region_vars.new_region_var(origin))) + self.tcx.mk_region(ty::ReVar(self.region_vars.borrow_mut().new_region_var(origin))) } /// Create a region inference variable for the given @@ -1124,7 +1126,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context, region_map, free_regions); - let (lexical_region_resolutions, errors) = self.region_vars.resolve_regions(®ion_rels); + let (lexical_region_resolutions, errors) = + self.region_vars.borrow_mut().resolve_regions(®ion_rels); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); @@ -1362,7 +1365,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a, bound); - self.region_vars.verify_generic_bound(origin, kind, a, bound); + self.region_vars.borrow_mut().verify_generic_bound(origin, kind, a, bound); } pub fn type_moves_by_default(&self, diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 6085c6038a52b..37699ccea0dd2 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -23,7 +23,6 @@ use ty::{Region, RegionVid}; use ty::ReStatic; use ty::{BrFresh, ReLateBound, ReSkolemized, ReVar}; -use std::cell::{Cell, RefCell}; use std::fmt; use std::mem; use std::u32; @@ -144,19 +143,19 @@ enum CombineMapType { type CombineMap<'tcx> = FxHashMap, RegionVid>; pub struct RegionVarBindings<'tcx> { - pub(in infer) var_origins: RefCell>, + pub(in infer) var_origins: Vec, /// Constraints of the form `A <= B` introduced by the region /// checker. Here at least one of `A` and `B` must be a region /// variable. - pub(in infer) constraints: RefCell, SubregionOrigin<'tcx>>>, + pub(in infer) constraints: FxHashMap, SubregionOrigin<'tcx>>, /// A "verify" is something that we need to verify after inference is /// done, but which does not directly affect inference in any way. /// /// An example is a `A <= B` where neither `A` nor `B` are /// inference variables. - pub(in infer) verifys: RefCell>>, + pub(in infer) verifys: Vec>, /// A "given" is a relationship that is known to hold. In particular, /// we often know from closure fn signatures that a particular free @@ -175,12 +174,12 @@ pub struct RegionVarBindings<'tcx> { /// record the fact that `'a <= 'b` is implied by the fn signature, /// and then ignore the constraint when solving equations. This is /// a bit of a hack but seems to work. - pub(in infer) givens: RefCell, ty::RegionVid)>>, + pub(in infer) givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>, - lubs: RefCell>, - glbs: RefCell>, - skolemization_count: Cell, - bound_count: Cell, + lubs: CombineMap<'tcx>, + glbs: CombineMap<'tcx>, + skolemization_count: u32, + bound_count: u32, /// The undo log records actions that might later be undone. /// @@ -191,9 +190,9 @@ pub struct RegionVarBindings<'tcx> { /// otherwise we end up adding entries for things like the lower /// bound on a variable and so forth, which can never be rolled /// back. - undo_log: RefCell>>, + undo_log: Vec>, - unification_table: RefCell>, + unification_table: UnificationTable, } pub struct RegionSnapshot { @@ -239,73 +238,70 @@ impl TaintDirections { impl<'tcx> RegionVarBindings<'tcx> { pub fn new() -> RegionVarBindings<'tcx> { RegionVarBindings { - var_origins: RefCell::new(Vec::new()), - constraints: RefCell::new(FxHashMap()), - verifys: RefCell::new(Vec::new()), - givens: RefCell::new(FxHashSet()), - lubs: RefCell::new(FxHashMap()), - glbs: RefCell::new(FxHashMap()), - skolemization_count: Cell::new(0), - bound_count: Cell::new(0), - undo_log: RefCell::new(Vec::new()), - unification_table: RefCell::new(UnificationTable::new()), + var_origins: Vec::new(), + constraints: FxHashMap(), + verifys: Vec::new(), + givens: FxHashSet(), + lubs: FxHashMap(), + glbs: FxHashMap(), + skolemization_count: 0, + bound_count: 0, + undo_log: Vec::new(), + unification_table: UnificationTable::new(), } } fn in_snapshot(&self) -> bool { - !self.undo_log.borrow().is_empty() + !self.undo_log.is_empty() } - pub fn start_snapshot(&self) -> RegionSnapshot { - let length = self.undo_log.borrow().len(); + pub fn start_snapshot(&mut self) -> RegionSnapshot { + let length = self.undo_log.len(); debug!("RegionVarBindings: start_snapshot({})", length); - self.undo_log.borrow_mut().push(OpenSnapshot); + self.undo_log.push(OpenSnapshot); RegionSnapshot { length, - region_snapshot: self.unification_table.borrow_mut().snapshot(), - skolemization_count: self.skolemization_count.get(), + region_snapshot: self.unification_table.snapshot(), + skolemization_count: self.skolemization_count, } } - pub fn commit(&self, snapshot: RegionSnapshot) { + pub fn commit(&mut self, snapshot: RegionSnapshot) { debug!("RegionVarBindings: commit({})", snapshot.length); - assert!(self.undo_log.borrow().len() > snapshot.length); - assert!((*self.undo_log.borrow())[snapshot.length] == OpenSnapshot); + assert!(self.undo_log.len() > snapshot.length); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); assert!( - self.skolemization_count.get() == snapshot.skolemization_count, + self.skolemization_count == snapshot.skolemization_count, "failed to pop skolemized regions: {} now vs {} at start", - self.skolemization_count.get(), + self.skolemization_count, snapshot.skolemization_count ); - let mut undo_log = self.undo_log.borrow_mut(); if snapshot.length == 0 { - undo_log.truncate(0); + self.undo_log.truncate(0); } else { - (*undo_log)[snapshot.length] = CommitedSnapshot; + (*self.undo_log)[snapshot.length] = CommitedSnapshot; } self.unification_table - .borrow_mut() .commit(snapshot.region_snapshot); } - pub fn rollback_to(&self, snapshot: RegionSnapshot) { + pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { debug!("RegionVarBindings: rollback_to({:?})", snapshot); - let mut undo_log = self.undo_log.borrow_mut(); - assert!(undo_log.len() > snapshot.length); - assert!((*undo_log)[snapshot.length] == OpenSnapshot); - while undo_log.len() > snapshot.length + 1 { - self.rollback_undo_entry(undo_log.pop().unwrap()); + assert!(self.undo_log.len() > snapshot.length); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); + while self.undo_log.len() > snapshot.length + 1 { + let undo_entry = self.undo_log.pop().unwrap(); + self.rollback_undo_entry(undo_entry); } - let c = undo_log.pop().unwrap(); + let c = self.undo_log.pop().unwrap(); assert!(c == OpenSnapshot); - self.skolemization_count.set(snapshot.skolemization_count); + self.skolemization_count = snapshot.skolemization_count; self.unification_table - .borrow_mut() .rollback_to(snapshot.region_snapshot); } - fn rollback_undo_entry(&self, undo_entry: UndoLogEntry<'tcx>) { + fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) { match undo_entry { OpenSnapshot => { panic!("Failure to observe stack discipline"); @@ -314,48 +310,46 @@ impl<'tcx> RegionVarBindings<'tcx> { // nothing to do here } AddVar(vid) => { - let mut var_origins = self.var_origins.borrow_mut(); - var_origins.pop().unwrap(); - assert_eq!(var_origins.len(), vid.index as usize); + self.var_origins.pop().unwrap(); + assert_eq!(self.var_origins.len(), vid.index as usize); } AddConstraint(ref constraint) => { - self.constraints.borrow_mut().remove(constraint); + self.constraints.remove(constraint); } AddVerify(index) => { - self.verifys.borrow_mut().pop(); - assert_eq!(self.verifys.borrow().len(), index); + self.verifys.pop(); + assert_eq!(self.verifys.len(), index); } AddGiven(sub, sup) => { - self.givens.borrow_mut().remove(&(sub, sup)); + self.givens.remove(&(sub, sup)); } AddCombination(Glb, ref regions) => { - self.glbs.borrow_mut().remove(regions); + self.glbs.remove(regions); } AddCombination(Lub, ref regions) => { - self.lubs.borrow_mut().remove(regions); + self.lubs.remove(regions); } } } pub fn num_vars(&self) -> u32 { - let len = self.var_origins.borrow().len(); + let len = self.var_origins.len(); // enforce no overflow assert!(len as u32 as usize == len); len as u32 } - pub fn new_region_var(&self, origin: RegionVariableOrigin) -> RegionVid { + pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { let vid = RegionVid { index: self.num_vars(), }; - self.var_origins.borrow_mut().push(origin.clone()); + self.var_origins.push(origin.clone()); let u_vid = self.unification_table - .borrow_mut() .new_key(unify_key::RegionVidKey { min_vid: vid }); assert_eq!(vid, u_vid); if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddVar(vid)); + self.undo_log.push(AddVar(vid)); } debug!( "created new region variable {:?} with origin {:?}", @@ -366,7 +360,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.var_origins.borrow()[vid.index as usize].clone() + self.var_origins[vid.index as usize].clone() } /// Creates a new skolemized region. Skolemized regions are fresh @@ -389,16 +383,16 @@ impl<'tcx> RegionVarBindings<'tcx> { /// it's just there to make it explicit which snapshot bounds the /// skolemized region that results. It should always be the top-most snapshot. pub fn push_skolemized( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, br: ty::BoundRegion, snapshot: &RegionSnapshot, ) -> Region<'tcx> { assert!(self.in_snapshot()); - assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); - let sc = self.skolemization_count.get(); - self.skolemization_count.set(sc + 1); + let sc = self.skolemization_count; + self.skolemization_count = sc + 1; tcx.mk_region(ReSkolemized(ty::SkolemizedRegionVid { index: sc }, br)) } @@ -407,7 +401,7 @@ impl<'tcx> RegionVarBindings<'tcx> { /// completes to remove all trace of the skolemized regions /// created in that time. pub fn pop_skolemized( - &self, + &mut self, _tcx: TyCtxt<'_, '_, 'tcx>, skols: &FxHashSet>, snapshot: &RegionSnapshot, @@ -415,23 +409,23 @@ impl<'tcx> RegionVarBindings<'tcx> { debug!("pop_skolemized_regions(skols={:?})", skols); assert!(self.in_snapshot()); - assert!(self.undo_log.borrow()[snapshot.length] == OpenSnapshot); + assert!(self.undo_log[snapshot.length] == OpenSnapshot); assert!( - self.skolemization_count.get() as usize >= skols.len(), + self.skolemization_count as usize >= skols.len(), "popping more skolemized variables than actually exist, \ sc now = {}, skols.len = {}", - self.skolemization_count.get(), + self.skolemization_count, skols.len() ); - let last_to_pop = self.skolemization_count.get(); + let last_to_pop = self.skolemization_count; let first_to_pop = last_to_pop - (skols.len() as u32); assert!( first_to_pop >= snapshot.skolemization_count, "popping more regions than snapshot contains, \ sc now = {}, sc then = {}, skols.len = {}", - self.skolemization_count.get(), + self.skolemization_count, snapshot.skolemization_count, skols.len() ); @@ -446,13 +440,11 @@ impl<'tcx> RegionVarBindings<'tcx> { }), "invalid skolemization keys or keys out of range ({}..{}): {:?}", snapshot.skolemization_count, - self.skolemization_count.get(), + self.skolemization_count, skols } - let mut undo_log = self.undo_log.borrow_mut(); - - let constraints_to_kill: Vec = undo_log + let constraints_to_kill: Vec = self.undo_log .iter() .enumerate() .rev() @@ -461,11 +453,11 @@ impl<'tcx> RegionVarBindings<'tcx> { .collect(); for index in constraints_to_kill { - let undo_entry = mem::replace(&mut undo_log[index], Purged); + let undo_entry = mem::replace(&mut self.undo_log[index], Purged); self.rollback_undo_entry(undo_entry); } - self.skolemization_count.set(snapshot.skolemization_count); + self.skolemization_count = snapshot.skolemization_count; return; fn kill_constraint<'tcx>( @@ -490,7 +482,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn new_bound( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, debruijn: ty::DebruijnIndex, ) -> Region<'tcx> { @@ -512,32 +504,31 @@ impl<'tcx> RegionVarBindings<'tcx> { // changing the representation of bound regions in a fn // declaration - let sc = self.bound_count.get(); - self.bound_count.set(sc + 1); + let sc = self.bound_count; + self.bound_count = sc + 1; - if sc >= self.bound_count.get() { + if sc >= self.bound_count { bug!("rollover in RegionInference new_bound()"); } tcx.mk_region(ReLateBound(debruijn, BrFresh(sc))) } - fn add_constraint(&self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { + fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { // cannot add constraints once regions are resolved debug!("RegionVarBindings: add_constraint({:?})", constraint); if self.constraints - .borrow_mut() .insert(constraint, origin) .is_none() { if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddConstraint(constraint)); + self.undo_log.push(AddConstraint(constraint)); } } } - fn add_verify(&self, verify: Verify<'tcx>) { + fn add_verify(&mut self, verify: Verify<'tcx>) { // cannot add verifys once regions are resolved debug!("RegionVarBindings: add_verify({:?})", verify); @@ -549,26 +540,24 @@ impl<'tcx> RegionVarBindings<'tcx> { _ => {} } - let mut verifys = self.verifys.borrow_mut(); - let index = verifys.len(); - verifys.push(verify); + let index = self.verifys.len(); + self.verifys.push(verify); if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddVerify(index)); + self.undo_log.push(AddVerify(index)); } } - pub fn add_given(&self, sub: Region<'tcx>, sup: ty::RegionVid) { + pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { // cannot add givens once regions are resolved - let mut givens = self.givens.borrow_mut(); - if givens.insert((sub, sup)) { + if self.givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); - self.undo_log.borrow_mut().push(AddGiven(sub, sup)); + self.undo_log.push(AddGiven(sub, sup)); } } pub fn make_eqregion( - &self, + &mut self, origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, sup: Region<'tcx>, @@ -580,13 +569,13 @@ impl<'tcx> RegionVarBindings<'tcx> { self.make_subregion(origin, sup, sub); if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { - self.unification_table.borrow_mut().union(sub, sup); + self.unification_table.union(sub, sup); } } } pub fn make_subregion( - &self, + &mut self, origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, sup: Region<'tcx>, @@ -628,7 +617,7 @@ impl<'tcx> RegionVarBindings<'tcx> { /// See `Verify::VerifyGenericBound` pub fn verify_generic_bound( - &self, + &mut self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, sub: Region<'tcx>, @@ -643,7 +632,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn lub_regions( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, origin: SubregionOrigin<'tcx>, a: Region<'tcx>, @@ -665,7 +654,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn glb_regions( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, origin: SubregionOrigin<'tcx>, a: Region<'tcx>, @@ -687,23 +676,23 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn opportunistic_resolve_var( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, rid: RegionVid, ) -> ty::Region<'tcx> { - let vid = self.unification_table.borrow_mut().find_value(rid).min_vid; + let vid = self.unification_table.find_value(rid).min_vid; tcx.mk_region(ty::ReVar(vid)) } - fn combine_map(&self, t: CombineMapType) -> &RefCell> { + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> { match t { - Glb => &self.glbs, - Lub => &self.lubs, + Glb => &mut self.glbs, + Lub => &mut self.lubs, } } fn combine_vars( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, t: CombineMapType, a: Region<'tcx>, @@ -711,13 +700,13 @@ impl<'tcx> RegionVarBindings<'tcx> { origin: SubregionOrigin<'tcx>, ) -> Region<'tcx> { let vars = TwoRegions { a: a, b: b }; - if let Some(&c) = self.combine_map(t).borrow().get(&vars) { + if let Some(&c) = self.combine_map(t).get(&vars) { return tcx.mk_region(ReVar(c)); } let c = self.new_region_var(MiscVariable(origin.span())); - self.combine_map(t).borrow_mut().insert(vars, c); + self.combine_map(t).insert(vars, c); if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddCombination(t, vars)); + self.undo_log.push(AddCombination(t, vars)); } let new_r = tcx.mk_region(ReVar(c)); for &old_r in &[a, b] { @@ -731,7 +720,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn vars_created_since_snapshot(&self, mark: &RegionSnapshot) -> Vec { - self.undo_log.borrow()[mark.length..] + self.undo_log[mark.length..] .iter() .filter_map(|&elt| match elt { AddVar(vid) => Some(vid), @@ -768,8 +757,8 @@ impl<'tcx> RegionVarBindings<'tcx> { let mut taint_set = taint::TaintSet::new(directions, r0); taint_set.fixed_point( tcx, - &self.undo_log.borrow()[mark.length..], - &self.verifys.borrow(), + &self.undo_log[mark.length..], + &self.verifys, ); debug!("tainted: result={:?}", taint_set); return taint_set.into_set(); diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index b9501773e02b9..8a99eb0801432 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -74,8 +74,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self.infcx.region_vars.opportunistic_resolve_var(self.tcx(), rid), - _ => r, + ty::ReVar(rid) => + self.infcx.region_vars.borrow_mut().opportunistic_resolve_var(self.tcx(), rid), + _ => + r, } } } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 4056999681352..bba4328e6ea84 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -137,7 +137,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> // from the "cause" field, we could perhaps give more tailored // error messages. let origin = SubregionOrigin::Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.make_subregion(origin, a, b); + self.fields.infcx.region_vars.borrow_mut().make_subregion(origin, a, b); Ok(a) } From be36920de382fc9872c55109c8a58a059fa51861 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:34:22 -0500 Subject: [PATCH 26/68] rename `region_inference` module to `region_constraints` --- src/librustc/infer/error_reporting/mod.rs | 2 +- src/librustc/infer/higher_ranked/mod.rs | 2 +- src/librustc/infer/lexical_region_resolve/graphviz.rs | 4 ++-- src/librustc/infer/lexical_region_resolve/mod.rs | 8 ++++---- src/librustc/infer/mod.rs | 10 +++++----- .../{region_inference => region_constraints}/README.md | 0 .../{region_inference => region_constraints}/mod.rs | 0 .../{region_inference => region_constraints}/taint.rs | 0 src/librustc/middle/region.rs | 8 ++++---- src/librustc_typeck/check/_match.rs | 2 +- ...bound-regions-on-closures-to-inference-variables.rs | 2 +- 11 files changed, 19 insertions(+), 19 deletions(-) rename src/librustc/infer/{region_inference => region_constraints}/README.md (100%) rename src/librustc/infer/{region_inference => region_constraints}/mod.rs (100%) rename src/librustc/infer/{region_inference => region_constraints}/taint.rs (100%) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 9ce0e503280ea..67fdd68a826c2 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -57,7 +57,7 @@ use infer; use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs}; -use super::region_inference::GenericKind; +use super::region_constraints::GenericKind; use super::lexical_region_resolve::RegionResolutionError; use std::fmt; diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index e2a214ed0cbfe..5f898856d31f0 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -17,7 +17,7 @@ use super::{CombinedSnapshot, SubregionOrigin, SkolemizationMap}; use super::combine::CombineFields; -use super::region_inference::{TaintDirections}; +use super::region_constraints::{TaintDirections}; use ty::{self, TyCtxt, Binder, TypeFoldable}; use ty::error::TypeError; diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index f5a0812853355..a7e09396dfa91 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -9,7 +9,7 @@ // except according to those terms. //! This module provides linkage between libgraphviz traits and -//! `rustc::middle::typeck::infer::region_inference`, generating a +//! `rustc::middle::typeck::infer::region_constraints`, generating a //! rendering of the graph represented by the list of `Constraint` //! instances (which make up the edges of the graph), as well as the //! origin for each constraint (which are attached to the labels on @@ -25,7 +25,7 @@ use middle::free_region::RegionRelations; use middle::region; use super::Constraint; use infer::SubregionOrigin; -use infer::region_inference::RegionVarBindings; +use infer::region_constraints::RegionVarBindings; use util::nodemap::{FxHashMap, FxHashSet}; use std::borrow::Cow; diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index ac3ba57d980c1..aeeacfb132a65 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -12,10 +12,10 @@ use infer::SubregionOrigin; use infer::RegionVariableOrigin; -use infer::region_inference::Constraint; -use infer::region_inference::GenericKind; -use infer::region_inference::RegionVarBindings; -use infer::region_inference::VerifyBound; +use infer::region_constraints::Constraint; +use infer::region_constraints::GenericKind; +use infer::region_constraints::RegionVarBindings; +use infer::region_constraints::VerifyBound; use middle::free_region::RegionRelations; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 4aef09aa935a5..e5e6f292f6875 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -16,7 +16,7 @@ pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; pub use ty::IntVarValue; pub use self::freshen::TypeFreshener; -pub use self::region_inference::{GenericKind, VerifyBound}; +pub use self::region_constraints::{GenericKind, VerifyBound}; use hir::def_id::DefId; use middle::free_region::{FreeRegionMap, RegionRelations}; @@ -41,7 +41,7 @@ use arena::DroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; -use self::region_inference::{RegionVarBindings, RegionSnapshot}; +use self::region_constraints::{RegionVarBindings, RegionSnapshot}; use self::lexical_region_resolve::LexicalRegionResolutions; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -55,7 +55,7 @@ mod glb; mod higher_ranked; pub mod lattice; mod lub; -pub mod region_inference; +pub mod region_constraints; mod lexical_region_resolve; mod outlives; pub mod resolve; @@ -104,7 +104,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { float_unification_table: RefCell>, // For region variables. - region_vars: RefCell>, + region_constraints: RefCell>, // Once region inference is done, the values for each variable. lexical_region_resolutions: RefCell>>, @@ -1354,7 +1354,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { Ok(InferOk { value: result, obligations: combine.obligations }) } - /// See `verify_generic_bound` method in `region_inference` + /// See `verify_generic_bound` method in `region_constraints` pub fn verify_generic_bound(&self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, diff --git a/src/librustc/infer/region_inference/README.md b/src/librustc/infer/region_constraints/README.md similarity index 100% rename from src/librustc/infer/region_inference/README.md rename to src/librustc/infer/region_constraints/README.md diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_constraints/mod.rs similarity index 100% rename from src/librustc/infer/region_inference/mod.rs rename to src/librustc/infer/region_constraints/mod.rs diff --git a/src/librustc/infer/region_inference/taint.rs b/src/librustc/infer/region_constraints/taint.rs similarity index 100% rename from src/librustc/infer/region_inference/taint.rs rename to src/librustc/infer/region_constraints/taint.rs diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 89707839144cc..1bb19666af3cf 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -12,7 +12,7 @@ //! the parent links in the region hierarchy. //! //! Most of the documentation on regions can be found in -//! `middle/infer/region_inference/README.md` +//! `middle/infer/region_constraints/README.md` use ich::{StableHashingContext, NodeIdHashingMode}; use util::nodemap::{FxHashMap, FxHashSet}; @@ -321,7 +321,7 @@ pub struct ScopeTree { /// hierarchy based on their lexical mapping. This is used to /// handle the relationships between regions in a fn and in a /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_inference for + /// section of the README in infer::region_constraints for /// more details. closure_tree: FxHashMap, @@ -408,7 +408,7 @@ pub struct Context { /// of the innermost fn body. Each fn forms its own disjoint tree /// in the region hierarchy. These fn bodies are themselves /// arranged into a tree. See the "Modeling closures" section of - /// the README in infer::region_inference for more + /// the README in infer::region_constraints for more /// details. root_id: Option, @@ -647,7 +647,7 @@ impl<'tcx> ScopeTree { // different functions. Compare those fn for lexical // nesting. The reasoning behind this is subtle. See the // "Modeling closures" section of the README in - // infer::region_inference for more details. + // infer::region_constraints for more details. let a_root_scope = a_ancestors[a_index]; let b_root_scope = a_ancestors[a_index]; return match (a_root_scope.data(), b_root_scope.data()) { diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 272f13b28030e..ea0fa945c3783 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -471,7 +471,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // // 2. Things go horribly wrong if we use subtype. The reason for // THIS is a fairly subtle case involving bound regions. See the - // `givens` field in `region_inference`, as well as the test + // `givens` field in `region_constraints`, as well as the test // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, // for details. Short version is that we must sometimes detect // relationships between specific region variables and regions diff --git a/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs b/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs index ae4adbfb1f497..3162ef54f39bc 100644 --- a/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs +++ b/src/test/run-pass/regions-relate-bound-regions-on-closures-to-inference-variables.rs @@ -42,7 +42,7 @@ impl<'a,'tcx> Foo<'a,'tcx> { // inferring `'_2` to be `'static` in this case, because // it is created outside the closure but then related to // regions bound by the closure itself. See the - // `region_inference.rs` file (and the `givens` field, in + // `region_constraints.rs` file (and the `givens` field, in // particular) for more details. this.foo() })) From 619f8bd0d5945798a0785b7424a7ee3a53615827 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:55:40 -0500 Subject: [PATCH 27/68] infer: rename `region_vars` field to `region_constraints` --- src/librustc/infer/equate.rs | 2 +- src/librustc/infer/fudge.rs | 4 ++-- src/librustc/infer/glb.rs | 2 +- src/librustc/infer/higher_ranked/mod.rs | 24 ++++++++++++-------- src/librustc/infer/lub.rs | 2 +- src/librustc/infer/mod.rs | 30 ++++++++++++------------- src/librustc/infer/resolve.rs | 3 ++- src/librustc/infer/sub.rs | 2 +- 8 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index dbe3905a8c619..0c59fa703bb66 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -104,7 +104,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> a, b); let origin = Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.borrow_mut().make_eqregion(origin, a, b); + self.fields.infcx.region_constraints.borrow_mut().make_eqregion(origin, a, b); Ok(a) } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index b6637069408e0..729e67437ba3e 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -78,8 +78,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.type_variables.borrow_mut().types_created_since_snapshot( &snapshot.type_snapshot); let region_vars = - self.region_vars.borrow().vars_created_since_snapshot( - &snapshot.region_vars_snapshot); + self.region_constraints.borrow().vars_created_since_snapshot( + &snapshot.region_constraints_snapshot); Ok((type_variables, region_vars, value)) } diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index c0e1369410616..d63036eecff9f 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.borrow_mut().glb_regions(self.tcx(), origin, a, b)) + Ok(self.fields.infcx.region_constraints.borrow_mut().glb_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 5f898856d31f0..2aef9fece87a5 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -176,7 +176,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { .filter(|&r| r != representative) { let origin = SubregionOrigin::Subtype(self.trace.clone()); - self.infcx.region_vars.borrow_mut().make_eqregion(origin, + self.infcx.region_constraints.borrow_mut().make_eqregion(origin, *representative, *region); } @@ -427,7 +427,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - infcx.region_vars.borrow_mut().new_bound(infcx.tcx, debruijn) + infcx.region_constraints.borrow_mut().new_bound(infcx.tcx, debruijn) } } } @@ -481,7 +481,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r: ty::Region<'tcx>, directions: TaintDirections) -> FxHashSet> { - self.region_vars.borrow().tainted(self.tcx, &snapshot.region_vars_snapshot, r, directions) + self.region_constraints.borrow().tainted( + self.tcx, + &snapshot.region_constraints_snapshot, + r, + directions) } fn region_vars_confined_to_snapshot(&self, @@ -539,7 +543,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { */ let mut region_vars = - self.region_vars.borrow().vars_created_since_snapshot(&snapshot.region_vars_snapshot); + self.region_constraints.borrow().vars_created_since_snapshot( + &snapshot.region_constraints_snapshot); let escaping_types = self.type_variables.borrow_mut().types_escaping_snapshot(&snapshot.type_snapshot); @@ -581,9 +586,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { where T : TypeFoldable<'tcx> { let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.region_vars.borrow_mut().push_skolemized(self.tcx, + self.region_constraints.borrow_mut().push_skolemized(self.tcx, br, - &snapshot.region_vars_snapshot) + &snapshot.region_constraints_snapshot) }); debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})", @@ -768,9 +773,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { debug!("pop_skolemized({:?})", skol_map); let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect(); - self.region_vars.borrow_mut().pop_skolemized(self.tcx, - &skol_regions, - &snapshot.region_vars_snapshot); + self.region_constraints.borrow_mut().pop_skolemized( + self.tcx, + &skol_regions, + &snapshot.region_constraints_snapshot); if !skol_map.is_empty() { self.projection_cache.borrow_mut().rollback_skolemized( &snapshot.projection_cache_snapshot); diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index e3a16ecb444ad..0c4f4efe9947c 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_vars.borrow_mut().lub_regions(self.tcx(), origin, a, b)) + Ok(self.fields.infcx.region_constraints.borrow_mut().lub_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index e5e6f292f6875..fe15f9acdb5a0 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -104,7 +104,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { float_unification_table: RefCell>, // For region variables. - region_constraints: RefCell>, + region_constraints: RefCell>, // Once region inference is done, the values for each variable. lexical_region_resolutions: RefCell>>, @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), - region_vars: RefCell::new(RegionVarBindings::new()), + region_constraints: RefCell::new(RegionVarBindings::new()), lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), @@ -459,7 +459,7 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { type_snapshot: type_variable::Snapshot, int_snapshot: unify::Snapshot, float_snapshot: unify::Snapshot, - region_vars_snapshot: RegionSnapshot, + region_constraints_snapshot: RegionSnapshot, was_in_snapshot: bool, _in_progress_tables: Option>>, } @@ -767,7 +767,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot: self.type_variables.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), - region_vars_snapshot: self.region_vars.borrow_mut().start_snapshot(), + region_constraints_snapshot: self.region_constraints.borrow_mut().start_snapshot(), was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. @@ -783,7 +783,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot, int_snapshot, float_snapshot, - region_vars_snapshot, + region_constraints_snapshot, was_in_snapshot, _in_progress_tables } = snapshot; @@ -801,9 +801,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .rollback_to(float_snapshot); - self.region_vars + self.region_constraints .borrow_mut() - .rollback_to(region_vars_snapshot); + .rollback_to(region_constraints_snapshot); } fn commit_from(&self, snapshot: CombinedSnapshot) { @@ -812,7 +812,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot, int_snapshot, float_snapshot, - region_vars_snapshot, + region_constraints_snapshot, was_in_snapshot, _in_progress_tables } = snapshot; @@ -830,9 +830,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .commit(float_snapshot); - self.region_vars + self.region_constraints .borrow_mut() - .commit(region_vars_snapshot); + .commit(region_constraints_snapshot); } /// Execute `f` and commit the bindings @@ -887,7 +887,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub: ty::Region<'tcx>, sup: ty::RegionVid) { - self.region_vars.borrow_mut().add_given(sub, sup); + self.region_constraints.borrow_mut().add_given(sub, sup); } pub fn can_sub(&self, @@ -927,7 +927,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a: ty::Region<'tcx>, b: ty::Region<'tcx>) { debug!("sub_regions({:?} <: {:?})", a, b); - self.region_vars.borrow_mut().make_subregion(origin, a, b); + self.region_constraints.borrow_mut().make_subregion(origin, a, b); } pub fn equality_predicate(&self, @@ -1030,7 +1030,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { - self.tcx.mk_region(ty::ReVar(self.region_vars.borrow_mut().new_region_var(origin))) + self.tcx.mk_region(ty::ReVar(self.region_constraints.borrow_mut().new_region_var(origin))) } /// Create a region inference variable for the given @@ -1127,7 +1127,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_map, free_regions); let (lexical_region_resolutions, errors) = - self.region_vars.borrow_mut().resolve_regions(®ion_rels); + self.region_constraints.borrow_mut().resolve_regions(®ion_rels); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); @@ -1365,7 +1365,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a, bound); - self.region_vars.borrow_mut().verify_generic_bound(origin, kind, a, bound); + self.region_constraints.borrow_mut().verify_generic_bound(origin, kind, a, bound); } pub fn type_moves_by_default(&self, diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 8a99eb0801432..53d963832426b 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -75,7 +75,8 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { ty::ReVar(rid) => - self.infcx.region_vars.borrow_mut().opportunistic_resolve_var(self.tcx(), rid), + self.infcx.region_constraints.borrow_mut() + .opportunistic_resolve_var(self.tcx(), rid), _ => r, } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index bba4328e6ea84..bc4bb0c4712fa 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -137,7 +137,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> // from the "cause" field, we could perhaps give more tailored // error messages. let origin = SubregionOrigin::Subtype(self.fields.trace.clone()); - self.fields.infcx.region_vars.borrow_mut().make_subregion(origin, a, b); + self.fields.infcx.region_constraints.borrow_mut().make_subregion(origin, a, b); Ok(a) } From 9dc09dc092fa10ea7e0a5328f5c1337499e8c071 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 07:01:48 -0500 Subject: [PATCH 28/68] rename RegionVarBindings to RegionConstraintCollector --- .../infer/lexical_region_resolve/graphviz.rs | 6 ++--- .../infer/lexical_region_resolve/mod.rs | 8 +++---- src/librustc/infer/mod.rs | 6 ++--- src/librustc/infer/region_constraints/mod.rs | 24 +++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index a7e09396dfa91..7f74da023897b 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -25,7 +25,7 @@ use middle::free_region::RegionRelations; use middle::region; use super::Constraint; use infer::SubregionOrigin; -use infer::region_constraints::RegionVarBindings; +use infer::region_constraints::RegionConstraintCollector; use util::nodemap::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -56,7 +56,7 @@ graphs will be printed. \n\ } pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( - region_vars: &RegionVarBindings<'tcx>, + region_constraints: &RegionConstraintCollector<'tcx>, region_rels: &RegionRelations<'a, 'gcx, 'tcx>) { let tcx = region_rels.tcx; @@ -112,7 +112,7 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( } }; - match dump_region_constraints_to(region_rels, ®ion_vars.constraints, &output_path) { + match dump_region_constraints_to(region_rels, ®ion_constraints.constraints, &output_path) { Ok(()) => {} Err(e) => { let msg = format!("io error dumping region constraints: {}", e); diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index aeeacfb132a65..9a02b274b5fb0 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -14,7 +14,7 @@ use infer::SubregionOrigin; use infer::RegionVariableOrigin; use infer::region_constraints::Constraint; use infer::region_constraints::GenericKind; -use infer::region_constraints::RegionVarBindings; +use infer::region_constraints::RegionConstraintCollector; use infer::region_constraints::VerifyBound; use middle::free_region::RegionRelations; use rustc_data_structures::fx::FxHashSet; @@ -73,7 +73,7 @@ struct RegionAndOrigin<'tcx> { type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; -impl<'tcx> RegionVarBindings<'tcx> { +impl<'tcx> RegionConstraintCollector<'tcx> { /// This function performs the actual region resolution. It must be /// called after all constraints have been added. It performs a /// fixed-point iteration to find region values which satisfy all @@ -86,7 +86,7 @@ impl<'tcx> RegionVarBindings<'tcx> { LexicalRegionResolutions<'tcx>, Vec>, ) { - debug!("RegionVarBindings: resolve_regions()"); + debug!("RegionConstraintCollector: resolve_regions()"); let mut errors = vec![]; let values = self.infer_variable_values(region_rels, &mut errors); (values, errors) @@ -642,7 +642,7 @@ impl<'tcx> RegionVarBindings<'tcx> { return (result, dup_found); fn process_edges<'tcx>( - this: &RegionVarBindings<'tcx>, + this: &RegionConstraintCollector<'tcx>, state: &mut WalkState<'tcx>, graph: &RegionGraph<'tcx>, source_vid: RegionVid, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index fe15f9acdb5a0..d42419d7dc64d 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -41,7 +41,7 @@ use arena::DroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; -use self::region_constraints::{RegionVarBindings, RegionSnapshot}; +use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; use self::lexical_region_resolve::LexicalRegionResolutions; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -104,7 +104,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { float_unification_table: RefCell>, // For region variables. - region_constraints: RefCell>, + region_constraints: RefCell>, // Once region inference is done, the values for each variable. lexical_region_resolutions: RefCell>>, @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), - region_constraints: RefCell::new(RegionVarBindings::new()), + region_constraints: RefCell::new(RegionConstraintCollector::new()), lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 37699ccea0dd2..9ac9c620125e2 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -142,7 +142,7 @@ enum CombineMapType { type CombineMap<'tcx> = FxHashMap, RegionVid>; -pub struct RegionVarBindings<'tcx> { +pub struct RegionConstraintCollector<'tcx> { pub(in infer) var_origins: Vec, /// Constraints of the form `A <= B` introduced by the region @@ -235,9 +235,9 @@ impl TaintDirections { } } -impl<'tcx> RegionVarBindings<'tcx> { - pub fn new() -> RegionVarBindings<'tcx> { - RegionVarBindings { +impl<'tcx> RegionConstraintCollector<'tcx> { + pub fn new() -> RegionConstraintCollector<'tcx> { + RegionConstraintCollector { var_origins: Vec::new(), constraints: FxHashMap(), verifys: Vec::new(), @@ -257,7 +257,7 @@ impl<'tcx> RegionVarBindings<'tcx> { pub fn start_snapshot(&mut self) -> RegionSnapshot { let length = self.undo_log.len(); - debug!("RegionVarBindings: start_snapshot({})", length); + debug!("RegionConstraintCollector: start_snapshot({})", length); self.undo_log.push(OpenSnapshot); RegionSnapshot { length, @@ -267,7 +267,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn commit(&mut self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: commit({})", snapshot.length); + debug!("RegionConstraintCollector: commit({})", snapshot.length); assert!(self.undo_log.len() > snapshot.length); assert!(self.undo_log[snapshot.length] == OpenSnapshot); assert!( @@ -287,7 +287,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { - debug!("RegionVarBindings: rollback_to({:?})", snapshot); + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); assert!(self.undo_log.len() > snapshot.length); assert!(self.undo_log[snapshot.length] == OpenSnapshot); while self.undo_log.len() > snapshot.length + 1 { @@ -516,7 +516,7 @@ impl<'tcx> RegionVarBindings<'tcx> { fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { // cannot add constraints once regions are resolved - debug!("RegionVarBindings: add_constraint({:?})", constraint); + debug!("RegionConstraintCollector: add_constraint({:?})", constraint); if self.constraints .insert(constraint, origin) @@ -530,7 +530,7 @@ impl<'tcx> RegionVarBindings<'tcx> { fn add_verify(&mut self, verify: Verify<'tcx>) { // cannot add verifys once regions are resolved - debug!("RegionVarBindings: add_verify({:?})", verify); + debug!("RegionConstraintCollector: add_verify({:?})", verify); // skip no-op cases known to be satisfied match verify.bound { @@ -582,7 +582,7 @@ impl<'tcx> RegionVarBindings<'tcx> { ) { // cannot add constraints once regions are resolved debug!( - "RegionVarBindings: make_subregion({:?}, {:?}) due to {:?}", + "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}", sub, sup, origin @@ -639,7 +639,7 @@ impl<'tcx> RegionVarBindings<'tcx> { b: Region<'tcx>, ) -> Region<'tcx> { // cannot add constraints once regions are resolved - debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b); + debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); match (a, b) { (r @ &ReStatic, _) | (_, r @ &ReStatic) => { r // nothing lives longer than static @@ -661,7 +661,7 @@ impl<'tcx> RegionVarBindings<'tcx> { b: Region<'tcx>, ) -> Region<'tcx> { // cannot add constraints once regions are resolved - debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b); + debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); match (a, b) { (&ReStatic, r) | (r, &ReStatic) => { r // static lives longer than everything else From 0c3f002777bfe7e498cc65747df188ae3998fb22 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 07:22:39 -0500 Subject: [PATCH 29/68] make the `region_constraints` field an `Option` This way, we can `take()` ownership of it when we are going to resolve regions. --- src/librustc/infer/equate.rs | 3 +- src/librustc/infer/fudge.rs | 2 +- src/librustc/infer/glb.rs | 2 +- src/librustc/infer/higher_ranked/mod.rs | 24 +++++------ src/librustc/infer/lub.rs | 2 +- src/librustc/infer/mod.rs | 54 ++++++++++++++++--------- src/librustc/infer/resolve.rs | 4 +- src/librustc/infer/sub.rs | 3 +- 8 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index 0c59fa703bb66..2ae8f8ae93357 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -104,7 +104,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> a, b); let origin = Subtype(self.fields.trace.clone()); - self.fields.infcx.region_constraints.borrow_mut().make_eqregion(origin, a, b); + self.fields.infcx.borrow_region_constraints() + .make_eqregion(origin, a, b); Ok(a) } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 729e67437ba3e..756a6947ee3f8 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -78,7 +78,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.type_variables.borrow_mut().types_created_since_snapshot( &snapshot.type_snapshot); let region_vars = - self.region_constraints.borrow().vars_created_since_snapshot( + self.borrow_region_constraints().vars_created_since_snapshot( &snapshot.region_constraints_snapshot); Ok((type_variables, region_vars, value)) diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index d63036eecff9f..8b42314ed97cf 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_constraints.borrow_mut().glb_regions(self.tcx(), origin, a, b)) + Ok(self.fields.infcx.borrow_region_constraints().glb_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/higher_ranked/mod.rs b/src/librustc/infer/higher_ranked/mod.rs index 2aef9fece87a5..c49b3b4b9c873 100644 --- a/src/librustc/infer/higher_ranked/mod.rs +++ b/src/librustc/infer/higher_ranked/mod.rs @@ -176,9 +176,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { .filter(|&r| r != representative) { let origin = SubregionOrigin::Subtype(self.trace.clone()); - self.infcx.region_constraints.borrow_mut().make_eqregion(origin, - *representative, - *region); + self.infcx.borrow_region_constraints() + .make_eqregion(origin, + *representative, + *region); } } @@ -427,7 +428,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> { fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, debruijn: ty::DebruijnIndex) -> ty::Region<'tcx> { - infcx.region_constraints.borrow_mut().new_bound(infcx.tcx, debruijn) + infcx.borrow_region_constraints().new_bound(infcx.tcx, debruijn) } } } @@ -481,7 +482,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r: ty::Region<'tcx>, directions: TaintDirections) -> FxHashSet> { - self.region_constraints.borrow().tainted( + self.borrow_region_constraints().tainted( self.tcx, &snapshot.region_constraints_snapshot, r, @@ -543,7 +544,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { */ let mut region_vars = - self.region_constraints.borrow().vars_created_since_snapshot( + self.borrow_region_constraints().vars_created_since_snapshot( &snapshot.region_constraints_snapshot); let escaping_types = @@ -586,9 +587,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { where T : TypeFoldable<'tcx> { let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| { - self.region_constraints.borrow_mut().push_skolemized(self.tcx, - br, - &snapshot.region_constraints_snapshot) + self.borrow_region_constraints() + .push_skolemized(self.tcx, br, &snapshot.region_constraints_snapshot) }); debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})", @@ -773,10 +773,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { { debug!("pop_skolemized({:?})", skol_map); let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect(); - self.region_constraints.borrow_mut().pop_skolemized( - self.tcx, - &skol_regions, - &snapshot.region_constraints_snapshot); + self.borrow_region_constraints() + .pop_skolemized(self.tcx, &skol_regions, &snapshot.region_constraints_snapshot); if !skol_map.is_empty() { self.projection_cache.borrow_mut().rollback_skolemized( &snapshot.projection_cache_snapshot); diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 0c4f4efe9947c..4a2a7a6bdfeca 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> b); let origin = Subtype(self.fields.trace.clone()); - Ok(self.fields.infcx.region_constraints.borrow_mut().lub_regions(self.tcx(), origin, a, b)) + Ok(self.fields.infcx.borrow_region_constraints().lub_regions(self.tcx(), origin, a, b)) } fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d42419d7dc64d..a5cae839aa70b 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -31,7 +31,7 @@ use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::relate::RelateResult; use traits::{self, ObligationCause, PredicateObligations, Reveal}; use rustc_data_structures::unify::{self, UnificationTable}; -use std::cell::{Cell, RefCell, Ref}; +use std::cell::{Cell, RefCell, Ref, RefMut}; use std::fmt; use syntax::ast; use errors::DiagnosticBuilder; @@ -103,8 +103,12 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // Map from floating variable to the kind of float it represents float_unification_table: RefCell>, - // For region variables. - region_constraints: RefCell>, + // Tracks the set of region variables and the constraints between + // them. This is initially `Some(_)` but when + // `resolve_regions_and_report_errors` is invoked, this gets set + // to `None` -- further attempts to perform unification etc may + // fail if new region constraints would've been added. + region_constraints: RefCell>>, // Once region inference is done, the values for each variable. lexical_region_resolutions: RefCell>>, @@ -424,7 +428,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), - region_constraints: RefCell::new(RegionConstraintCollector::new()), + region_constraints: RefCell::new(Some(RegionConstraintCollector::new())), lexical_region_resolutions: RefCell::new(None), selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), @@ -767,7 +771,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { type_snapshot: self.type_variables.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), - region_constraints_snapshot: self.region_constraints.borrow_mut().start_snapshot(), + region_constraints_snapshot: self.borrow_region_constraints().start_snapshot(), was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. @@ -801,8 +805,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .rollback_to(float_snapshot); - self.region_constraints - .borrow_mut() + self.borrow_region_constraints() .rollback_to(region_constraints_snapshot); } @@ -830,8 +833,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .commit(float_snapshot); - self.region_constraints - .borrow_mut() + self.borrow_region_constraints() .commit(region_constraints_snapshot); } @@ -887,7 +889,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sub: ty::Region<'tcx>, sup: ty::RegionVid) { - self.region_constraints.borrow_mut().add_given(sub, sup); + self.borrow_region_constraints().add_given(sub, sup); } pub fn can_sub(&self, @@ -927,7 +929,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a: ty::Region<'tcx>, b: ty::Region<'tcx>) { debug!("sub_regions({:?} <: {:?})", a, b); - self.region_constraints.borrow_mut().make_subregion(origin, a, b); + self.borrow_region_constraints().make_subregion(origin, a, b); } pub fn equality_predicate(&self, @@ -1030,7 +1032,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { - self.tcx.mk_region(ty::ReVar(self.region_constraints.borrow_mut().new_region_var(origin))) + self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin))) } /// Create a region inference variable for the given @@ -1114,6 +1116,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.tainted_by_errors_flag.set(true) } + /// Process the region constraints and report any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_type_vars_if_possible` as well as `fully_resolve`. pub fn resolve_regions_and_report_errors(&self, region_context: DefId, region_map: ®ion::ScopeTree, @@ -1126,8 +1132,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context, region_map, free_regions); - let (lexical_region_resolutions, errors) = - self.region_constraints.borrow_mut().resolve_regions(®ion_rels); + let mut region_constraints = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved"); + let (lexical_region_resolutions, errors) = region_constraints.resolve_regions(®ion_rels); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); @@ -1365,7 +1373,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { a, bound); - self.region_constraints.borrow_mut().verify_generic_bound(origin, kind, a, bound); + self.borrow_region_constraints().verify_generic_bound(origin, kind, a, bound); } pub fn type_moves_by_default(&self, @@ -1446,11 +1454,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// Normalizes associated types in `value`, potentially returning /// new obligations that must further be processed. pub fn partially_normalize_associated_types_in(&self, - span: Span, - body_id: ast::NodeId, - param_env: ty::ParamEnv<'tcx>, - value: &T) - -> InferOk<'tcx, T> + span: Span, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'tcx>, + value: &T) + -> InferOk<'tcx, T> where T : TypeFoldable<'tcx> { debug!("partially_normalize_associated_types_in(value={:?})", value); @@ -1463,6 +1471,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { obligations); InferOk { value, obligations } } + + fn borrow_region_constraints(&self) -> RefMut<'_, RegionConstraintCollector<'tcx>> { + RefMut::map( + self.region_constraints.borrow_mut(), + |c| c.as_mut().expect("region constraints already solved")) + } } impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> { diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 53d963832426b..5e70c0ce368fc 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -75,8 +75,8 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { ty::ReVar(rid) => - self.infcx.region_constraints.borrow_mut() - .opportunistic_resolve_var(self.tcx(), rid), + self.infcx.borrow_region_constraints() + .opportunistic_resolve_var(self.tcx(), rid), _ => r, } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index bc4bb0c4712fa..f891f692c7d82 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -137,7 +137,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> // from the "cause" field, we could perhaps give more tailored // error messages. let origin = SubregionOrigin::Subtype(self.fields.trace.clone()); - self.fields.infcx.region_constraints.borrow_mut().make_subregion(origin, a, b); + self.fields.infcx.borrow_region_constraints() + .make_subregion(origin, a, b); Ok(a) } From d975b11eeb0e2049b79431e7217b62048bfd1684 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 11:59:49 -0500 Subject: [PATCH 30/68] separate the `Collector` from the `Data` it is collecting --- .../infer/lexical_region_resolve/graphviz.rs | 20 +-- .../infer/lexical_region_resolve/mod.rs | 10 +- src/librustc/infer/mod.rs | 9 +- src/librustc/infer/region_constraints/mod.rs | 170 ++++++++++-------- 4 files changed, 112 insertions(+), 97 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index 7f74da023897b..ea6b74f6e05ec 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -25,7 +25,7 @@ use middle::free_region::RegionRelations; use middle::region; use super::Constraint; use infer::SubregionOrigin; -use infer::region_constraints::RegionConstraintCollector; +use infer::region_constraints::RegionConstraintData; use util::nodemap::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -56,7 +56,7 @@ graphs will be printed. \n\ } pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( - region_constraints: &RegionConstraintCollector<'tcx>, + region_data: &RegionConstraintData<'tcx>, region_rels: &RegionRelations<'a, 'gcx, 'tcx>) { let tcx = region_rels.tcx; @@ -112,7 +112,7 @@ pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>( } }; - match dump_region_constraints_to(region_rels, ®ion_constraints.constraints, &output_path) { + match dump_region_data_to(region_rels, ®ion_data.constraints, &output_path) { Ok(()) => {} Err(e) => { let msg = format!("io error dumping region constraints: {}", e); @@ -266,15 +266,15 @@ impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { pub type ConstraintMap<'tcx> = FxHashMap, SubregionOrigin<'tcx>>; -fn dump_region_constraints_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>, - map: &ConstraintMap<'tcx>, - path: &str) - -> io::Result<()> { - debug!("dump_region_constraints map (len: {}) path: {}", +fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>, + map: &ConstraintMap<'tcx>, + path: &str) + -> io::Result<()> { + debug!("dump_region_data map (len: {}) path: {}", map.len(), path); - let g = ConstraintGraph::new(format!("region_constraints"), region_rels, map); - debug!("dump_region_constraints calling render"); + let g = ConstraintGraph::new(format!("region_data"), region_rels, map); + debug!("dump_region_data calling render"); let mut v = Vec::new(); dot::render(&g, &mut v).unwrap(); File::create(path).and_then(|mut f| f.write_all(&v)) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 9a02b274b5fb0..3522420a5dc13 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -14,7 +14,7 @@ use infer::SubregionOrigin; use infer::RegionVariableOrigin; use infer::region_constraints::Constraint; use infer::region_constraints::GenericKind; -use infer::region_constraints::RegionConstraintCollector; +use infer::region_constraints::RegionConstraintData; use infer::region_constraints::VerifyBound; use middle::free_region::RegionRelations; use rustc_data_structures::fx::FxHashSet; @@ -73,20 +73,20 @@ struct RegionAndOrigin<'tcx> { type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; -impl<'tcx> RegionConstraintCollector<'tcx> { +impl<'tcx> RegionConstraintData<'tcx> { /// This function performs the actual region resolution. It must be /// called after all constraints have been added. It performs a /// fixed-point iteration to find region values which satisfy all /// constraints, assuming such values can be found; if they cannot, /// errors are reported. pub fn resolve_regions( - &mut self, + mut self, region_rels: &RegionRelations<'_, '_, 'tcx>, ) -> ( LexicalRegionResolutions<'tcx>, Vec>, ) { - debug!("RegionConstraintCollector: resolve_regions()"); + debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; let values = self.infer_variable_values(region_rels, &mut errors); (values, errors) @@ -642,7 +642,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { return (result, dup_found); fn process_edges<'tcx>( - this: &RegionConstraintCollector<'tcx>, + this: &RegionConstraintData<'tcx>, state: &mut WalkState<'tcx>, graph: &RegionGraph<'tcx>, source_vid: RegionVid, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index a5cae839aa70b..53112bcd88073 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1132,10 +1132,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context, region_map, free_regions); - let mut region_constraints = self.region_constraints.borrow_mut() - .take() - .expect("regions already resolved"); - let (lexical_region_resolutions, errors) = region_constraints.resolve_regions(®ion_rels); + let region_data = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_data(); + let (lexical_region_resolutions, errors) = region_data.resolve_regions(®ion_rels); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 9ac9c620125e2..c7ac5f240e4c7 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -29,6 +29,69 @@ use std::u32; mod taint; +pub struct RegionConstraintCollector<'tcx> { + data: RegionConstraintData<'tcx>, + lubs: CombineMap<'tcx>, + glbs: CombineMap<'tcx>, + skolemization_count: u32, + bound_count: u32, + + /// The undo log records actions that might later be undone. + /// + /// Note: when the undo_log is empty, we are not actively + /// snapshotting. When the `start_snapshot()` method is called, we + /// push an OpenSnapshot entry onto the list to indicate that we + /// are now actively snapshotting. The reason for this is that + /// otherwise we end up adding entries for things like the lower + /// bound on a variable and so forth, which can never be rolled + /// back. + undo_log: Vec>, + + unification_table: UnificationTable, +} + +/// The full set of region constraints gathered up by the collector. +/// Describes a set of region variables ranging from 0..N (where N is +/// the length of the `var_origins` vector), and various constraints +/// between them. +#[derive(Default)] +pub struct RegionConstraintData<'tcx> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + pub var_origins: Vec, + + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: FxHashMap, SubregionOrigin<'tcx>>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec>, + + /// A "given" is a relationship that is known to hold. In + /// particular, we often know from closure fn signatures that a + /// particular free region must be a subregion of a region + /// variable: + /// + /// foo.iter().filter(<'a> |x: &'a &'b T| ...) + /// + /// In situations like this, `'b` is in fact a region variable + /// introduced by the call to `iter()`, and `'a` is a bound region + /// on the closure (as indicated by the `<'a>` prefix). If we are + /// naive, we wind up inferring that `'b` must be `'static`, + /// because we require that it be greater than `'a` and we do not + /// know what `'a` is precisely. + /// + /// This hashmap is used to avoid that naive scenario. Basically + /// we record the fact that `'a <= 'b` is implied by the fn + /// signature, and then ignore the constraint when solving + /// equations. This is a bit of a hack but seems to work. + pub givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>, +} + /// A constraint that influences the inference process. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum Constraint<'tcx> { @@ -142,59 +205,6 @@ enum CombineMapType { type CombineMap<'tcx> = FxHashMap, RegionVid>; -pub struct RegionConstraintCollector<'tcx> { - pub(in infer) var_origins: Vec, - - /// Constraints of the form `A <= B` introduced by the region - /// checker. Here at least one of `A` and `B` must be a region - /// variable. - pub(in infer) constraints: FxHashMap, SubregionOrigin<'tcx>>, - - /// A "verify" is something that we need to verify after inference is - /// done, but which does not directly affect inference in any way. - /// - /// An example is a `A <= B` where neither `A` nor `B` are - /// inference variables. - pub(in infer) verifys: Vec>, - - /// A "given" is a relationship that is known to hold. In particular, - /// we often know from closure fn signatures that a particular free - /// region must be a subregion of a region variable: - /// - /// foo.iter().filter(<'a> |x: &'a &'b T| ...) - /// - /// In situations like this, `'b` is in fact a region variable - /// introduced by the call to `iter()`, and `'a` is a bound region - /// on the closure (as indicated by the `<'a>` prefix). If we are - /// naive, we wind up inferring that `'b` must be `'static`, - /// because we require that it be greater than `'a` and we do not - /// know what `'a` is precisely. - /// - /// This hashmap is used to avoid that naive scenario. Basically we - /// record the fact that `'a <= 'b` is implied by the fn signature, - /// and then ignore the constraint when solving equations. This is - /// a bit of a hack but seems to work. - pub(in infer) givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>, - - lubs: CombineMap<'tcx>, - glbs: CombineMap<'tcx>, - skolemization_count: u32, - bound_count: u32, - - /// The undo log records actions that might later be undone. - /// - /// Note: when the undo_log is empty, we are not actively - /// snapshotting. When the `start_snapshot()` method is called, we - /// push an OpenSnapshot entry onto the list to indicate that we - /// are now actively snapshotting. The reason for this is that - /// otherwise we end up adding entries for things like the lower - /// bound on a variable and so forth, which can never be rolled - /// back. - undo_log: Vec>, - - unification_table: UnificationTable, -} - pub struct RegionSnapshot { length: usize, region_snapshot: unify::Snapshot, @@ -238,10 +248,7 @@ impl TaintDirections { impl<'tcx> RegionConstraintCollector<'tcx> { pub fn new() -> RegionConstraintCollector<'tcx> { RegionConstraintCollector { - var_origins: Vec::new(), - constraints: FxHashMap(), - verifys: Vec::new(), - givens: FxHashSet(), + data: RegionConstraintData::default(), lubs: FxHashMap(), glbs: FxHashMap(), skolemization_count: 0, @@ -251,6 +258,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } + /// Once all the constraints have been gathered, extract out the final data. + pub fn into_data(self) -> RegionConstraintData<'tcx> { + self.data + } + fn in_snapshot(&self) -> bool { !self.undo_log.is_empty() } @@ -310,18 +322,18 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // nothing to do here } AddVar(vid) => { - self.var_origins.pop().unwrap(); - assert_eq!(self.var_origins.len(), vid.index as usize); + self.data.var_origins.pop().unwrap(); + assert_eq!(self.data.var_origins.len(), vid.index as usize); } AddConstraint(ref constraint) => { - self.constraints.remove(constraint); + self.data.constraints.remove(constraint); } AddVerify(index) => { - self.verifys.pop(); - assert_eq!(self.verifys.len(), index); + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); } AddGiven(sub, sup) => { - self.givens.remove(&(sub, sup)); + self.data.givens.remove(&(sub, sup)); } AddCombination(Glb, ref regions) => { self.glbs.remove(regions); @@ -332,18 +344,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - pub fn num_vars(&self) -> u32 { - let len = self.var_origins.len(); - // enforce no overflow - assert!(len as u32 as usize == len); - len as u32 - } - pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { let vid = RegionVid { - index: self.num_vars(), + index: self.data.num_vars(), }; - self.var_origins.push(origin.clone()); + self.data.var_origins.push(origin.clone()); let u_vid = self.unification_table .new_key(unify_key::RegionVidKey { min_vid: vid }); @@ -360,7 +365,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.var_origins[vid.index as usize].clone() + self.data.var_origins[vid.index as usize].clone() } /// Creates a new skolemized region. Skolemized regions are fresh @@ -518,7 +523,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: add_constraint({:?})", constraint); - if self.constraints + if self.data.constraints .insert(constraint, origin) .is_none() { @@ -540,8 +545,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> { _ => {} } - let index = self.verifys.len(); - self.verifys.push(verify); + let index = self.data.verifys.len(); + self.data.verifys.push(verify); if self.in_snapshot() { self.undo_log.push(AddVerify(index)); } @@ -549,7 +554,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { // cannot add givens once regions are resolved - if self.givens.insert((sub, sup)) { + if self.data.givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); self.undo_log.push(AddGiven(sub, sup)); @@ -758,7 +763,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { taint_set.fixed_point( tcx, &self.undo_log[mark.length..], - &self.verifys, + &self.data.verifys, ); debug!("tainted: result={:?}", taint_set); return taint_set.into_set(); @@ -854,3 +859,12 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { } } } + +impl<'tcx> RegionConstraintData<'tcx> { + pub fn num_vars(&self) -> u32 { + let len = self.var_origins.len(); + // enforce no overflow + assert!(len as u32 as usize == len); + len as u32 + } +} From 4d7a1ab5db138314e898c375054c92a42517e370 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 12:03:49 -0500 Subject: [PATCH 31/68] fix error messages relating to removing lint for E0276 --- src/test/ui/compare-method/proj-outlives-region.stderr | 6 +----- src/test/ui/compare-method/region-unrelated.stderr | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/test/ui/compare-method/proj-outlives-region.stderr b/src/test/ui/compare-method/proj-outlives-region.stderr index e58251c846f8d..f871f034a924d 100644 --- a/src/test/ui/compare-method/proj-outlives-region.stderr +++ b/src/test/ui/compare-method/proj-outlives-region.stderr @@ -1,4 +1,4 @@ -error: impl has stricter requirements than trait +error[E0276]: impl has stricter requirements than trait --> $DIR/proj-outlives-region.rs:19:5 | 14 | fn foo() where T: 'a; @@ -6,10 +6,6 @@ error: impl has stricter requirements than trait ... 19 | fn foo() where U: 'a { } //~ ERROR E0276 | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a` - | - = note: #[deny(extra_requirement_in_impl)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #37166 error: aborting due to previous error diff --git a/src/test/ui/compare-method/region-unrelated.stderr b/src/test/ui/compare-method/region-unrelated.stderr index 95db68fea5cf7..1df83c7fb0c39 100644 --- a/src/test/ui/compare-method/region-unrelated.stderr +++ b/src/test/ui/compare-method/region-unrelated.stderr @@ -1,4 +1,4 @@ -error: impl has stricter requirements than trait +error[E0276]: impl has stricter requirements than trait --> $DIR/region-unrelated.rs:19:5 | 14 | fn foo() where T: 'a; @@ -6,10 +6,6 @@ error: impl has stricter requirements than trait ... 19 | fn foo() where V: 'a { } | ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `V: 'a` - | - = note: #[deny(extra_requirement_in_impl)] on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #37166 error: aborting due to previous error From 769b30dc74bf3461dbfa6f995332a3eb62308413 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:06:46 -0500 Subject: [PATCH 32/68] make `RegionVid` implement `Idx` and use `IndexVec` --- .../infer/lexical_region_resolve/mod.rs | 37 ++++++++++++------- src/librustc/infer/region_constraints/mod.rs | 19 ++++------ src/librustc/ty/sty.rs | 13 +++++++ src/librustc_data_structures/indexed_vec.rs | 5 +++ 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 3522420a5dc13..3aeecaf166ac6 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -17,6 +17,7 @@ use infer::region_constraints::GenericKind; use infer::region_constraints::RegionConstraintData; use infer::region_constraints::VerifyBound; use middle::free_region::RegionRelations; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING}; use std::fmt; @@ -29,7 +30,7 @@ use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; mod graphviz; pub struct LexicalRegionResolutions<'tcx> { - values: Vec>, + values: IndexVec>, error_region: ty::Region<'tcx>, } @@ -114,7 +115,7 @@ impl<'tcx> RegionConstraintData<'tcx> { (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { span_bug!( - self.var_origins[v_id.index as usize].span(), + self.var_origins[v_id].span(), "lub_concrete_regions invoked with non-concrete \ regions: {:?}, {:?}", a, @@ -211,7 +212,7 @@ impl<'tcx> RegionConstraintData<'tcx> { fn construct_var_data(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> LexicalRegionResolutions<'tcx> { LexicalRegionResolutions { error_region: tcx.types.re_static, - values: (0..self.num_vars() as usize) + values: (0..self.num_vars()) .map(|_| VarValue::Value(tcx.types.re_empty)) .collect(), } @@ -240,11 +241,20 @@ impl<'tcx> RegionConstraintData<'tcx> { let seeds: Vec<_> = self.givens.iter().cloned().collect(); for (r, vid) in seeds { + + // While all things transitively reachable in the graph + // from the variable (`'0` in the example above). let seed_index = NodeIndex(vid.index as usize); for succ_index in graph.depth_traverse(seed_index, OUTGOING) { - let succ_index = succ_index.0 as u32; + let succ_index = succ_index.0; + + // The first N nodes correspond to the region + // variables. Other nodes correspond to constant + // regions. if succ_index < self.num_vars() { - let succ_vid = RegionVid { index: succ_index }; + let succ_vid = RegionVid::new(succ_index); + + // Add `'c <= '1`. self.givens.insert((r, succ_vid)); } } @@ -442,11 +452,10 @@ impl<'tcx> RegionConstraintData<'tcx> { // idea is to report errors that derive from independent // regions of the graph, but not those that derive from // overlapping locations. - let mut dup_vec = vec![u32::MAX; self.num_vars() as usize]; + let mut dup_vec = vec![u32::MAX; self.num_vars()]; - for index in 0..self.num_vars() { - let node_vid = RegionVid { index }; - match var_data.value(node_vid) { + for (node_vid, value) in var_data.values.iter_enumerated() { + match *value { VarValue::Value(_) => { /* Inference successful */ } VarValue::ErrorValue => { /* Inference impossible, this value contains @@ -560,7 +569,7 @@ impl<'tcx> RegionConstraintData<'tcx> { for lower_bound in &lower_bounds { for upper_bound in &upper_bounds { if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { - let origin = self.var_origins[node_idx.index as usize].clone(); + let origin = self.var_origins[node_idx].clone(); debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ sup: {:?}", @@ -582,7 +591,7 @@ impl<'tcx> RegionConstraintData<'tcx> { } span_bug!( - self.var_origins[node_idx.index as usize].span(), + self.var_origins[node_idx].span(), "collect_error_for_expanding_node() could not find \ error for var {:?}, lower_bounds={:?}, \ upper_bounds={:?}", @@ -741,15 +750,15 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { } fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { - &self.values[rid.index as usize] + &self.values[rid] } fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> { - &mut self.values[rid.index as usize] + &mut self.values[rid] } pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - let result = match self.values[rid.index as usize] { + let result = match self.values[rid] { VarValue::Value(r) => r, VarValue::ErrorValue => self.error_region, }; diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index c7ac5f240e4c7..21301bf435878 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -16,6 +16,7 @@ use self::CombineMapType::*; use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; use super::unify_key; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unify::{self, UnificationTable}; use ty::{self, Ty, TyCtxt}; @@ -50,6 +51,8 @@ pub struct RegionConstraintCollector<'tcx> { unification_table: UnificationTable, } +pub type VarOrigins = IndexVec; + /// The full set of region constraints gathered up by the collector. /// Describes a set of region variables ranging from 0..N (where N is /// the length of the `var_origins` vector), and various constraints @@ -57,7 +60,7 @@ pub struct RegionConstraintCollector<'tcx> { #[derive(Default)] pub struct RegionConstraintData<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. - pub var_origins: Vec, + pub var_origins: IndexVec, /// Constraints of the form `A <= B`, where either `A` or `B` can /// be a region variable (or neither, as it happens). @@ -345,10 +348,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { - let vid = RegionVid { - index: self.data.num_vars(), - }; - self.data.var_origins.push(origin.clone()); + let vid = self.data.var_origins.push(origin.clone()); let u_vid = self.unification_table .new_key(unify_key::RegionVidKey { min_vid: vid }); @@ -365,7 +365,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.data.var_origins[vid.index as usize].clone() + self.data.var_origins[vid].clone() } /// Creates a new skolemized region. Skolemized regions are fresh @@ -861,10 +861,7 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { } impl<'tcx> RegionConstraintData<'tcx> { - pub fn num_vars(&self) -> u32 { - let len = self.var_origins.len(); - // enforce no overflow - assert!(len as u32 as usize == len); - len as u32 + pub fn num_vars(&self) -> usize { + self.var_origins.len() } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index d0ac7d0183a58..307fd921218cd 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -14,6 +14,7 @@ use hir::def_id::DefId; use middle::const_val::ConstVal; use middle::region; +use rustc_data_structures::indexed_vec::Idx; use ty::subst::{Substs, Subst}; use ty::{self, AdtDef, TypeFlags, Ty, TyCtxt, TypeFoldable}; use ty::{Slice, TyS}; @@ -898,6 +899,18 @@ pub struct RegionVid { pub index: u32, } +// TODO after rebasing, should be able to use `newtype_index!` +impl Idx for RegionVid { + fn new(value: usize) -> Self { + assert!(value < ::std::u32::MAX as usize); + RegionVid { index: value as u32 } + } + + fn index(self) -> usize { + self.index as usize + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub struct SkolemizedRegionVid { pub index: u32, diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index a733e9de5a1ab..622e8c51bee9d 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -384,6 +384,11 @@ impl IndexVec { idx } + #[inline] + pub fn pop(&mut self) -> Option { + self.raw.pop() + } + #[inline] pub fn len(&self) -> usize { self.raw.len() From 7129376d42d0bfed67914528fb8ffbc06df04f30 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 18:46:37 -0500 Subject: [PATCH 33/68] region_constraints: only push givens into undo-log if in a snapshot --- src/librustc/infer/region_constraints/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 21301bf435878..d3d27a48e8f45 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -557,7 +557,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { if self.data.givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); - self.undo_log.push(AddGiven(sub, sup)); + if self.in_snapshot() { + self.undo_log.push(AddGiven(sub, sup)); + } } } From 6c35c6d85099bda72993f2699a381a256d1fe613 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:37:55 -0500 Subject: [PATCH 34/68] split the `var_origins` from the `RegionConstraintData` --- .../infer/lexical_region_resolve/mod.rs | 316 +++++++++--------- src/librustc/infer/mod.rs | 19 +- src/librustc/infer/region_constraints/mod.rs | 53 ++- 3 files changed, 206 insertions(+), 182 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 3aeecaf166ac6..e097bf27c473a 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -15,6 +15,7 @@ use infer::RegionVariableOrigin; use infer::region_constraints::Constraint; use infer::region_constraints::GenericKind; use infer::region_constraints::RegionConstraintData; +use infer::region_constraints::VarOrigins; use infer::region_constraints::VerifyBound; use middle::free_region::RegionRelations; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; @@ -29,6 +30,28 @@ use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; mod graphviz; +/// This function performs lexical region resolution given a complete +/// set of constraints and variable origins. It performs a fixed-point +/// iteration to find region values which satisfy all constraints, +/// assuming such values can be found. It returns the final values of +/// all the variables as well as a set of errors that must be reported. +pub fn resolve<'tcx>( + region_rels: &RegionRelations<'_, '_, 'tcx>, + var_origins: VarOrigins, + data: RegionConstraintData<'tcx> +) -> ( + LexicalRegionResolutions<'tcx>, + Vec>, +) { + debug!("RegionConstraintData: resolve_regions()"); + let mut errors = vec![]; + let mut resolver = LexicalResolver { region_rels, var_origins, data }; + let values = resolver.infer_variable_values(&mut errors); + (values, errors) +} + +/// Contains the result of lexical region resolution. Offers methods +/// to lookup up the final value of a region variable. pub struct LexicalRegionResolutions<'tcx> { values: IndexVec>, error_region: ty::Region<'tcx>, @@ -74,139 +97,40 @@ struct RegionAndOrigin<'tcx> { type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; -impl<'tcx> RegionConstraintData<'tcx> { - /// This function performs the actual region resolution. It must be - /// called after all constraints have been added. It performs a - /// fixed-point iteration to find region values which satisfy all - /// constraints, assuming such values can be found; if they cannot, - /// errors are reported. - pub fn resolve_regions( - mut self, - region_rels: &RegionRelations<'_, '_, 'tcx>, - ) -> ( - LexicalRegionResolutions<'tcx>, - Vec>, - ) { - debug!("RegionConstraintData: resolve_regions()"); - let mut errors = vec![]; - let values = self.infer_variable_values(region_rels, &mut errors); - (values, errors) - } - - fn lub_concrete_regions( - &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, - a: Region<'tcx>, - b: Region<'tcx>, - ) -> Region<'tcx> { - let tcx = region_rels.tcx; - match (a, b) { - (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { - bug!("cannot relate region: LUB({:?}, {:?})", a, b); - } - - (r @ &ReStatic, _) | (_, r @ &ReStatic) => { - r // nothing lives longer than static - } - - (&ReEmpty, r) | (r, &ReEmpty) => { - r // everything lives longer than empty - } - - (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { - span_bug!( - self.var_origins[v_id].span(), - "lub_concrete_regions invoked with non-concrete \ - regions: {:?}, {:?}", - a, - b - ); - } - - (&ReEarlyBound(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReEarlyBound(_)) | - (&ReFree(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReFree(_)) => { - // A "free" region can be interpreted as "some region - // at least as big as fr.scope". So, we can - // reasonably compare free regions and scopes: - let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - region_rels.region_scope_tree.early_free_scope(region_rels.tcx, br) - } - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - region_rels.region_scope_tree.free_scope(region_rels.tcx, fr) - } - _ => bug!(), - }; - let r_id = region_rels - .region_scope_tree - .nearest_common_ancestor(fr_scope, s_id); - if r_id == fr_scope { - // if the free region's scope `fr.scope` is bigger than - // the scope region `s_id`, then the LUB is the free - // region itself: - match (a, b) { - (_, &ReScope(_)) => return a, - (&ReScope(_), _) => return b, - _ => bug!(), - } - } - - // otherwise, we don't know what the free region is, - // so we must conservatively say the LUB is static: - tcx.types.re_static - } - - (&ReScope(a_id), &ReScope(b_id)) => { - // The region corresponding to an outer block is a - // subtype of the region corresponding to an inner - // block. - let lub = region_rels - .region_scope_tree - .nearest_common_ancestor(a_id, b_id); - tcx.mk_region(ReScope(lub)) - } - - (&ReEarlyBound(_), &ReEarlyBound(_)) | - (&ReFree(_), &ReEarlyBound(_)) | - (&ReEarlyBound(_), &ReFree(_)) | - (&ReFree(_), &ReFree(_)) => region_rels.lub_free_regions(a, b), - - // For these types, we cannot define any additional - // relationship: - (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b { - a - } else { - tcx.types.re_static - }, - } - } +struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + region_rels: &'cx RegionRelations<'cx, 'gcx, 'tcx>, + var_origins: VarOrigins, + data: RegionConstraintData<'tcx> +} +impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { fn infer_variable_values( &mut self, - region_rels: &RegionRelations<'_, '_, 'tcx>, errors: &mut Vec>, ) -> LexicalRegionResolutions<'tcx> { - let mut var_data = self.construct_var_data(region_rels.tcx); + let mut var_data = self.construct_var_data(self.region_rels.tcx); // Dorky hack to cause `dump_constraints` to only get called // if debug mode is enabled: debug!( "----() End constraint listing (context={:?}) {:?}---", - region_rels.context, - self.dump_constraints(region_rels) + self.region_rels.context, + self.dump_constraints(self.region_rels) ); - graphviz::maybe_print_constraints_for(self, region_rels); + graphviz::maybe_print_constraints_for(&self.data, self.region_rels); let graph = self.construct_graph(); self.expand_givens(&graph); - self.expansion(region_rels, &mut var_data); - self.collect_errors(region_rels, &mut var_data, errors); - self.collect_var_errors(region_rels, &var_data, &graph, errors); + self.expansion(&mut var_data); + self.collect_errors(&mut var_data, errors); + self.collect_var_errors(&var_data, &graph, errors); var_data } + fn num_vars(&self) -> usize { + self.var_origins.len() + } + /// Initially, the value for all variables is set to `'empty`, the /// empty region. The `expansion` phase will grow this larger. fn construct_var_data(&self, tcx: TyCtxt<'_, '_, 'tcx>) -> LexicalRegionResolutions<'tcx> { @@ -223,7 +147,7 @@ impl<'tcx> RegionConstraintData<'tcx> { "----() Start constraint listing (context={:?}) ()----", free_regions.context ); - for (idx, (constraint, _)) in self.constraints.iter().enumerate() { + for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { debug!("Constraint {} => {:?}", idx, constraint); } } @@ -239,7 +163,7 @@ impl<'tcx> RegionConstraintData<'tcx> { // and '0 <= '1 // then 'c <= '1 - let seeds: Vec<_> = self.givens.iter().cloned().collect(); + let seeds: Vec<_> = self.data.givens.iter().cloned().collect(); for (r, vid) in seeds { // While all things transitively reachable in the graph @@ -255,7 +179,7 @@ impl<'tcx> RegionConstraintData<'tcx> { let succ_vid = RegionVid::new(succ_index); // Add `'c <= '1`. - self.givens.insert((r, succ_vid)); + self.data.givens.insert((r, succ_vid)); } } } @@ -263,7 +187,6 @@ impl<'tcx> RegionConstraintData<'tcx> { fn expansion( &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) { self.iterate_until_fixed_point("Expansion", |constraint, origin| { @@ -271,13 +194,13 @@ impl<'tcx> RegionConstraintData<'tcx> { match *constraint { Constraint::RegSubVar(a_region, b_vid) => { let b_data = var_values.value_mut(b_vid); - self.expand_node(region_rels, a_region, b_vid, b_data) + self.expand_node(a_region, b_vid, b_data) } Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) { VarValue::ErrorValue => false, VarValue::Value(a_region) => { let b_node = var_values.value_mut(b_vid); - self.expand_node(region_rels, a_region, b_vid, b_node) + self.expand_node(a_region, b_vid, b_node) } }, Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { @@ -291,7 +214,6 @@ impl<'tcx> RegionConstraintData<'tcx> { fn expand_node( &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, a_region: Region<'tcx>, b_vid: RegionVid, b_data: &mut VarValue<'tcx>, @@ -301,7 +223,7 @@ impl<'tcx> RegionConstraintData<'tcx> { // Check if this relationship is implied by a given. match *a_region { ty::ReEarlyBound(_) | ty::ReFree(_) => { - if self.givens.contains(&(a_region, b_vid)) { + if self.data.givens.contains(&(a_region, b_vid)) { debug!("given"); return false; } @@ -311,7 +233,7 @@ impl<'tcx> RegionConstraintData<'tcx> { match *b_data { VarValue::Value(cur_region) => { - let lub = self.lub_concrete_regions(region_rels, a_region, cur_region); + let lub = self.lub_concrete_regions(a_region, cur_region); if lub == cur_region { return false; } @@ -333,16 +255,105 @@ impl<'tcx> RegionConstraintData<'tcx> { } } + + fn lub_concrete_regions( + &self, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + let tcx = self.region_rels.tcx; + match (a, b) { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { + bug!("cannot relate region: LUB({:?}, {:?})", a, b); + } + + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + (&ReEmpty, r) | (r, &ReEmpty) => { + r // everything lives longer than empty + } + + (&ReVar(v_id), _) | (_, &ReVar(v_id)) => { + span_bug!( + self.var_origins[v_id].span(), + "lub_concrete_regions invoked with non-concrete \ + regions: {:?}, {:?}", + a, + b + ); + } + + (&ReEarlyBound(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReEarlyBound(_)) | + (&ReFree(_), &ReScope(s_id)) | + (&ReScope(s_id), &ReFree(_)) => { + // A "free" region can be interpreted as "some region + // at least as big as fr.scope". So, we can + // reasonably compare free regions and scopes: + let fr_scope = match (a, b) { + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { + self.region_rels.region_scope_tree.early_free_scope(self.region_rels.tcx, br) + } + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { + self.region_rels.region_scope_tree.free_scope(self.region_rels.tcx, fr) + } + _ => bug!(), + }; + let r_id = self.region_rels + .region_scope_tree + .nearest_common_ancestor(fr_scope, s_id); + if r_id == fr_scope { + // if the free region's scope `fr.scope` is bigger than + // the scope region `s_id`, then the LUB is the free + // region itself: + match (a, b) { + (_, &ReScope(_)) => return a, + (&ReScope(_), _) => return b, + _ => bug!(), + } + } + + // otherwise, we don't know what the free region is, + // so we must conservatively say the LUB is static: + tcx.types.re_static + } + + (&ReScope(a_id), &ReScope(b_id)) => { + // The region corresponding to an outer block is a + // subtype of the region corresponding to an inner + // block. + let lub = self.region_rels + .region_scope_tree + .nearest_common_ancestor(a_id, b_id); + tcx.mk_region(ReScope(lub)) + } + + (&ReEarlyBound(_), &ReEarlyBound(_)) | + (&ReFree(_), &ReEarlyBound(_)) | + (&ReEarlyBound(_), &ReFree(_)) | + (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), + + // For these types, we cannot define any additional + // relationship: + (&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b { + a + } else { + tcx.types.re_static + }, + } + } + /// After expansion is complete, go and check upper bounds (i.e., /// cases where the region cannot grow larger than a fixed point) /// and check that they are satisfied. fn collect_errors( &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, var_data: &mut LexicalRegionResolutions<'tcx>, errors: &mut Vec>, ) { - for (constraint, origin) in &self.constraints { + for (constraint, origin) in &self.data.constraints { debug!( "collect_errors: constraint={:?} origin={:?}", constraint, @@ -354,7 +365,7 @@ impl<'tcx> RegionConstraintData<'tcx> { } Constraint::RegSubReg(sub, sup) => { - if region_rels.is_subregion_of(sub, sup) { + if self.region_rels.is_subregion_of(sub, sup) { continue; } @@ -385,7 +396,7 @@ impl<'tcx> RegionConstraintData<'tcx> { // Do not report these errors immediately: // instead, set the variable value to error and // collect them later. - if !region_rels.is_subregion_of(a_region, b_region) { + if !self.region_rels.is_subregion_of(a_region, b_region) { debug!( "collect_errors: region error at {:?}: \ cannot verify that {:?}={:?} <= {:?}", @@ -400,7 +411,7 @@ impl<'tcx> RegionConstraintData<'tcx> { } } - for verify in &self.verifys { + for verify in &self.data.verifys { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(verify.region); @@ -410,7 +421,7 @@ impl<'tcx> RegionConstraintData<'tcx> { continue; } - if verify.bound.is_met(region_rels, var_data, sub) { + if self.bound_is_met(&verify.bound, var_data, sub) { continue; } @@ -434,7 +445,6 @@ impl<'tcx> RegionConstraintData<'tcx> { /// and create a `RegionResolutionError` for each of them. fn collect_var_errors( &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, var_data: &LexicalRegionResolutions<'tcx>, graph: &RegionGraph<'tcx>, errors: &mut Vec>, @@ -481,7 +491,6 @@ impl<'tcx> RegionConstraintData<'tcx> { starts to create problems we'll have to revisit this portion of the code and think hard about it. =) */ self.collect_error_for_expanding_node( - region_rels, graph, &mut dup_vec, node_vid, @@ -509,7 +518,7 @@ impl<'tcx> RegionConstraintData<'tcx> { let dummy_source = graph.add_node(()); let dummy_sink = graph.add_node(()); - for (constraint, _) in &self.constraints { + for (constraint, _) in &self.data.constraints { match *constraint { Constraint::VarSubVar(a_id, b_id) => { graph.add_edge( @@ -536,7 +545,6 @@ impl<'tcx> RegionConstraintData<'tcx> { fn collect_error_for_expanding_node( &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, graph: &RegionGraph<'tcx>, dup_vec: &mut [u32], node_idx: RegionVid, @@ -568,7 +576,7 @@ impl<'tcx> RegionConstraintData<'tcx> { for lower_bound in &lower_bounds { for upper_bound in &upper_bounds { - if !region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { + if !self.region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { let origin = self.var_origins[node_idx].clone(); debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ @@ -624,7 +632,7 @@ impl<'tcx> RegionConstraintData<'tcx> { // to start off the process, walk the source node in the // direction specified - process_edges(self, &mut state, graph, orig_node_idx, dir); + process_edges(&self.data, &mut state, graph, orig_node_idx, dir); while !state.stack.is_empty() { let node_idx = state.stack.pop().unwrap(); @@ -642,7 +650,7 @@ impl<'tcx> RegionConstraintData<'tcx> { node_idx ); - process_edges(self, &mut state, graph, node_idx, dir); + process_edges(&self.data, &mut state, graph, node_idx, dir); } let WalkState { @@ -699,7 +707,7 @@ impl<'tcx> RegionConstraintData<'tcx> { changed = false; iteration += 1; debug!("---- {} Iteration {}{}", "#", tag, iteration); - for (constraint, origin) in &self.constraints { + for (constraint, origin) in &self.data.constraints { let edge_changed = body(constraint, origin); if edge_changed { debug!("Updated due to constraint {:?}", constraint); @@ -709,38 +717,36 @@ impl<'tcx> RegionConstraintData<'tcx> { } debug!("---- {} Complete after {} iteration(s)", tag, iteration); } -} -impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) - } -} - - -impl<'tcx> VerifyBound<'tcx> { - fn is_met( + fn bound_is_met( &self, - region_rels: &RegionRelations<'_, '_, 'tcx>, + bound: &VerifyBound<'tcx>, var_values: &LexicalRegionResolutions<'tcx>, min: ty::Region<'tcx>, ) -> bool { - match self { + match bound { VerifyBound::AnyRegion(rs) => rs.iter() .map(|&r| var_values.normalize(r)) - .any(|r| region_rels.is_subregion_of(min, r)), + .any(|r| self.region_rels.is_subregion_of(min, r)), VerifyBound::AllRegions(rs) => rs.iter() .map(|&r| var_values.normalize(r)) - .all(|r| region_rels.is_subregion_of(min, r)), + .all(|r| self.region_rels.is_subregion_of(min, r)), - VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.is_met(region_rels, var_values, min)), + VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)), - VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.is_met(region_rels, var_values, min)), + VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)), } } } +impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) + } +} + + impl<'tcx> LexicalRegionResolutions<'tcx> { fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 53112bcd88073..a1ad65a6c4a48 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1128,15 +1128,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { "region_obligations not empty: {:#?}", self.region_obligations.borrow()); - let region_rels = RegionRelations::new(self.tcx, - region_context, - region_map, - free_regions); - let region_data = self.region_constraints.borrow_mut() - .take() - .expect("regions already resolved") - .into_data(); - let (lexical_region_resolutions, errors) = region_data.resolve_regions(®ion_rels); + let region_rels = &RegionRelations::new(self.tcx, + region_context, + region_map, + free_regions); + let (var_origins, data) = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_origins_and_data(); + let (lexical_region_resolutions, errors) = + lexical_region_resolve::resolve(region_rels, var_origins, data); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index d3d27a48e8f45..aaa45fc11f4c3 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -31,10 +31,26 @@ use std::u32; mod taint; pub struct RegionConstraintCollector<'tcx> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + pub var_origins: IndexVec, + data: RegionConstraintData<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. lubs: CombineMap<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. glbs: CombineMap<'tcx>, + + /// Number of skolemized variables currently active. skolemization_count: u32, + + /// Global counter used during the GLB algorithm to create unique + /// names for fresh bound regions bound_count: u32, /// The undo log records actions that might later be undone. @@ -48,20 +64,25 @@ pub struct RegionConstraintCollector<'tcx> { /// back. undo_log: Vec>, + /// When we add a R1 == R2 constriant, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when dropck and other such code + /// is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that + /// have been equated but appear distinct. unification_table: UnificationTable, } pub type VarOrigins = IndexVec; /// The full set of region constraints gathered up by the collector. -/// Describes a set of region variables ranging from 0..N (where N is -/// the length of the `var_origins` vector), and various constraints -/// between them. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. #[derive(Default)] pub struct RegionConstraintData<'tcx> { - /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. - pub var_origins: IndexVec, - /// Constraints of the form `A <= B`, where either `A` or `B` can /// be a region variable (or neither, as it happens). pub constraints: FxHashMap, SubregionOrigin<'tcx>>, @@ -251,6 +272,7 @@ impl TaintDirections { impl<'tcx> RegionConstraintCollector<'tcx> { pub fn new() -> RegionConstraintCollector<'tcx> { RegionConstraintCollector { + var_origins: VarOrigins::default(), data: RegionConstraintData::default(), lubs: FxHashMap(), glbs: FxHashMap(), @@ -262,8 +284,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } /// Once all the constraints have been gathered, extract out the final data. - pub fn into_data(self) -> RegionConstraintData<'tcx> { - self.data + pub fn into_origins_and_data(self) -> (VarOrigins, RegionConstraintData<'tcx>) { + (self.var_origins, self.data) } fn in_snapshot(&self) -> bool { @@ -325,8 +347,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // nothing to do here } AddVar(vid) => { - self.data.var_origins.pop().unwrap(); - assert_eq!(self.data.var_origins.len(), vid.index as usize); + self.var_origins.pop().unwrap(); + assert_eq!(self.var_origins.len(), vid.index as usize); } AddConstraint(ref constraint) => { self.data.constraints.remove(constraint); @@ -348,7 +370,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } pub fn new_region_var(&mut self, origin: RegionVariableOrigin) -> RegionVid { - let vid = self.data.var_origins.push(origin.clone()); + let vid = self.var_origins.push(origin.clone()); let u_vid = self.unification_table .new_key(unify_key::RegionVidKey { min_vid: vid }); @@ -364,8 +386,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { return vid; } + /// Returns the origin for the given variable. pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { - self.data.var_origins[vid].clone() + self.var_origins[vid].clone() } /// Creates a new skolemized region. Skolemized regions are fresh @@ -861,9 +884,3 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { } } } - -impl<'tcx> RegionConstraintData<'tcx> { - pub fn num_vars(&self) -> usize { - self.var_origins.len() - } -} From 965650adfa4068ee535e4f3c932fdfecf9c1f441 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:42:56 -0500 Subject: [PATCH 35/68] add method `take_and_reset_region_constraints` to `InferCtxt` --- src/librustc/infer/mod.rs | 16 +++++++++++++++- src/librustc/infer/region_constraints/mod.rs | 11 +++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index a1ad65a6c4a48..21f427fa80c13 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -16,7 +16,7 @@ pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; pub use ty::IntVarValue; pub use self::freshen::TypeFreshener; -pub use self::region_constraints::{GenericKind, VerifyBound}; +pub use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData}; use hir::def_id::DefId; use middle::free_region::{FreeRegionMap, RegionRelations}; @@ -1152,6 +1152,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + /// Obtains (and clears) the current set of region + /// constraints. The inference context is still usable: further + /// unifications will simply add new constraints. + /// + /// This method is not meant to be used with normal lexical region + /// resolution. Rather, it is used in the NLL mode as a kind of + /// interim hack: basically we run normal type-check and generate + /// region constraints as normal, but then we take them and + /// translate them into the form that the NLL solver + /// understands. See the NLL module for mode details. + pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> { + self.borrow_region_constraints().take_and_reset_data() + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_type_vars_if_possible(&t).to_string() } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index aaa45fc11f4c3..f9fd1278bdfca 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -284,10 +284,21 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } /// Once all the constraints have been gathered, extract out the final data. + /// + /// Not legal during a snapshot. pub fn into_origins_and_data(self) -> (VarOrigins, RegionConstraintData<'tcx>) { + assert!(!self.in_snapshot()); (self.var_origins, self.data) } + /// Takes (and clears) the current set of constraints. Note that the set of + /// variables remains intact. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { + mem::replace(&mut self.data, RegionConstraintData::default()) + } + fn in_snapshot(&self) -> bool { !self.undo_log.is_empty() } From 3eed63a2b83c89b36f085e000470ffde74ed6e14 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:46:58 -0500 Subject: [PATCH 36/68] rustfmt `lexical_region_resolve` --- .../infer/lexical_region_resolve/mod.rs | 56 ++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index e097bf27c473a..0692d284d7c16 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -38,14 +38,18 @@ mod graphviz; pub fn resolve<'tcx>( region_rels: &RegionRelations<'_, '_, 'tcx>, var_origins: VarOrigins, - data: RegionConstraintData<'tcx> + data: RegionConstraintData<'tcx>, ) -> ( LexicalRegionResolutions<'tcx>, Vec>, ) { debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; - let mut resolver = LexicalResolver { region_rels, var_origins, data }; + let mut resolver = LexicalResolver { + region_rels, + var_origins, + data, + }; let values = resolver.infer_variable_values(&mut errors); (values, errors) } @@ -100,7 +104,7 @@ type RegionGraph<'tcx> = graph::Graph<(), Constraint<'tcx>>; struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> { region_rels: &'cx RegionRelations<'cx, 'gcx, 'tcx>, var_origins: VarOrigins, - data: RegionConstraintData<'tcx> + data: RegionConstraintData<'tcx>, } impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { @@ -165,7 +169,6 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { let seeds: Vec<_> = self.data.givens.iter().cloned().collect(); for (r, vid) in seeds { - // While all things transitively reachable in the graph // from the variable (`'0` in the example above). let seed_index = NodeIndex(vid.index as usize); @@ -185,10 +188,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { } } - fn expansion( - &self, - var_values: &mut LexicalRegionResolutions<'tcx>, - ) { + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { self.iterate_until_fixed_point("Expansion", |constraint, origin| { debug!("expansion: constraint={:?} origin={:?}", constraint, origin); match *constraint { @@ -222,12 +222,11 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { // Check if this relationship is implied by a given. match *a_region { - ty::ReEarlyBound(_) | ty::ReFree(_) => { - if self.data.givens.contains(&(a_region, b_vid)) { - debug!("given"); - return false; - } - } + ty::ReEarlyBound(_) | ty::ReFree(_) => if self.data.givens.contains(&(a_region, b_vid)) + { + debug!("given"); + return false; + }, _ => {} } @@ -256,11 +255,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { } - fn lub_concrete_regions( - &self, - a: Region<'tcx>, - b: Region<'tcx>, - ) -> Region<'tcx> { + fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.region_rels.tcx; match (a, b) { (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { @@ -293,12 +288,12 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { // at least as big as fr.scope". So, we can // reasonably compare free regions and scopes: let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - self.region_rels.region_scope_tree.early_free_scope(self.region_rels.tcx, br) - } - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - self.region_rels.region_scope_tree.free_scope(self.region_rels.tcx, fr) - } + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels + .region_scope_tree + .early_free_scope(self.region_rels.tcx, br), + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels + .region_scope_tree + .free_scope(self.region_rels.tcx, fr), _ => bug!(), }; let r_id = self.region_rels @@ -490,12 +485,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { that is not used is not a problem, so if this rule starts to create problems we'll have to revisit this portion of the code and think hard about it. =) */ - self.collect_error_for_expanding_node( - graph, - &mut dup_vec, - node_vid, - errors, - ); + self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); } } } @@ -576,7 +566,9 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { for lower_bound in &lower_bounds { for upper_bound in &upper_bounds { - if !self.region_rels.is_subregion_of(lower_bound.region, upper_bound.region) { + if !self.region_rels + .is_subregion_of(lower_bound.region, upper_bound.region) + { let origin = self.var_origins[node_idx].clone(); debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ From 54a7deefb1ca0111e9433667feeb41cb83edf470 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:40:35 -0500 Subject: [PATCH 37/68] MIR typeck: rustfmt --- src/librustc_mir/transform/type_check.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index d016f3253539b..03de427df9e3c 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -391,10 +391,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { traits::ObligationCause::misc(span, self.body_id) } - fn fully_perform_op(&self, - op: OP) - -> Result> - where OP: FnOnce() -> InferResult<'tcx, R> + fn fully_perform_op(&self, op: OP) -> Result> + where + OP: FnOnce() -> InferResult<'tcx, R>, { let mut fulfill_cx = FulfillmentContext::new(); let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op())?; @@ -405,12 +404,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { Ok(value) } - fn sub_types( - &self, - sub: Ty<'tcx>, - sup: Ty<'tcx>, - _at_location: Location, - ) -> UnitResult<'tcx> { + fn sub_types(&self, sub: Ty<'tcx>, sup: Ty<'tcx>, _at_location: Location) -> UnitResult<'tcx> { self.fully_perform_op(|| { self.infcx .at(&self.misc(self.last_span), self.param_env) From 607daa07bf924fe294d9b2b6fbfbd613ad8f8343 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 18:47:09 -0500 Subject: [PATCH 38/68] MIR typeck: refactor to track region constraints --- src/librustc/infer/region_constraints/mod.rs | 8 + src/librustc_mir/transform/type_check.rs | 318 ++++++++++++++----- 2 files changed, 252 insertions(+), 74 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index f9fd1278bdfca..918850d0952f4 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -895,3 +895,11 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { } } } + +impl<'tcx> RegionConstraintData<'tcx> { + /// True if this region constraint data contains no constraints. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { constraints, verifys, givens } = self; + constraints.is_empty() && verifys.is_empty() && givens.is_empty() + } +} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 03de427df9e3c..9f22a6160b179 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,7 +11,8 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::infer::{InferCtxt, InferOk, InferResult, UnitResult}; +use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, + RegionConstraintData, UnitResult}; use rustc::traits::{self, FulfillmentContext}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; @@ -28,6 +29,34 @@ use syntax_pos::{Span, DUMMY_SP}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::Idx; +/// Type checks the given `mir` in the context of the inference +/// context `infcx`. Returns any region constraints that have yet to +/// be proven. +/// +/// This phase of type-check ought to be infallible -- this is because +/// the original, HIR-based type-check succeeded. So if any errors +/// occur here, we will get a `bug!` reported. +pub fn type_check<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + body_id: ast::NodeId, + param_env: ty::ParamEnv<'gcx>, + mir: &Mir<'tcx>, +) -> MirTypeckRegionConstraints<'tcx> { + let mut checker = TypeChecker::new(infcx, body_id, param_env); + let errors_reported = { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + verifier.errors_reported + }; + + if !errors_reported { + // if verifier failed, don't do further checks to avoid ICEs + checker.typeck_mir(mir); + } + + checker.constraints +} + fn mirbug(tcx: TyCtxt, span: Span, msg: &str) { tcx.sess.diagnostic().span_bug(span, msg); } @@ -128,7 +157,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() { + if ty.has_escaping_regions() || ty.references_error() { span_mirbug_and_err!(self, parent, "bad type {:?}", ty) } else { ty @@ -145,7 +174,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let sty = self.sanitize_type(lvalue, 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) { + if let Err(terr) = self.cx + .eq_types(self.last_span, ty, sty, location.at_self()) + { span_mirbug!( self, lvalue, @@ -267,16 +298,18 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { ProjectionElem::Field(field, fty) => { let fty = self.sanitize_type(lvalue, fty); match self.field_ty(lvalue, base, field, location) { - Ok(ty) => if let Err(terr) = self.cx.eq_types(span, ty, fty, location) { - span_mirbug!( - self, - lvalue, - "bad field access ({:?}: {:?}): {:?}", - ty, - fty, - terr - ); - }, + Ok(ty) => { + if let Err(terr) = self.cx.eq_types(span, ty, fty, location.at_self()) { + span_mirbug!( + self, + lvalue, + "bad field access ({:?}: {:?}): {:?}", + ty, + fty, + terr + ); + } + } Err(FieldAccessError::OutOfRange { field_count }) => span_mirbug!( self, lvalue, @@ -364,12 +397,61 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { } } +/// The MIR type checker. Visits the MIR and enforces all the +/// constraints needed for it to be valid and well-typed. Along the +/// way, it accrues region constraints -- these can later be used by +/// NLL region checking. pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, param_env: ty::ParamEnv<'gcx>, last_span: Span, body_id: ast::NodeId, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, + constraints: MirTypeckRegionConstraints<'tcx>, +} + +/// A collection of region constraints that must be satisfied for the +/// program to be considered well-typed. +#[derive(Default)] +pub struct MirTypeckRegionConstraints<'tcx> { + /// In general, the type-checker is not responsible for enforcing + /// liveness constraints; this job falls to the region inferencer, + /// which performs a liveness analysis. However, in some limited + /// cases, the MIR type-checker creates temporary regions that do + /// not otherwise appear in the MIR -- in particular, the + /// late-bound regions that it instantiates at call-sites -- and + /// hence it must report on their liveness constraints. + pub liveness_set: Vec<(ty::Region<'tcx>, Location)>, + + /// During the course of type-checking, we will accumulate region + /// constraints due to performing subtyping operations or solving + /// traits. These are accumulated into this vector for later use. + pub outlives_sets: Vec>, +} + +/// Outlives relationships between regions and types created at a +/// particular point within the control-flow graph. +pub struct OutlivesSet<'tcx> { + /// The locations associated with these constraints. + pub locations: Locations, + + /// Constraints generated. In terms of the NLL RFC, when you have + /// a constraint `R1: R2 @ P`, the data in there specifies things + /// like `R1: R2`. + pub data: RegionConstraintData<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +pub struct Locations { + /// The location in the MIR that generated these constraints. + /// This is intended for error reporting and diagnosis; the + /// constraints may *take effect* at a distinct spot. + pub from_location: Location, + + /// The constraints must be met at this location. In terms of the + /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field + /// is the `P` value. + pub at_location: Location, } impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { @@ -384,6 +466,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { body_id, param_env, reported_errors: FxHashSet(), + constraints: MirTypeckRegionConstraints::default(), } } @@ -391,37 +474,54 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { traits::ObligationCause::misc(span, self.body_id) } - fn fully_perform_op(&self, op: OP) -> Result> + fn fully_perform_op( + &mut self, + locations: Locations, + op: OP, + ) -> Result> where - OP: FnOnce() -> InferResult<'tcx, R>, + OP: FnOnce(&mut Self) -> InferResult<'tcx, R>, { let mut fulfill_cx = FulfillmentContext::new(); - let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op())?; + let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; fulfill_cx.register_predicate_obligations(self.infcx, obligations); if let Err(e) = fulfill_cx.select_all_or_error(self.infcx) { span_mirbug!(self, "", "errors selecting obligation: {:?}", e); - } // FIXME propagate + } + + let data = self.infcx.take_and_reset_region_constraints(); + if !data.is_empty() { + self.constraints + .outlives_sets + .push(OutlivesSet { locations, data }); + } + Ok(value) } - fn sub_types(&self, sub: Ty<'tcx>, sup: Ty<'tcx>, _at_location: Location) -> UnitResult<'tcx> { - self.fully_perform_op(|| { - self.infcx - .at(&self.misc(self.last_span), self.param_env) + fn sub_types( + &mut self, + sub: Ty<'tcx>, + sup: Ty<'tcx>, + locations: Locations, + ) -> UnitResult<'tcx> { + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) .sup(sup, sub) }) } fn eq_types( - &self, + &mut self, _span: Span, a: Ty<'tcx>, b: Ty<'tcx>, - _at_location: Location, + locations: Locations, ) -> UnitResult<'tcx> { - self.fully_perform_op(|| { - self.infcx - .at(&self.misc(self.last_span), self.param_env) + self.fully_perform_op(locations, |this| { + this.infcx + .at(&this.misc(this.last_span), this.param_env) .eq(b, a) }) } @@ -437,7 +537,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { StatementKind::Assign(ref lv, ref rv) => { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty, location.successor_within_block()) { + if let Err(terr) = + self.sub_types(rv_ty, lv_ty, location.at_successor_within_block()) + { span_mirbug!( self, stmt, @@ -482,7 +584,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_terminator(&mut self, mir: &Mir<'tcx>, term: &Terminator<'tcx>, location: Location) { + fn check_terminator( + &mut self, + mir: &Mir<'tcx>, + term: &Terminator<'tcx>, + term_location: Location, + ) { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { @@ -505,7 +612,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let lv_ty = location.ty(mir, tcx).to_ty(tcx); let rv_ty = value.ty(mir, tcx); - if let Err(terr) = self.sub_types(rv_ty, lv_ty, target.start_location()) { + let locations = Locations { + from_location: term_location, + at_location: target.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, lv_ty, locations) { span_mirbug!( self, term, @@ -520,7 +631,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // *both* blocks, so we need to ensure that it holds // at both locations. if let Some(unwind) = unwind { - if let Err(terr) = self.sub_types(rv_ty, lv_ty, unwind.start_location()) { + let locations = Locations { + from_location: term_location, + at_location: unwind.start_location(), + }; + if let Err(terr) = self.sub_types(rv_ty, lv_ty, locations) { span_mirbug!( self, term, @@ -538,7 +653,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { .. } => { let discr_ty = discr.ty(mir, tcx); - if let Err(terr) = self.sub_types(discr_ty, switch_ty, location) { + if let Err(terr) = self.sub_types(discr_ty, switch_ty, term_location.at_self()) { span_mirbug!( self, term, @@ -568,14 +683,31 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { return; } }; - let sig = tcx.erase_late_bound_regions(&sig); - let sig = self.normalize(&sig, location); - self.check_call_dest(mir, term, &sig, destination); + let (sig, map) = self.infcx.replace_late_bound_regions_with_fresh_var( + term.source_info.span, + LateBoundRegionConversionTime::FnCall, + &sig, + ); + let sig = self.normalize(&sig, term_location); + self.check_call_dest(mir, term, &sig, destination, term_location); + + // The ordinary liveness rules will ensure that all + // regions in the type of the callee are live here. We + // then further constrain the late-bound regions that + // were instantiated at the call site to be live as + // well. The resulting is that all the input (and + // output) types in the signature must be live, since + // all the inputs that fed into it were live. + for &late_bound_region in map.values() { + self.constraints + .liveness_set + .push((late_bound_region, term_location)); + } if self.is_box_free(func) { - self.check_box_free_inputs(mir, term, &sig, args, location); + self.check_box_free_inputs(mir, term, &sig, args, term_location); } else { - self.check_call_inputs(mir, term, &sig, args, location); + self.check_call_inputs(mir, term, &sig, args, term_location); } } TerminatorKind::Assert { @@ -599,16 +731,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let value_ty = value.ty(mir, tcx); match mir.yield_ty { None => span_mirbug!(self, term, "yield in non-generator"), - Some(ty) => if let Err(terr) = self.sub_types(value_ty, ty, location) { - span_mirbug!( - self, - term, - "type of yield value is {:?}, but the yield type is {:?}: {:?}", - value_ty, - ty, - terr - ); - }, + Some(ty) => { + if let Err(terr) = self.sub_types(value_ty, ty, term_location.at_self()) { + span_mirbug!( + self, + term, + "type of yield value is {:?}, but the yield type is {:?}: {:?}", + value_ty, + ty, + terr + ); + } + } } } } @@ -620,14 +754,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { term: &Terminator<'tcx>, sig: &ty::FnSig<'tcx>, destination: &Option<(Lvalue<'tcx>, BasicBlock)>, + term_location: Location, ) { let tcx = self.tcx(); match *destination { Some((ref dest, target_block)) => { let dest_ty = dest.ty(mir, tcx).to_ty(tcx); - if let Err(terr) = - self.sub_types(sig.output(), dest_ty, target_block.start_location()) - { + let locations = Locations { + from_location: term_location, + at_location: target_block.start_location(), + }; + if let Err(terr) = self.sub_types(sig.output(), dest_ty, locations) { span_mirbug!( self, term, @@ -653,7 +790,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { term: &Terminator<'tcx>, sig: &ty::FnSig<'tcx>, args: &[Operand<'tcx>], - location: Location, + term_location: Location, ) { debug!("check_call_inputs({:?}, {:?})", sig, args); if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.variadic) { @@ -661,7 +798,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } for (n, (fn_arg, op_arg)) in sig.inputs().iter().zip(args).enumerate() { let op_arg_ty = op_arg.ty(mir, self.tcx()); - if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, location) { + if let Err(terr) = self.sub_types(op_arg_ty, fn_arg, term_location.at_self()) { span_mirbug!( self, term, @@ -699,7 +836,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { term: &Terminator<'tcx>, sig: &ty::FnSig<'tcx>, args: &[Operand<'tcx>], - location: Location, + term_location: Location, ) { debug!("check_box_free_inputs"); @@ -733,7 +870,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } }; - if let Err(terr) = self.sub_types(arg_ty, pointee_ty, location) { + if let Err(terr) = self.sub_types(arg_ty, pointee_ty, term_location.at_self()) { span_mirbug!( self, term, @@ -805,9 +942,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => { - self.assert_iscleanup(mir, block, real_target, is_cleanup); + self.assert_iscleanup(mir, block_data, real_target, is_cleanup); for target in imaginary_targets { - self.assert_iscleanup(mir, block, *target, is_cleanup); + self.assert_iscleanup(mir, block_data, *target, is_cleanup); } } } @@ -831,7 +968,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_local(&mut self, mir: &Mir<'gcx>, local: Local, local_decl: &LocalDecl<'gcx>) { + fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { match mir.local_kind(local) { LocalKind::ReturnPointer | LocalKind::Arg => { // return values of normal functions are required to be @@ -847,7 +984,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let span = local_decl.source_info.span; let ty = local_decl.ty; - if !ty.is_sized(self.tcx().global_tcx(), self.param_env, span) { + + // Erase the regions from `ty` to get a global type. The + // `Sized` bound in no way depends on precise regions, so this + // shouldn't affect `is_sized`. + let gcx = self.tcx().global_tcx(); + let erased_ty = gcx.lift(&self.tcx().erase_regions(&ty)).unwrap(); + if !erased_ty.is_sized(gcx, self.param_env, span) { // in current MIR construction, all non-control-flow rvalue // expressions evaluate through `as_temp` or `into` a return // slot or local, so to find all unsized rvalues it is enough @@ -865,7 +1008,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn typeck_mir(&mut self, mir: &Mir<'gcx>) { + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); @@ -891,16 +1034,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - - fn normalize(&mut self, value: &T, _location: Location) -> T + fn normalize(&mut self, value: &T, location: Location) -> T where T: fmt::Debug + TypeFoldable<'tcx>, { - self.fully_perform_op(|| { - let mut selcx = traits::SelectionContext::new(self.infcx); - let cause = traits::ObligationCause::misc(self.last_span, ast::CRATE_NODE_ID); + self.fully_perform_op(location.at_self(), |this| { + let mut selcx = traits::SelectionContext::new(this.infcx); + let cause = traits::ObligationCause::misc(this.last_span, ast::CRATE_NODE_ID); let traits::Normalized { value, obligations } = - traits::normalize(&mut selcx, self.param_env, cause, value); + traits::normalize(&mut selcx, this.param_env, cause, value); Ok(InferOk { value, obligations }) }).unwrap() } @@ -921,16 +1063,44 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let mut checker = TypeChecker::new(&infcx, item_id, param_env); - { - let mut verifier = TypeVerifier::new(&mut checker, mir); - verifier.visit_mir(mir); - if verifier.errors_reported { - // don't do further checks to avoid ICEs - return; - } - } - checker.typeck_mir(mir); + let _region_constraint_sets = type_check(&infcx, item_id, param_env, mir); + + // For verification purposes, we just ignore the resulting + // region constraint sets. Not our problem. =) }); } } + +trait AtLocation { + /// Creates a `Locations` where `self` is both the from-location + /// and the at-location. This means that any required region + /// relationships must hold upon entering the statement/terminator + /// indicated by `self`. This is typically used when processing + /// "inputs" to the given location. + fn at_self(self) -> Locations; + + /// Creates a `Locations` where `self` is the from-location and + /// its successor within the block is the at-location. This means + /// that any required region relationships must hold only upon + /// **exiting** the statement/terminator indicated by `self`. This + /// is for example used when you have a `lv = rv` statement: it + /// indicates that the `typeof(rv) <: typeof(lv)` as of the + /// **next** statement. + fn at_successor_within_block(self) -> Locations; +} + +impl AtLocation for Location { + fn at_self(self) -> Locations { + Locations { + from_location: self, + at_location: self, + } + } + + fn at_successor_within_block(self) -> Locations { + Locations { + from_location: self, + at_location: self.successor_within_block(), + } + } +} From 33c73371cb7f82c4f839b579068f3a88cd35bba9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:04:45 -0500 Subject: [PATCH 39/68] replace `usize` with `RegionIndex` in indices map --- src/librustc_mir/lib.rs | 1 + src/librustc_mir/transform/nll/free_regions.rs | 17 ++++++++++------- src/librustc_mir/transform/nll/region_infer.rs | 14 ++++++-------- src/librustc_mir/transform/nll/renumber.rs | 5 +++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index b72823dff2bdc..b8bed053e379f 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -21,6 +21,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(i128_type)] +#![feature(match_default_bindings)] #![feature(rustc_diagnostic_macros)] #![feature(placement_in_syntax)] #![feature(collection_placement)] diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs index 006a2f9047ad9..f4b1024fa93f5 100644 --- a/src/librustc_mir/transform/nll/free_regions.rs +++ b/src/librustc_mir/transform/nll/free_regions.rs @@ -28,14 +28,17 @@ use rustc::mir::transform::MirSource; use rustc::ty; use rustc::ty::subst::Substs; use rustc::util::nodemap::FxHashMap; +use rustc_data_structures::indexed_vec::Idx; + +use super::RegionIndex; #[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. The - /// corresponding variable will be "capped" so that it cannot - /// grow. - pub indices: FxHashMap, usize>, + /// 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, RegionIndex>, /// The map from the typeck tables telling us how to relate free regions. pub free_region_map: &'tcx FreeRegionMap<'tcx>, @@ -80,9 +83,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>( } fn insert_free_region<'tcx>( - free_regions: &mut FxHashMap, usize>, + free_regions: &mut FxHashMap, RegionIndex>, region: ty::Region<'tcx>, ) { - let len = free_regions.len(); - free_regions.entry(region).or_insert(len); + let next = RegionIndex::new(free_regions.len()); + free_regions.entry(region).or_insert(next); } diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 553d5ad4a320a..f5731255449ec 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -13,7 +13,7 @@ use super::free_regions::FreeRegions; use rustc::infer::InferCtxt; use rustc::mir::{Location, Mir}; use rustc::ty; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; use std::collections::BTreeSet; use std::fmt; @@ -151,15 +151,13 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// 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 { - ref indices, - ref free_region_map, + let FreeRegions { + indices, + free_region_map, } = free_regions; // For each free region X: - for (free_region, index) in indices { - let variable = RegionIndex::new(*index); - + for (free_region, &variable) in indices { self.free_regions.push(variable); // Initialize the name and a few other details. @@ -184,7 +182,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // 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 = RegionIndex::new(indices[superregion]); + let superregion_index = indices[superregion]; self.definitions[superregion_index] .value .add_free_region(variable); diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index a3ff7a041ca07..c0d6e0cbe657b 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::ty::subst::{Kind, Substs}; use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable}; use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind}; @@ -17,6 +17,7 @@ use rustc::infer::{self as rustc_infer, InferCtxt}; use syntax_pos::DUMMY_SP; use std::collections::HashMap; +use super::RegionIndex; use super::free_regions::FreeRegions; /// Replaces all free regions appearing in the MIR with fresh @@ -51,7 +52,7 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { num_region_variables: usize, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, free_regions: &'a FreeRegions<'tcx>, - free_region_inference_vars: Vec>, + free_region_inference_vars: IndexVec>, arg_count: usize, } From b96e6eae09ea894917faf5258351d855b1e22c21 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:15:38 -0500 Subject: [PATCH 40/68] replace `RegionIndex` with `RegionVid` (which now impls Idx) --- src/librustc/ty/sty.rs | 2 +- src/librustc_mir/dataflow/impls/borrows.rs | 4 +-- .../transform/nll/constraint_generation.rs | 12 +++---- .../transform/nll/free_regions.rs | 10 +++--- src/librustc_mir/transform/nll/mod.rs | 25 ++++++--------- .../transform/nll/region_infer.rs | 31 +++++++++---------- src/librustc_mir/transform/nll/renumber.rs | 3 +- src/librustc_mir/transform/nll/subtype.rs | 13 ++++---- 8 files changed, 45 insertions(+), 55 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 307fd921218cd..c3b375bf7a8d3 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -894,7 +894,7 @@ pub struct FloatVid { pub index: u32, } -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash, Copy)] pub struct RegionVid { pub index: u32, } diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index e8bf543b70b6a..b8f49392c5771 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -22,7 +22,7 @@ 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::ToRegionIndex; +use transform::nll::ToRegionVid; use syntax_pos::Span; @@ -139,7 +139,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { location: Location) { if let Some(regioncx) = self.nonlexical_regioncx { for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { - let borrow_region = borrow_data.region.to_region_index(); + let borrow_region = borrow_data.region.to_region_vid(); if !regioncx.region_contains_point(borrow_region, location) { // The region checker really considers the borrow // to start at the point **after** the location of diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index a2f9bbb174eb7..8000d6d63c728 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -24,7 +24,7 @@ use syntax::codemap::DUMMY_SP; use super::subtype; use super::LivenessResults; -use super::ToRegionIndex; +use super::ToRegionVid; use super::region_infer::RegionInferenceContext; pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( @@ -103,7 +103,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { self.infcx .tcx .for_each_free_region(&live_ty, |live_region| { - let vid = live_region.to_region_index(); + let vid = live_region.to_region_vid(); self.regioncx.add_live_point(vid, location); }); } @@ -200,8 +200,8 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { }; self.regioncx.add_outlives(span, - borrow_region.to_region_index(), - destination_region.to_region_index(), + borrow_region.to_region_vid(), + destination_region.to_region_vid(), location.successor_within_block()); } @@ -230,8 +230,8 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { let span = self.mir.source_info(location).span; self.regioncx.add_outlives(span, - base_region.to_region_index(), - borrow_region.to_region_index(), + base_region.to_region_vid(), + borrow_region.to_region_vid(), location.successor_within_block()); } } diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs index f4b1024fa93f5..d7e497df58548 100644 --- a/src/librustc_mir/transform/nll/free_regions.rs +++ b/src/librustc_mir/transform/nll/free_regions.rs @@ -25,20 +25,18 @@ use rustc::infer::InferCtxt; use rustc::middle::free_region::FreeRegionMap; use rustc::mir::transform::MirSource; -use rustc::ty; +use rustc::ty::{self, RegionVid}; use rustc::ty::subst::Substs; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::Idx; -use super::RegionIndex; - #[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 indices: FxHashMap, RegionIndex>, + pub indices: FxHashMap, RegionVid>, /// The map from the typeck tables telling us how to relate free regions. pub free_region_map: &'tcx FreeRegionMap<'tcx>, @@ -83,9 +81,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>( } fn insert_free_region<'tcx>( - free_regions: &mut FxHashMap, RegionIndex>, + free_regions: &mut FxHashMap, RegionVid>, region: ty::Region<'tcx>, ) { - let next = RegionIndex::new(free_regions.len()); + let next = RegionVid::new(free_regions.len()); free_regions.entry(region).or_insert(next); } diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index e24def2292ec6..3288a05b31ecf 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::ty::{self, RegionKind}; +use rustc::ty::{self, RegionKind, RegionVid}; use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::infer::InferCtxt; use rustc::util::nodemap::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; use std::collections::BTreeSet; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; @@ -150,23 +149,19 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( }); } -newtype_index!(RegionIndex { - DEBUG_FORMAT = "'_#{}r", -}); - /// Right now, we piggy back on the `ReVar` to store our NLL inference -/// regions. These are indexed with `RegionIndex`. This method will -/// assert that the region is a `ReVar` and convert the internal index -/// into a `RegionIndex`. This is reasonable because in our MIR we -/// replace all free regions with inference variables. -pub trait ToRegionIndex { - fn to_region_index(&self) -> RegionIndex; +/// regions. These are indexed with `RegionVid`. This method will +/// assert that the region is a `ReVar` and extract its interal index. +/// This is reasonable because in our MIR we replace all free regions +/// with inference variables. +pub trait ToRegionVid { + fn to_region_vid(&self) -> RegionVid; } -impl ToRegionIndex for RegionKind { - fn to_region_index(&self) -> RegionIndex { +impl ToRegionVid for RegionKind { + fn to_region_vid(&self) -> RegionVid { if let &ty::ReVar(vid) = self { - RegionIndex::new(vid.index as usize) + vid } else { bug!("region is not an ReVar: {:?}", self) } diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index f5731255449ec..add48a9600a51 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::RegionIndex; use super::free_regions::FreeRegions; use rustc::infer::InferCtxt; use rustc::mir::{Location, Mir}; -use rustc::ty; +use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; use std::collections::BTreeSet; @@ -21,10 +20,10 @@ use syntax_pos::Span; pub struct RegionInferenceContext<'tcx> { /// Contains the definition for every region variable. Region - /// variables are identified by their index (`RegionIndex`). The + /// 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>, + definitions: IndexVec>, /// The indices of all "free regions" in scope. These are the /// lifetime parameters (anonymous and named) declared in the @@ -35,7 +34,7 @@ pub struct RegionInferenceContext<'tcx> { /// /// These indices will be from 0..N, as it happens, but we collect /// them into a vector for convenience. - free_regions: Vec, + free_regions: Vec, /// The constraints we have accumulated and used during solving. constraints: Vec, @@ -66,7 +65,7 @@ struct RegionDefinition<'tcx> { #[derive(Clone, Default, PartialEq, Eq)] struct Region { points: BTreeSet, - free_regions: BTreeSet, + free_regions: BTreeSet, } impl fmt::Debug for Region { @@ -84,7 +83,7 @@ impl Region { self.points.insert(point) } - fn add_free_region(&mut self, region: RegionIndex) -> bool { + fn add_free_region(&mut self, region: RegionVid) -> bool { self.free_regions.insert(region) } @@ -99,10 +98,10 @@ pub struct Constraint { span: Span, /// The region SUP must outlive SUB... - sup: RegionIndex, + sup: RegionVid, /// Region that must be outlived. - sub: RegionIndex, + sub: RegionVid, /// At this location. point: Location, @@ -198,24 +197,24 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { } /// Returns an iterator over all the region indices. - pub fn regions(&self) -> impl Iterator { + 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: RegionIndex, p: Location) -> bool { + 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: RegionIndex) -> &fmt::Debug { + 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: RegionIndex, point: Location) { + 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 { @@ -231,8 +230,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { pub(super) fn add_outlives( &mut self, span: Span, - sup: RegionIndex, - sub: RegionIndex, + sup: RegionVid, + sub: RegionVid, point: Location, ) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); @@ -268,7 +267,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { fn propagate_constraints( &mut self, mir: &Mir<'tcx>, - ) -> Vec<(RegionIndex, Span, RegionIndex)> { + ) -> Vec<(RegionVid, Span, RegionVid)> { let mut changed = true; let mut dfs = Dfs::new(mir); let mut error_regions = FxHashSet(); diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index c0d6e0cbe657b..7cdcb106c8c24 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -17,7 +17,6 @@ use rustc::infer::{self as rustc_infer, InferCtxt}; use syntax_pos::DUMMY_SP; use std::collections::HashMap; -use super::RegionIndex; use super::free_regions::FreeRegions; /// Replaces all free regions appearing in the MIR with fresh @@ -52,7 +51,7 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { num_region_variables: usize, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, free_regions: &'a FreeRegions<'tcx>, - free_region_inference_vars: IndexVec>, + free_region_inference_vars: IndexVec>, arg_count: usize, } diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs index 953fc0eb733c2..bb41477dcbf9e 100644 --- a/src/librustc_mir/transform/nll/subtype.rs +++ b/src/librustc_mir/transform/nll/subtype.rs @@ -8,15 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::RegionIndex; -use transform::nll::ToRegionIndex; -use rustc::ty::{self, Ty, TyCtxt}; +use transform::nll::ToRegionVid; +use rustc::ty::{self, Ty, TyCtxt, RegionVid}; use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) - -> Vec<(RegionIndex, RegionIndex)> + -> Vec<(RegionVid, RegionVid)> { let mut subtype = Subtype::new(tcx); match subtype.relate(&a, &b) { @@ -28,7 +27,7 @@ pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, - outlives_pairs: Vec<(RegionIndex, RegionIndex)>, + outlives_pairs: Vec<(RegionVid, RegionVid)>, ambient_variance: ty::Variance, } @@ -67,8 +66,8 @@ impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> { fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>) -> RelateResult<'tcx, ty::Region<'tcx>> { - let a = r_a.to_region_index(); - let b = r_b.to_region_index(); + let a = r_a.to_region_vid(); + let b = r_b.to_region_vid(); match self.ambient_variance { ty::Covariant => { From 40b773e624535806a19dfb7bd0cdd88972fff72c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:29:23 -0500 Subject: [PATCH 41/68] IndexVec: add `'_` to make clear where borrowing is happening --- src/librustc_data_structures/indexed_vec.rs | 4 ++-- src/librustc_data_structures/lib.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 622e8c51bee9d..e2f50c8c8891b 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -416,7 +416,7 @@ impl IndexVec { } #[inline] - pub fn iter_enumerated(&self) -> Enumerated> + pub fn iter_enumerated(&self) -> Enumerated> { self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData }) } @@ -432,7 +432,7 @@ impl IndexVec { } #[inline] - pub fn iter_enumerated_mut(&mut self) -> Enumerated> + pub fn iter_enumerated_mut(&mut self) -> Enumerated> { self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData }) } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 3a20343033c23..8862ba3545eba 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -31,6 +31,7 @@ #![feature(i128)] #![feature(conservative_impl_trait)] #![feature(specialization)] +#![feature(underscore_lifetimes)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] From 3a4b321984939f3e240edb4cb4b55715c9cdc099 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:29:50 -0500 Subject: [PATCH 42/68] infer: give access to region variable origins --- src/librustc/infer/mod.rs | 17 ++++++++++++++++- src/librustc/infer/region_constraints/mod.rs | 6 +++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 21f427fa80c13..10734802a6d0e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -25,7 +25,7 @@ use middle::lang_items; use mir::tcx::LvalueTy; use ty::subst::{Kind, Subst, Substs}; use ty::{TyVid, IntVid, FloatVid}; -use ty::{self, Ty, TyCtxt}; +use ty::{self, RegionVid, Ty, TyCtxt}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::relate::RelateResult; @@ -1166,6 +1166,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.borrow_region_constraints().take_and_reset_data() } + /// Returns the number of region variables created thus far. + pub fn num_region_vars(&self) -> usize { + self.borrow_region_constraints().var_origins().len() + } + + /// Returns an iterator over all region variables created thus far. + pub fn all_region_vars(&self) -> impl Iterator { + self.borrow_region_constraints().var_origins().indices() + } + + /// Returns the origin of a given region variable. + pub fn region_var_origin(&self, var: RegionVid) -> RegionVariableOrigin { + self.borrow_region_constraints().var_origins()[var].clone() + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_type_vars_if_possible(&t).to_string() } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 918850d0952f4..02f2c27303f51 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -32,7 +32,7 @@ mod taint; pub struct RegionConstraintCollector<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. - pub var_origins: IndexVec, + var_origins: IndexVec, data: RegionConstraintData<'tcx>, @@ -283,6 +283,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } + pub fn var_origins(&self) -> &VarOrigins { + &self.var_origins + } + /// Once all the constraints have been gathered, extract out the final data. /// /// Not legal during a snapshot. From 7dcf87bf25704d63803e3b345051e251b4b0d1ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:33:15 -0500 Subject: [PATCH 43/68] infer: extract total number of region variables from infcx We are heading towards deeper integration with the region inference system in infcx; in particular, prior to the creation of the `RegionInferenceContext`, it will be the "owner" of the set of region variables. --- src/librustc_mir/transform/nll/mod.rs | 4 ++-- src/librustc_mir/transform/nll/region_infer.rs | 6 ++---- src/librustc_mir/transform/nll/renumber.rs | 9 ++------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 3288a05b31ecf..03e3b8c340215 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -40,7 +40,7 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( let free_regions = &free_regions::free_regions(infcx, source); // Replace all regions with fresh inference variables. - let num_region_variables = renumber::renumber_mir(infcx, free_regions, mir); + renumber::renumber_mir(infcx, free_regions, mir); // Compute what is live where. let liveness = &LivenessResults { @@ -63,7 +63,7 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( // Create the region inference context, generate the constraints, // and then solve them. - let mut regioncx = RegionInferenceContext::new(free_regions, num_region_variables, mir); + let mut regioncx = RegionInferenceContext::new(infcx, free_regions, mir); constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); regioncx.solve(infcx, &mir); diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index add48a9600a51..f1160d42155ab 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -113,14 +113,12 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// of those will be constant regions representing the free /// regions defined in `free_regions`. pub fn new( + infcx: &InferCtxt<'_, '_, 'tcx>, free_regions: &FreeRegions<'tcx>, - num_region_variables: usize, mir: &Mir<'tcx>, ) -> Self { let mut result = Self { - definitions: (0..num_region_variables) - .map(|_| RegionDefinition::default()) - .collect(), + definitions: infcx.all_region_vars().map(|_| RegionDefinition::default()).collect(), constraints: Vec::new(), free_regions: Vec::new(), }; diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index 7cdcb106c8c24..c053dab123d7f 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -25,7 +25,7 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, free_regions: &FreeRegions<'tcx>, mir: &mut Mir<'tcx>, -) -> usize { +) { // Create inference variables for each of the free regions // declared on the function signature. let free_region_inference_vars = (0..free_regions.indices.len()) @@ -37,18 +37,15 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( let mut visitor = NLLVisitor { infcx, lookup_map: HashMap::new(), - num_region_variables: free_regions.indices.len(), free_regions, free_region_inference_vars, arg_count: mir.arg_count, }; visitor.visit_mir(mir); - visitor.num_region_variables } struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { lookup_map: HashMap, - num_region_variables: usize, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, free_regions: &'a FreeRegions<'tcx>, free_region_inference_vars: IndexVec>, @@ -66,9 +63,7 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { self.infcx .tcx .fold_regions(value, &mut false, |_region, _depth| { - self.num_region_variables += 1; - self.infcx - .next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) + self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) }) } From 7947173ecbeb3db0c632f91cb25923aabf40af78 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 05:21:48 -0500 Subject: [PATCH 44/68] formalize giving ownership of region vars to region inf. context --- src/librustc/infer/error_reporting/mod.rs | 1 + src/librustc/infer/mod.rs | 60 ++++++++++++------ src/librustc/mir/visit.rs | 2 +- src/librustc_mir/transform/nll/mod.rs | 3 +- .../transform/nll/region_infer.rs | 61 +++++++++++-------- src/librustc_mir/transform/nll/renumber.rs | 59 +++++------------- src/librustc_mir/transform/type_check.rs | 4 +- 7 files changed, 102 insertions(+), 88 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 67fdd68a826c2..84baece77fe59 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -1029,6 +1029,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let var_name = self.tcx.hir.name(var_node_id); format!(" for capture of `{}` by closure", var_name) } + infer::NLL(..) => bug!("NLL variable found in lexical phase"), }; struct_span_err!(self.tcx.sess, var_origin.span(), E0495, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 10734802a6d0e..2b080c54da1f3 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -16,7 +16,6 @@ pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; pub use ty::IntVarValue; pub use self::freshen::TypeFreshener; -pub use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData}; use hir::def_id::DefId; use middle::free_region::{FreeRegionMap, RegionRelations}; @@ -25,7 +24,7 @@ use middle::lang_items; use mir::tcx::LvalueTy; use ty::subst::{Kind, Subst, Substs}; use ty::{TyVid, IntVid, FloatVid}; -use ty::{self, RegionVid, Ty, TyCtxt}; +use ty::{self, Ty, TyCtxt}; use ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use ty::relate::RelateResult; @@ -42,6 +41,7 @@ use arena::DroplessArena; use self::combine::CombineFields; use self::higher_ranked::HrMatchResult; use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; +use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins}; use self::lexical_region_resolve::LexicalRegionResolutions; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; @@ -321,7 +321,7 @@ pub enum LateBoundRegionConversionTime { /// Reasons to create a region inference variable /// /// See `error_reporting` module for more details -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub enum RegionVariableOrigin { // Region variables created for ill-categorized reasons, // mostly indicates places in need of refactoring @@ -349,6 +349,20 @@ pub enum RegionVariableOrigin { UpvarRegion(ty::UpvarId, Span), BoundRegionInCoherence(ast::Name), + + // This origin is used for the inference variables that we create + // during NLL region processing. + NLL(NLLRegionVariableOrigin), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum NLLRegionVariableOrigin { + // During NLL region processing, we create variables for free + // regions that we encounter in the function signature and + // elsewhere. This origin indices we've got one of those. + FreeRegion, + + Inferred(::mir::visit::TyContext), } #[derive(Copy, Clone, Debug)] @@ -1030,11 +1044,23 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { .new_key(None) } + /// Create a fresh region variable with the next available index. + /// + /// # Parameters + /// + /// - `origin`: information about why we created this variable, for use + /// during diagnostics / error-reporting. pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin))) } + /// Just a convenient wrapper of `next_region_var` for using during NLL. + pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin) + -> ty::Region<'tcx> { + self.next_region_var(RegionVariableOrigin::NLL(origin)) + } + /// Create a region inference variable for the given /// region parameter definition. pub fn region_var_for_def(&self, @@ -1166,19 +1192,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.borrow_region_constraints().take_and_reset_data() } - /// Returns the number of region variables created thus far. - pub fn num_region_vars(&self) -> usize { - self.borrow_region_constraints().var_origins().len() - } - - /// Returns an iterator over all region variables created thus far. - pub fn all_region_vars(&self) -> impl Iterator { - self.borrow_region_constraints().var_origins().indices() - } - - /// Returns the origin of a given region variable. - pub fn region_var_origin(&self, var: RegionVid) -> RegionVariableOrigin { - self.borrow_region_constraints().var_origins()[var].clone() + /// Takes ownership of the list of variable regions. This implies + /// that all the region constriants have already been taken, and + /// hence that `resolve_regions_and_report_errors` can never be + /// called. This is used only during NLL processing to "hand off" ownership + /// of the set of region vairables into the NLL region context. + pub fn take_region_var_origins(&self) -> VarOrigins { + let (var_origins, data) = self.region_constraints.borrow_mut() + .take() + .expect("regions already resolved") + .into_origins_and_data(); + assert!(data.is_empty()); + var_origins } pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { @@ -1609,7 +1634,8 @@ impl RegionVariableOrigin { EarlyBoundRegion(a, ..) => a, LateBoundRegion(a, ..) => a, BoundRegionInCoherence(_) => syntax_pos::DUMMY_SP, - UpvarRegion(_, a) => a + UpvarRegion(_, a) => a, + NLL(..) => bug!("NLL variable used with `span`"), } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 00863abc84dee..b75163dbaa60f 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -811,7 +811,7 @@ make_mir_visitor!(MutVisitor,mut); /// Extra information passed to `visit_ty` and friends to give context /// about where the type etc appears. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum TyContext { LocalDecl { /// The index of the local variable we are visiting. diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 03e3b8c340215..4900cc6509427 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -63,7 +63,8 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( // Create the region inference context, generate the constraints, // and then solve them. - let mut regioncx = RegionInferenceContext::new(infcx, free_regions, mir); + let var_origins = infcx.take_region_var_origins(); + let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir); constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); regioncx.solve(infcx, &mir); diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index f1160d42155ab..24821529bade0 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -10,6 +10,9 @@ use super::free_regions::FreeRegions; use rustc::infer::InferCtxt; +use rustc::infer::RegionVariableOrigin; +use rustc::infer::NLLRegionVariableOrigin; +use rustc::infer::region_constraints::VarOrigins; use rustc::mir::{Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; @@ -25,23 +28,17 @@ pub struct RegionInferenceContext<'tcx> { /// from as well as its final inferred value. definitions: IndexVec>, - /// The indices of all "free regions" in scope. These are the - /// lifetime parameters (anonymous and named) declared in the - /// function signature: - /// - /// fn foo<'a, 'b>(x: &Foo<'a, 'b>) - /// ^^ ^^ ^ - /// - /// These indices will be from 0..N, as it happens, but we collect - /// them into a vector for convenience. - free_regions: Vec, - /// The constraints we have accumulated and used during solving. constraints: Vec, } -#[derive(Default)] struct RegionDefinition<'tcx> { + /// Why we created this variable. Mostly these will be + /// `RegionVariableOrigin::NLL`, but some variables get created + /// elsewhere in the code with other causes (e.g., instantiation + /// late-bound-regions). + origin: RegionVariableOrigin, + /// If this is a free-region, then this is `Some(X)` where `X` is /// the name of the region. name: Option>, @@ -112,15 +109,16 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free /// regions defined in `free_regions`. - pub fn new( - infcx: &InferCtxt<'_, '_, 'tcx>, - free_regions: &FreeRegions<'tcx>, - mir: &Mir<'tcx>, - ) -> Self { + pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self { + // Create a RegionDefinition for each inference variable. + let definitions = var_origins + .into_iter() + .map(|origin| RegionDefinition::new(origin)) + .collect(); + let mut result = Self { - definitions: infcx.all_region_vars().map(|_| RegionDefinition::default()).collect(), + definitions: definitions, constraints: Vec::new(), - free_regions: Vec::new(), }; result.init_free_regions(free_regions, mir); @@ -155,7 +153,11 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // For each free region X: for (free_region, &variable) in indices { - self.free_regions.push(variable); + // These should be free-region variables. + assert!(match self.definitions[variable].origin { + RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true, + _ => false, + }); // Initialize the name and a few other details. self.definitions[variable].name = Some(free_region); @@ -262,10 +264,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { /// for each region variable until all the constraints are /// satisfied. Note that some values may grow **too** large to be /// feasible, but we check this later. - fn propagate_constraints( - &mut self, - mir: &Mir<'tcx>, - ) -> Vec<(RegionVid, Span, RegionVid)> { + fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, RegionVid)> { let mut changed = true; let mut dfs = Dfs::new(mir); let mut error_regions = FxHashSet(); @@ -393,3 +392,17 @@ impl<'a, 'tcx> Dfs<'a, 'tcx> { changed } } + +impl<'tcx> RegionDefinition<'tcx> { + fn new(origin: RegionVariableOrigin) -> Self { + // Create a new region definition. Note that, for free + // regions, these fields get updated later in + // `init_free_regions`. + Self { + origin, + name: None, + constant: false, + value: Region::default(), + } + } +} diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index c053dab123d7f..a7c8deb18544a 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -9,14 +9,13 @@ // except according to those terms. use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc::ty::subst::{Kind, Substs}; -use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable}; +use rustc::ty::subst::Substs; +use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable}; use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; -use rustc::infer::{self as rustc_infer, InferCtxt}; -use syntax_pos::DUMMY_SP; -use std::collections::HashMap; +use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; +use super::ToRegionVid; use super::free_regions::FreeRegions; /// Replaces all free regions appearing in the MIR with fresh @@ -29,14 +28,16 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( // Create inference variables for each of the free regions // declared on the function signature. let free_region_inference_vars = (0..free_regions.indices.len()) - .map(|_| { - infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) + .map(RegionVid::new) + .map(|vid_expected| { + let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion); + assert_eq!(vid_expected, r.to_region_vid()); + r }) .collect(); let mut visitor = NLLVisitor { infcx, - lookup_map: HashMap::new(), free_regions, free_region_inference_vars, arg_count: mir.arg_count, @@ -45,7 +46,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( } struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - lookup_map: HashMap, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, free_regions: &'a FreeRegions<'tcx>, free_region_inference_vars: IndexVec>, @@ -56,14 +56,15 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { /// Replaces all regions appearing in `value` with fresh inference /// variables. This is what we do for almost the entire MIR, with /// the exception of the declared types of our arguments. - fn renumber_regions(&mut self, value: &T) -> T + fn renumber_regions(&mut self, ty_context: TyContext, value: &T) -> T where T: TypeFoldable<'tcx>, { self.infcx .tcx .fold_regions(value, &mut false, |_region, _depth| { - self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) + let origin = NLLRegionVariableOrigin::Inferred(ty_context); + self.infcx.next_nll_region_var(origin) }) } @@ -81,26 +82,6 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { }) } - fn store_region(&mut self, region: &RegionKind, lookup: TyContext) { - if let RegionKind::ReVar(rid) = *region { - self.lookup_map.entry(rid).or_insert(lookup); - } - } - - fn store_ty_regions(&mut self, ty: &Ty<'tcx>, ty_context: TyContext) { - for region in ty.regions() { - self.store_region(region, ty_context); - } - } - - fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) { - if let Some(ty) = kind.as_type() { - self.store_ty_regions(&ty, ty_context); - } else if let Some(region) = kind.as_region() { - self.store_region(region, ty_context); - } - } - fn is_argument_or_return_slot(&self, local: Local) -> bool { // The first argument is return slot, next N are arguments. local.index() <= self.arg_count @@ -118,26 +99,21 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { *ty = if is_arg { self.renumber_free_regions(&old_ty) } else { - self.renumber_regions(&old_ty) + self.renumber_regions(ty_context, &old_ty) }; - self.store_ty_regions(ty, ty_context); } fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { - *substs = self.renumber_regions(&{ *substs }); let ty_context = TyContext::Location(location); - for kind in *substs { - self.store_kind_regions(kind, ty_context); - } + *substs = self.renumber_regions(ty_context, &{ *substs }); } fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { match *rvalue { Rvalue::Ref(ref mut r, _, _) => { let old_r = *r; - *r = self.renumber_regions(&old_r); let ty_context = TyContext::Location(location); - self.store_region(r, ty_context); + *r = self.renumber_regions(ty_context, &old_r); } Rvalue::Use(..) | Rvalue::Repeat(..) | @@ -156,11 +132,8 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { } fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) { - *substs = self.renumber_regions(substs); let ty_context = TyContext::Location(location); - for kind in substs.substs { - self.store_kind_regions(kind, ty_context); - } + *substs = self.renumber_regions(ty_context, substs); } fn visit_statement( diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 9f22a6160b179..71af02a6f0183 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,8 +11,8 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, - RegionConstraintData, UnitResult}; +use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; +use rustc::infer::region_constraints::RegionConstraintData; use rustc::traits::{self, FulfillmentContext}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; From 2cd18e3b9830619b6495a9059680ca801ee114c9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:44:41 -0500 Subject: [PATCH 45/68] simplify lifetime annotations for `MirBorrowckCtxt` --- src/librustc_mir/borrow_check.rs | 72 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 2cb1a23ef5a15..010a301a7f04b 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -147,13 +147,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } #[allow(dead_code)] -pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - mir: &'b Mir<'tcx>, +pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + mir: &'cx Mir<'tcx>, node_id: ast::NodeId, - move_data: &'b MoveData<'tcx>, - param_env: ParamEnv<'tcx>, - fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>, + move_data: &'cx MoveData<'tcx>, + param_env: ParamEnv<'gcx>, + fake_infer_ctxt: &'cx InferCtxt<'cx, 'gcx, 'tcx>, } // (forced to be `pub` due to its use as an associated type below.) @@ -175,12 +175,10 @@ struct FlowInProgress where BD: BitDenotation { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'tcx> - for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> -{ - type FlowState = InProgress<'b, 'gcx, 'tcx>; +impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> { + type FlowState = InProgress<'cx, 'gcx, 'tcx>; - fn mir(&self) -> &'b 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), @@ -431,12 +429,12 @@ enum WriteKind { Move, } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn access_lvalue(&mut self, context: Context, lvalue_span: (&Lvalue<'tcx>, Span), kind: (ShallowOrDeep, ReadOrWrite), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { // FIXME: also need to check permissions (e.g. reject mut // borrow of immutable ref, moves through non-`Box`-ref) let (sd, rw) = kind; @@ -492,7 +490,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> lvalue_span: (&Lvalue<'tcx>, Span), kind: ShallowOrDeep, mode: MutateMode, - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + 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 => { @@ -513,7 +511,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, (rvalue, span): (&Rvalue<'tcx>, Span), _location: Location, - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { match *rvalue { Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => { let access_kind = match bk { @@ -570,7 +568,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, consume_via_drop: ConsumeKind, (operand, span): (&Operand<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { match *operand { Operand::Consume(ref lvalue) => { self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state) @@ -583,7 +581,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, consume_via_drop: ConsumeKind, lvalue_span: (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { let lvalue = lvalue_span.0; let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); let moves_by_default = @@ -610,11 +608,11 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn check_if_reassignment_to_immutable_state(&mut self, context: Context, (lvalue, span): (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + 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). @@ -665,7 +663,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> context: Context, desired_action: &str, lvalue_span: (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { // FIXME: analogous code in check_loans first maps `lvalue` to // its base_path ... but is that what we want here? let lvalue = self.base_path(lvalue_span.0); @@ -788,7 +786,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> fn check_if_assigned_path_is_moved(&mut self, context: Context, (lvalue, span): (&Lvalue<'tcx>, Span), - flow_state: &InProgress<'b, 'gcx, 'tcx>) { + flow_state: &InProgress<'cx, 'gcx, 'tcx>) { // recur down lvalue; dispatch to check_if_path_is_moved when necessary let mut lvalue = lvalue; loop { @@ -853,11 +851,11 @@ enum NoMovePathFound { ReachedStatic, } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn each_borrow_involving_path(&mut self, _context: Context, access_lvalue: (ShallowOrDeep, &Lvalue<'tcx>), - flow_state: &InProgress<'b, 'gcx, 'tcx>, + flow_state: &InProgress<'cx, 'gcx, 'tcx>, mut op: F) where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue) -> Control { @@ -957,11 +955,11 @@ mod prefixes { } - pub(super) struct Prefixes<'c, 'gcx: 'tcx, 'tcx: 'c> { - mir: &'c Mir<'tcx>, - tcx: TyCtxt<'c, 'gcx, 'tcx>, + pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + mir: &'cx Mir<'tcx>, + tcx: TyCtxt<'cx, 'gcx, 'tcx>, kind: PrefixSet, - next: Option<&'c Lvalue<'tcx>>, + next: Option<&'cx Lvalue<'tcx>>, } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -975,21 +973,21 @@ mod prefixes { Supporting, } - impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { + impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { /// Returns an iterator over the prefixes of `lvalue` /// (inclusive) from longest to smallest, potentially /// terminating the iteration early based on `kind`. - pub(super) fn prefixes<'d>(&self, - lvalue: &'d Lvalue<'tcx>, - kind: PrefixSet) - -> Prefixes<'d, 'gcx, 'tcx> where 'b: 'd + pub(super) fn prefixes(&self, + lvalue: &'cx Lvalue<'tcx>, + kind: PrefixSet) + -> Prefixes<'cx, 'gcx, 'tcx> { Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx } } } - impl<'c, 'gcx, 'tcx> Iterator for Prefixes<'c, 'gcx, 'tcx> { - type Item = &'c Lvalue<'tcx>; + impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> { + type Item = &'cx Lvalue<'tcx>; fn next(&mut self) -> Option { let mut cursor = match self.next { None => return None, @@ -1082,7 +1080,7 @@ mod prefixes { } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { fn report_use_of_moved(&mut self, _context: Context, desired_action: &str, @@ -1203,7 +1201,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // End-user visible description of `lvalue` fn describe_lvalue(&self, lvalue: &Lvalue) -> String { let mut buf = String::new(); @@ -1338,7 +1336,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> } } -impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> { +impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { // FIXME (#16118): function intended to allow the borrow checker // to be less precise in its handling of Box while still allowing // moves out of a Box. They should be removed when/if we stop From 2f70705699a99c84284d653b7d6a6a4256991cb5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 11:52:39 -0500 Subject: [PATCH 46/68] erase regions in MIR borrowck when checking if type moves by default --- src/librustc_mir/borrow_check.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 010a301a7f04b..87299fa00e826 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -136,7 +136,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, node_id: id, move_data: &mdpe.move_data, param_env: param_env, - fake_infer_ctxt: &infcx, }; let mut state = InProgress::new(flow_borrows, @@ -153,7 +152,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { node_id: ast::NodeId, move_data: &'cx MoveData<'tcx>, param_env: ParamEnv<'gcx>, - fake_infer_ctxt: &'cx InferCtxt<'cx, 'gcx, 'tcx>, } // (forced to be `pub` due to its use as an associated type below.) @@ -583,9 +581,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { lvalue_span: (&Lvalue<'tcx>, Span), flow_state: &InProgress<'cx, 'gcx, 'tcx>) { let lvalue = lvalue_span.0; + let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); - let moves_by_default = - self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP); + + // Erase the regions in type before checking whether it moves by + // default. There are a few reasons to do this: + // + // - They should not affect the result. + // - It avoids adding new region constraints into the surrounding context, + // which would trigger an ICE, since the infcx will have been "frozen" by + // the NLL region context. + let gcx = self.tcx.global_tcx(); + let erased_ty = gcx.lift(&self.tcx.erase_regions(&ty)).unwrap(); + let moves_by_default = erased_ty.moves_by_default(gcx, self.param_env, DUMMY_SP); + if moves_by_default { // move of lvalue: check if this is move of already borrowed path self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), flow_state); From 48d3d7eb8f9c85b837a98e9312122ad2e833e02a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:29:10 -0500 Subject: [PATCH 47/68] MIR-dump: print return type from local_decls for `_0` We've kind of got the same information twice in the MIR, between the return-type field and the local-decls. Seems un-great. --- src/librustc_mir/util/pretty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 0f07f1fe550cc..175f510b9f756 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -350,7 +350,7 @@ pub fn write_mir_intro<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, let indented_retptr = format!("{}let mut {:?}: {};", INDENT, RETURN_POINTER, - mir.return_ty); + mir.local_decls[RETURN_POINTER].ty); writeln!(w, "{0:1$} // return pointer", indented_retptr, ALIGN)?; From 03e76ccb797b30036f1fcc2f73e117e260c10be0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:30:06 -0500 Subject: [PATCH 48/68] renumber: handle ReturnTy better --- src/librustc/mir/visit.rs | 11 ++++++----- src/librustc_mir/transform/nll/renumber.rs | 10 +++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index b75163dbaa60f..b09ab8da7c12d 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -292,11 +292,10 @@ macro_rules! make_mir_visitor { self.visit_visibility_scope_data(scope); } - let lookup = TyContext::SourceInfo(SourceInfo { + self.visit_ty(&$($mutability)* mir.return_ty, TyContext::ReturnTy(SourceInfo { span: mir.span, scope: ARGUMENT_VISIBILITY_SCOPE, - }); - self.visit_ty(&$($mutability)* mir.return_ty, lookup); + })); for local in mir.local_decls.indices() { self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]); @@ -821,9 +820,11 @@ pub enum TyContext { source_info: SourceInfo, }, - Location(Location), + /// The return type of the function. + ReturnTy(SourceInfo), - SourceInfo(SourceInfo), + /// A type found at some location. + Location(Location), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index a7c8deb18544a..139679258441e 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -92,8 +92,15 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { let is_arg = match ty_context { TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local), - _ => false, + TyContext::ReturnTy(..) => true, + TyContext::Location(..) => false, }; + debug!( + "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})", + ty, + is_arg, + ty_context + ); let old_ty = *ty; *ty = if is_arg { @@ -101,6 +108,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { } else { self.renumber_regions(ty_context, &old_ty) }; + debug!("visit_ty: ty={:?}", ty); } fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { From 410c433f9c13003e3847b9f90eb5089ed201c77b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:30:43 -0500 Subject: [PATCH 49/68] renumber: debug logs, use `visit_region` rather than `visit_rvalue` --- src/librustc_mir/transform/nll/renumber.rs | 51 ++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index 139679258441e..1076b774de657 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -11,7 +11,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::ty::subst::Substs; use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable}; -use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind}; +use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; @@ -36,6 +36,10 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>( }) .collect(); + debug!("renumber_mir()"); + debug!("renumber_mir: free_regions={:#?}", free_regions); + debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count); + let mut visitor = NLLVisitor { infcx, free_regions, @@ -60,6 +64,8 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + debug!("renumber_regions(value={:?})", value); + self.infcx .tcx .fold_regions(value, &mut false, |_region, _depth| { @@ -74,6 +80,8 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { where T: TypeFoldable<'tcx>, { + debug!("renumber_free_regions(value={:?})", value); + self.infcx .tcx .fold_regions(value, &mut false, |region, _depth| { @@ -112,36 +120,35 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { } fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { + debug!("visit_substs(substs={:?}, location={:?})", substs, location); + let ty_context = TyContext::Location(location); *substs = self.renumber_regions(ty_context, &{ *substs }); + + debug!("visit_substs: substs={:?}", substs); } - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - match *rvalue { - Rvalue::Ref(ref mut r, _, _) => { - let old_r = *r; - let ty_context = TyContext::Location(location); - *r = self.renumber_regions(ty_context, &old_r); - } - Rvalue::Use(..) | - Rvalue::Repeat(..) | - Rvalue::Len(..) | - Rvalue::Cast(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) | - Rvalue::NullaryOp(..) | - Rvalue::Aggregate(..) => { - // These variants don't contain regions. - } - } - self.super_rvalue(rvalue, location); + fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) { + debug!("visit_region(region={:?}, location={:?})", region, location); + + let old_region = *region; + let ty_context = TyContext::Location(location); + *region = self.renumber_regions(ty_context, &old_region); + + debug!("visit_region: region={:?}", region); } fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) { + debug!( + "visit_closure_substs(substs={:?}, location={:?})", + substs, + location + ); + let ty_context = TyContext::Location(location); *substs = self.renumber_regions(ty_context, substs); + + debug!("visit_closure_substs: substs={:?}", substs); } fn visit_statement( From ffd17eab0bbcb407447407b03627b877b38c2144 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:31:21 -0500 Subject: [PATCH 50/68] region_infer: improved debug logging --- .../transform/nll/region_infer.rs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 24821529bade0..566a3b23ff138 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -89,10 +89,12 @@ impl Region { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Constraint { - /// Where did this constraint arise? - span: Span, + // NB. The ordering here is not significant for correctness, but + // it is for conenience. 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, @@ -102,6 +104,9 @@ pub struct Constraint { /// At this location. point: Location, + + /// Where did this constraint arise? + span: Span, } impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { @@ -269,15 +274,23 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { 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!("constraint: {:?}", constraint); + debug!("propagate_constraints: constraint={:?}", constraint); let sub = &self.definitions[constraint.sub].value.clone(); let sup_def = &mut self.definitions[constraint.sup]; - debug!(" sub (before): {:?}", sub); - debug!(" sup (before): {:?}", sup_def.value); + 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 @@ -287,8 +300,8 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { changed = true; } - debug!(" sup (after) : {:?}", sup_def.value); - debug!(" changed : {:?}", changed); + 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 @@ -304,7 +317,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { .difference(&sup_def.value.free_regions) .next() .unwrap(); - debug!(" new_region : {:?}", new_region); + debug!("propagate_constraints: new_region : {:?}", new_region); if error_regions.insert(constraint.sup) { errors.push((constraint.sup, constraint.span, new_region)); } @@ -406,3 +419,16 @@ impl<'tcx> RegionDefinition<'tcx> { } } } + +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 + ) + } +} From 00205eab3d88e637bb762470f7b4d1c4e3557843 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 07:26:34 -0500 Subject: [PATCH 51/68] integrate NLL with MIR type-checker --- src/librustc/middle/free_region.rs | 2 +- src/librustc_mir/borrow_check.rs | 2 +- .../transform/nll/constraint_generation.rs | 63 ++-------- .../transform/nll/free_regions.rs | 3 + src/librustc_mir/transform/nll/mod.rs | 21 +++- .../transform/nll/region_infer.rs | 9 ++ src/librustc_mir/transform/nll/subtype.rs | 98 --------------- .../nll/subtype_constraint_generation.rs | 112 ++++++++++++++++++ src/test/ui/nll/get_default.rs | 53 +++++++++ src/test/ui/nll/get_default.stderr | 47 ++++++++ 10 files changed, 255 insertions(+), 155 deletions(-) delete mode 100644 src/librustc_mir/transform/nll/subtype.rs create mode 100644 src/librustc_mir/transform/nll/subtype_constraint_generation.rs create mode 100644 src/test/ui/nll/get_default.rs create mode 100644 src/test/ui/nll/get_default.stderr diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 3bcdc4f7e2c63..da505f167241b 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -192,7 +192,7 @@ impl<'tcx> FreeRegionMap<'tcx> { /// /// 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)); + assert!(is_free(r_a) || *r_a == ty::ReStatic); self.relation.greater_than(&r_a) } } diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 87299fa00e826..6abae2548d981 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -115,7 +115,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { None } else { - Some(nll::compute_regions(infcx, src, mir)) + Some(nll::compute_regions(infcx, src, param_env, mir)) }; let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 8000d6d63c728..cb3c4c54e97c6 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -9,7 +9,7 @@ // except according to those terms. use rustc::hir; -use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind}; +use rustc::mir::{Location, Lvalue, Mir, Rvalue}; use rustc::mir::transform::MirSource; use rustc::mir::visit::Visitor; use rustc::mir::Lvalue::Projection; @@ -22,7 +22,6 @@ use rustc::util::common::ErrorReported; use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; -use super::subtype; use super::LivenessResults; use super::ToRegionVid; use super::region_infer::RegionInferenceContext; @@ -182,29 +181,6 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { self.visit_mir(self.mir); } - fn add_borrow_constraint( - &mut self, - location: Location, - destination_lv: &Lvalue<'tcx>, - borrow_region: ty::Region<'tcx>, - _borrow_kind: BorrowKind, - _borrowed_lv: &Lvalue<'tcx>, - ) { - let tcx = self.infcx.tcx; - let span = self.mir.source_info(location).span; - let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); - - let destination_region = match destination_ty.sty { - ty::TyRef(r, _) => r, - _ => bug!() - }; - - self.regioncx.add_outlives(span, - borrow_region.to_region_vid(), - destination_region.to_region_vid(), - location.successor_within_block()); - } - fn add_reborrow_constraint( &mut self, location: Location, @@ -240,35 +216,22 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { } impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { - fn visit_statement(&mut self, - block: BasicBlock, - statement: &Statement<'tcx>, - location: Location) { + fn visit_rvalue(&mut self, + rvalue: &Rvalue<'tcx>, + location: Location) { + debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location); - debug!("visit_statement(statement={:?}, location={:?})", statement, location); - - // Look for a statement like: + // Look for an rvalue like: // - // D = & L + // & L // - // where D is the path to which we are assigning, and - // L is the path that is borrowed. - if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind { - if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv { - self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv); - self.add_reborrow_constraint(location, region, borrowed_lv); - } - - let tcx = self.infcx.tcx; - let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); - let rv_ty = rv.ty(self.mir, tcx); - - let span = self.mir.source_info(location).span; - for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) { - self.regioncx.add_outlives(span, a, b, location.successor_within_block()); - } + // 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_statement(block, statement, location); + self.super_rvalue(rvalue, location); } } diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs index d7e497df58548..9dcc17ede500c 100644 --- a/src/librustc_mir/transform/nll/free_regions.rs +++ b/src/librustc_mir/transform/nll/free_regions.rs @@ -53,6 +53,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>( let mut indices = FxHashMap(); + // `'static` is always free. + insert_free_region(&mut indices, infcx.tcx.types.re_static); + // Extract the early regions. let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id); for item_subst in item_substs { diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 4900cc6509427..18dbd824f185e 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -14,14 +14,15 @@ use rustc::mir::transform::MirSource; use rustc::infer::InferCtxt; use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; +use transform::type_check; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; use util as mir_util; use self::mir_util::PassWhere; mod constraint_generation; +mod subtype_constraint_generation; mod free_regions; -mod subtype; pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; @@ -34,6 +35,7 @@ mod renumber; pub fn compute_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, source: MirSource, + param_env: ty::ParamEnv<'gcx>, mir: &mut Mir<'tcx>, ) -> RegionInferenceContext<'tcx> { // Compute named region information. @@ -42,6 +44,16 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( // Replace all regions with fresh inference variables. renumber::renumber_mir(infcx, free_regions, mir); + // Run the MIR type-checker. + let body_id = source.item_id(); + let constraint_sets = &type_check::type_check(infcx, body_id, param_env, mir); + + // 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); + // Compute what is live where. let liveness = &LivenessResults { regular: liveness::liveness_of_locals( @@ -61,11 +73,10 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( ), }; - // Create the region inference context, generate the constraints, - // and then solve them. - let var_origins = infcx.take_region_var_origins(); - let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir); + // Generate non-subtyping constraints. constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); + + // Solve the region constraints. regioncx.solve(infcx, &mir); // Dump MIR results into a file, if that is enabled. This let us diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 566a3b23ff138..f218cc51d344b 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -183,6 +183,15 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { // 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) { diff --git a/src/librustc_mir/transform/nll/subtype.rs b/src/librustc_mir/transform/nll/subtype.rs deleted file mode 100644 index bb41477dcbf9e..0000000000000 --- a/src/librustc_mir/transform/nll/subtype.rs +++ /dev/null @@ -1,98 +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 transform::nll::ToRegionVid; -use rustc::ty::{self, Ty, TyCtxt, RegionVid}; -use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; - -pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> Vec<(RegionVid, RegionVid)> -{ - let mut subtype = Subtype::new(tcx); - match subtype.relate(&a, &b) { - Ok(_) => subtype.outlives_pairs, - - Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b) - } -} - -struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { - tcx: TyCtxt<'a, 'gcx, 'tcx>, - outlives_pairs: Vec<(RegionVid, RegionVid)>, - ambient_variance: ty::Variance, -} - -impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> { - Subtype { - tcx, - outlives_pairs: vec![], - ambient_variance: ty::Covariant, - } - } -} - -impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> { - fn tag(&self) -> &'static str { "Subtype" } - fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx } - fn a_is_expected(&self) -> bool { true } // irrelevant - - fn relate_with_variance>(&mut self, - variance: ty::Variance, - a: &T, - b: &T) - -> RelateResult<'tcx, T> - { - let old_ambient_variance = self.ambient_variance; - self.ambient_variance = self.ambient_variance.xform(variance); - - let result = self.relate(a, b); - self.ambient_variance = old_ambient_variance; - result - } - - fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - relate::super_relate_tys(self, t, t2) - } - - fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>) - -> RelateResult<'tcx, ty::Region<'tcx>> { - let a = r_a.to_region_vid(); - let b = r_b.to_region_vid(); - - match self.ambient_variance { - ty::Covariant => { - self.outlives_pairs.push((b, a)); - }, - - ty::Invariant => { - self.outlives_pairs.push((a, b)); - self.outlives_pairs.push((b, a)); - }, - - ty::Contravariant => { - self.outlives_pairs.push((a, b)); - }, - - ty::Bivariant => {}, - } - - Ok(r_a) - } - - fn binders(&mut self, _a: &ty::Binder, _b: &ty::Binder) - -> RelateResult<'tcx, ty::Binder> - where T: Relate<'tcx> - { - unimplemented!(); - } -} diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/transform/nll/subtype_constraint_generation.rs new file mode 100644 index 0000000000000..c1850c76541d7 --- /dev/null +++ b/src/librustc_mir/transform/nll/subtype_constraint_generation.rs @@ -0,0 +1,112 @@ +// 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::mir::Mir; +use rustc::infer::region_constraints::Constraint; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::ty; +use transform::type_check::MirTypeckRegionConstraints; +use transform::type_check::OutlivesSet; + +use super::free_regions::FreeRegions; +use super::region_infer::RegionInferenceContext; + +/// When the MIR type-checker executes, it validates all the types in +/// the MIR, and in the process generates a set of constraints that +/// must hold regarding the regions in the MIR, along with locations +/// *where* they must hold. This code takes those constriants and adds +/// them into the NLL `RegionInferenceContext`. +pub(super) fn generate<'tcx>( + regioncx: &mut RegionInferenceContext<'tcx>, + free_regions: &FreeRegions<'tcx>, + mir: &Mir<'tcx>, + constraints: &MirTypeckRegionConstraints<'tcx>, +) { + SubtypeConstraintGenerator { + regioncx, + free_regions, + mir, + }.generate(constraints); +} + +struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { + regioncx: &'cx mut RegionInferenceContext<'tcx>, + free_regions: &'cx FreeRegions<'tcx>, + mir: &'cx Mir<'tcx>, +} + +impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { + fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) { + let MirTypeckRegionConstraints { + liveness_set, + outlives_sets, + } = constraints; + + debug!( + "generate(liveness_set={} items, outlives_sets={} items)", + liveness_set.len(), + outlives_sets.len() + ); + + for (region, location) in liveness_set { + debug!("generate: {:#?} is live at {:#?}", region, location); + let region_vid = self.to_region_vid(region); + self.regioncx.add_live_point(region_vid, *location); + } + + for OutlivesSet { locations, data } in outlives_sets { + debug!("generate: constraints at: {:#?}", locations); + let RegionConstraintData { + constraints, + verifys, + givens, + } = data; + + for constraint in constraints.keys() { + debug!("generate: constraint: {:?}", constraint); + let (a_vid, b_vid) = match constraint { + Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid), + Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid), + Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)), + Constraint::RegSubReg(a_r, b_r) => { + (self.to_region_vid(a_r), self.to_region_vid(b_r)) + } + }; + + // We have the constraint that `a_vid <= b_vid`. Add + // `b_vid: a_vid` to our region checker. Note that we + // reverse direction, because `regioncx` talks about + // "outlives" (`>=`) whereas the region constraints + // talk about `<=`. + let span = self.mir.source_info(locations.from_location).span; + self.regioncx + .add_outlives(span, b_vid, a_vid, locations.at_location); + } + + assert!(verifys.is_empty(), "verifys not yet implemented"); + assert!( + givens.is_empty(), + "MIR type-checker does not use givens (thank goodness)" + ); + } + } + + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { + // 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*. + if let ty::ReVar(vid) = r { + *vid + } else { + self.free_regions.indices[&r] + } + } +} diff --git a/src/test/ui/nll/get_default.rs b/src/test/ui/nll/get_default.rs new file mode 100644 index 0000000000000..5605206221a11 --- /dev/null +++ b/src/test/ui/nll/get_default.rs @@ -0,0 +1,53 @@ +// 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. + +// Basic test for free regions in the NLL code. This test ought to +// report an error due to a reborrowing constraint. Right now, we get +// a variety of errors from the older, AST-based machinery (notably +// borrowck), and then we get the NLL error at the end. + +// compile-flags:-Znll -Zborrowck-mir + +struct Map { +} + +impl Map { + fn get(&self) -> Option<&String> { None } + fn set(&mut self, v: String) { } +} + +fn ok(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + return v; + } + None => { + map.set(String::new()); // Just AST errors here + } + } + } +} + +fn err(map: &mut Map) -> &String { + loop { + match map.get() { + Some(v) => { + map.set(String::new()); // Both AST and MIR error here + return v; + } + None => { + map.set(String::new()); // Just AST errors here + } + } + } +} + +fn main() { } diff --git a/src/test/ui/nll/get_default.stderr b/src/test/ui/nll/get_default.stderr new file mode 100644 index 0000000000000..9586f42672034 --- /dev/null +++ b/src/test/ui/nll/get_default.stderr @@ -0,0 +1,47 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:33:17 + | +28 | match map.get() { + | --- immutable borrow occurs here +... +33 | map.set(String::new()); // Just AST errors here + | ^^^ mutable borrow occurs here +... +37 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:43:17 + | +41 | match map.get() { + | --- immutable borrow occurs here +42 | Some(v) => { +43 | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here +... +51 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast) + --> $DIR/get_default.rs:47:17 + | +41 | match map.get() { + | --- immutable borrow occurs here +... +47 | map.set(String::new()); // Just AST errors here + | ^^^ mutable borrow occurs here +... +51 | } + | - immutable borrow ends here + +error[E0502]: cannot borrow `(*map)` as mutable because it is also borrowed as immutable (Mir) + --> $DIR/get_default.rs:43:17 + | +41 | match map.get() { + | --- immutable borrow occurs here +42 | Some(v) => { +43 | map.set(String::new()); // Both AST and MIR error here + | ^^^ mutable borrow occurs here + +error: aborting due to 4 previous errors + From c1d84ecb2cd04303fc07df5477b6bb97d3ce5576 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:41:57 -0500 Subject: [PATCH 52/68] update READMEs to describe the new situation The inference README, in particular, was VERY out of date! --- src/librustc/infer/README.md | 438 +++++++++--------- .../infer/lexical_region_resolve/README.md | 262 +++++++++++ .../infer/region_constraints/README.md | 265 +---------- 3 files changed, 482 insertions(+), 483 deletions(-) create mode 100644 src/librustc/infer/lexical_region_resolve/README.md diff --git a/src/librustc/infer/README.md b/src/librustc/infer/README.md index 6c1478531f147..e7daff3e2c371 100644 --- a/src/librustc/infer/README.md +++ b/src/librustc/infer/README.md @@ -1,239 +1,227 @@ # Type inference engine -This is loosely based on standard HM-type inference, but with an -extension to try and accommodate subtyping. There is nothing -principled about this extension; it's sound---I hope!---but it's a -heuristic, ultimately, and does not guarantee that it finds a valid -typing even if one exists (in fact, there are known scenarios where it -fails, some of which may eventually become problematic). - -## Key idea - -The main change is that each type variable T is associated with a -lower-bound L and an upper-bound U. L and U begin as bottom and top, -respectively, but gradually narrow in response to new constraints -being introduced. When a variable is finally resolved to a concrete -type, it can (theoretically) select any type that is a supertype of L -and a subtype of U. - -There are several critical invariants which we maintain: - -- the upper-bound of a variable only becomes lower and the lower-bound - only becomes higher over time; -- the lower-bound L is always a subtype of the upper bound U; -- the lower-bound L and upper-bound U never refer to other type variables, - but only to types (though those types may contain type variables). - -> An aside: if the terms upper- and lower-bound confuse you, think of -> "supertype" and "subtype". The upper-bound is a "supertype" -> (super=upper in Latin, or something like that anyway) and the lower-bound -> is a "subtype" (sub=lower in Latin). I find it helps to visualize -> a simple class hierarchy, like Java minus interfaces and -> primitive types. The class Object is at the root (top) and other -> types lie in between. The bottom type is then the Null type. -> So the tree looks like: -> -> ```text -> Object -> / \ -> String Other -> \ / -> (null) -> ``` -> -> So the upper bound type is the "supertype" and the lower bound is the -> "subtype" (also, super and sub mean upper and lower in Latin, or something -> like that anyway). - -## Satisfying constraints - -At a primitive level, there is only one form of constraint that the -inference understands: a subtype relation. So the outside world can -say "make type A a subtype of type B". If there are variables -involved, the inferencer will adjust their upper- and lower-bounds as -needed to ensure that this relation is satisfied. (We also allow "make -type A equal to type B", but this is translated into "A <: B" and "B -<: A") - -As stated above, we always maintain the invariant that type bounds -never refer to other variables. This keeps the inference relatively -simple, avoiding the scenario of having a kind of graph where we have -to pump constraints along and reach a fixed point, but it does impose -some heuristics in the case where the user is relating two type -variables A <: B. - -Combining two variables such that variable A will forever be a subtype -of variable B is the trickiest part of the algorithm because there is -often no right choice---that is, the right choice will depend on -future constraints which we do not yet know. The problem comes about -because both A and B have bounds that can be adjusted in the future. -Let's look at some of the cases that can come up. - -Imagine, to start, the best case, where both A and B have an upper and -lower bound (that is, the bounds are not top nor bot respectively). In -that case, if we're lucky, A.ub <: B.lb, and so we know that whatever -A and B should become, they will forever have the desired subtyping -relation. We can just leave things as they are. - -### Option 1: Unify - -However, suppose that A.ub is *not* a subtype of B.lb. In -that case, we must make a decision. One option is to unify A -and B so that they are one variable whose bounds are: - - UB = GLB(A.ub, B.ub) - LB = LUB(A.lb, B.lb) - -(Note that we will have to verify that LB <: UB; if it does not, the -types are not intersecting and there is an error) In that case, A <: B -holds trivially because A==B. However, we have now lost some -flexibility, because perhaps the user intended for A and B to end up -as different types and not the same type. - -Pictorially, what this does is to take two distinct variables with -(hopefully not completely) distinct type ranges and produce one with -the intersection. - -```text - B.ub B.ub - /\ / - A.ub / \ A.ub / - / \ / \ \ / - / X \ UB - / / \ \ / \ - / / / \ / / - \ \ / / \ / - \ X / LB - \ / \ / / \ - \ / \ / / \ - A.lb B.lb A.lb B.lb -``` +The type inference is based on standard HM-type inference, but +extended in various way to accommodate subtyping, region inference, +and higher-ranked types. + +## A note on terminology + +We use the notation `?T` to refer to inference variables, also called +existential variables. + +We use the term "region" and "lifetime" interchangeably. Both refer to +the `'a` in `&'a T`. + +The term "bound region" refers to regions bound in a function +signature, such as the `'a` in `for<'a> fn(&'a u32)`. A region is +"free" if it is not bound. +## Creating an inference context -### Option 2: Relate UB/LB - -Another option is to keep A and B as distinct variables but set their -bounds in such a way that, whatever happens, we know that A <: B will hold. -This can be achieved by ensuring that A.ub <: B.lb. In practice there -are two ways to do that, depicted pictorially here: - -```text - Before Option #1 Option #2 - - B.ub B.ub B.ub - /\ / \ / \ - A.ub / \ A.ub /(B')\ A.ub /(B')\ - / \ / \ \ / / \ / / - / X \ __UB____/ UB / - / / \ \ / | | / - / / / \ / | | / - \ \ / / /(A')| | / - \ X / / LB ______LB/ - \ / \ / / / \ / (A')/ \ - \ / \ / \ / \ \ / \ - A.lb B.lb A.lb B.lb A.lb B.lb +You create and "enter" an inference context by doing something like +the following: + +```rust +tcx.infer_ctxt().enter(|infcx| { + // use the inference context `infcx` in here +}) ``` -In these diagrams, UB and LB are defined as before. As you can see, -the new ranges `A'` and `B'` are quite different from the range that -would be produced by unifying the variables. +Each inference context creates a short-lived type arena to store the +fresh types and things that it will create, as described in +[the README in the ty module][ty-readme]. This arena is created by the `enter` +function and disposed after it returns. -### What we do now +[ty-readme]: src/librustc/ty/README.md -Our current technique is to *try* (transactionally) to relate the -existing bounds of A and B, if there are any (i.e., if `UB(A) != top -&& LB(B) != bot`). If that succeeds, we're done. If it fails, then -we merge A and B into same variable. +Within the closure, the infcx will have the type `InferCtxt<'cx, 'gcx, +'tcx>` for some fresh `'cx` and `'tcx` -- the latter corresponds to +the lifetime of this temporary arena, and the `'cx` is the lifetime of +the `InferCtxt` itself. (Again, see [that ty README][ty-readme] for +more details on this setup.) -This is not clearly the correct course. For example, if `UB(A) != -top` but `LB(B) == bot`, we could conceivably set `LB(B)` to `UB(A)` -and leave the variables unmerged. This is sometimes the better -course, it depends on the program. +The `tcx.infer_ctxt` method actually returns a build, which means +there are some kinds of configuration you can do before the `infcx` is +created. See `InferCtxtBuilder` for more information. -The main case which fails today that I would like to support is: +## Inference variables -```rust -fn foo(x: T, y: T) { ... } +The main purpose of the inference context is to house a bunch of +**inference variables** -- these represent types or regions whose precise +value is not yet known, but will be uncovered as we perform type-checking. + +If you're familiar with the basic ideas of unification from H-M type +systems, or logic languages like Prolog, this is the same concept. If +you're not, you might want to read a tutorial on how H-M type +inference works, or perhaps this blog post on +[unification in the Chalk project]. -fn bar() { - let x: @mut int = @mut 3; - let y: @int = @3; - foo(x, y); -} +[Unification in the Chalk project]: http://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/ + +All told, the inference context stores four kinds of inference variables as of this +writing: + +- Type variables, which come in three varieties: + - General type variables (the most common). These can be unified with any type. + - Integral type variables, which can only be unified with an integral type, and + arise from an integer literal expression like `22`. + - Float type variables, which can only be unified with a float type, and + arise from a float literal expression like `22.0`. +- Region variables, which represent lifetimes, and arise all over the dang place. + +All the type variables work in much the same way: you can create a new +type variable, and what you get is `Ty<'tcx>` representing an +unresolved type `?T`. Then later you can apply the various operations +that the inferencer supports, such as equality or subtyping, and it +will possibly **instantiate** (or **bind**) that `?T` to a specific +value as a result. + +The region variables work somewhat differently, and are described +below in a separate section. + +## Enforcing equality / subtyping + +The most basic operations you can perform in the type inferencer is +**equality**, which forces two types `T` and `U` to be the same. The +recommended way to add an equality constraint is using the `at` +method, roughly like so: + +``` +infcx.at(...).eq(t, u); ``` -In principle, the inferencer ought to find that the parameter `T` to -`foo(x, y)` is `@const int`. Today, however, it does not; this is -because the type variable `T` is merged with the type variable for -`X`, and thus inherits its UB/LB of `@mut int`. This leaves no -flexibility for `T` to later adjust to accommodate `@int`. - -Note: `@` and `@mut` are replaced with `Rc` and `Rc>` in current Rust. - -### What to do when not all bounds are present - -In the prior discussion we assumed that A.ub was not top and B.lb was -not bot. Unfortunately this is rarely the case. Often type variables -have "lopsided" bounds. For example, if a variable in the program has -been initialized but has not been used, then its corresponding type -variable will have a lower bound but no upper bound. When that -variable is then used, we would like to know its upper bound---but we -don't have one! In this case we'll do different things depending on -how the variable is being used. - -## Transactional support - -Whenever we adjust merge variables or adjust their bounds, we always -keep a record of the old value. This allows the changes to be undone. - -## Regions - -I've only talked about type variables here, but region variables -follow the same principle. They have upper- and lower-bounds. A -region A is a subregion of a region B if A being valid implies that B -is valid. This basically corresponds to the block nesting structure: -the regions for outer block scopes are superregions of those for inner -block scopes. - -## Integral and floating-point type variables - -There is a third variety of type variable that we use only for -inferring the types of unsuffixed integer literals. Integral type -variables differ from general-purpose type variables in that there's -no subtyping relationship among the various integral types, so instead -of associating each variable with an upper and lower bound, we just -use simple unification. Each integer variable is associated with at -most one integer type. Floating point types are handled similarly to -integral types. - -## GLB/LUB - -Computing the greatest-lower-bound and least-upper-bound of two -types/regions is generally straightforward except when type variables -are involved. In that case, we follow a similar "try to use the bounds -when possible but otherwise merge the variables" strategy. In other -words, `GLB(A, B)` where `A` and `B` are variables will often result -in `A` and `B` being merged and the result being `A`. - -## Type coercion - -We have a notion of assignability which differs somewhat from -subtyping; in particular it may cause region borrowing to occur. See -the big comment later in this file on Type Coercion for specifics. - -### In conclusion - -I showed you three ways to relate `A` and `B`. There are also more, -of course, though I'm not sure if there are any more sensible options. -The main point is that there are various options, each of which -produce a distinct range of types for `A` and `B`. Depending on what -the correct values for A and B are, one of these options will be the -right choice: but of course we don't know the right values for A and B -yet, that's what we're trying to find! In our code, we opt to unify -(Option #1). - -# Implementation details - -We make use of a trait-like implementation strategy to consolidate -duplicated code between subtypes, GLB, and LUB computations. See the -section on "Type Combining" in combine.rs for more details. +The first `at()` call provides a bit of context, i.e., why you are +doing this unification, and in what environment, and the `eq` method +performs the actual equality constraint. + +When you equate things, you force them to be precisely equal. Equating +returns a `InferResult` -- if it returns `Err(err)`, then equating +failed, and the enclosing `TypeError` will tell you what went wrong. + +The success case is perhaps more interesting. The "primary" return +type of `eq` is `()` -- that is, when it succeeds, it doesn't return a +value of any particular interest. Rather, it is executed for its +side-effects of constraining type variables and so forth. However, the +actual return type is not `()`, but rather `InferOk<()>`. The +`InferOk` type is used to carry extra trait obligations -- your job is +to ensure that these are fulfilled (typically by enrolling them in a +fulfillment context). See the [trait README] for more background here. + +[trait README]: ../traits/README.md + +You can also enforce subtyping through `infcx.at(..).sub(..)`. The same +basic concepts apply as above. + +## "Trying" equality + +Sometimes you would like to know if it is *possible* to equate two +types without error. You can test that with `infcx.can_eq` (or +`infcx.can_sub` for subtyping). If this returns `Ok`, then equality +is possible -- but in all cases, any side-effects are reversed. + +Be aware though that the success or failure of these methods is always +**modulo regions**. That is, two types `&'a u32` and `&'b u32` will +return `Ok` for `can_eq`, even if `'a != 'b`. This falls out from the +"two-phase" nature of how we solve region constraints. + +## Snapshots + +As described in the previous section on `can_eq`, often it is useful +to be able to do a series of operations and then roll back their +side-effects. This is done for various reasons: one of them is to be +able to backtrack, trying out multiple possibilities before settling +on which path to take. Another is in order to ensure that a series of +smaller changes take place atomically or not at all. + +To allow for this, the inference context supports a `snapshot` method. +When you call it, it will start recording changes that occur from the +operations you perform. When you are done, you can either invoke +`rollback_to`, which will undo those changes, or else `confirm`, which +will make the permanent. Snapshots can be nested as long as you follow +a stack-like discipline. + +Rather than use snapshots directly, it is often helpful to use the +methods like `commit_if_ok` or `probe` that encapsulte higher-level +patterns. + +## Subtyping obligations + +One thing worth discussing are subtyping obligations. When you force +two types to be a subtype, like `?T <: i32`, we can often convert those +into equality constraints. This follows from Rust's rather limited notion +of subtyping: so, in the above case, `?T <: i32` is equivalent to `?T = i32`. + +However, in some cases we have to be more careful. For example, when +regions are involved. So if you have `?T <: &'a i32`, what we would do +is to first "generalize" `&'a i32` into a type with a region variable: +`&'?b i32`, and then unify `?T` with that (`?T = &'?b i32`). We then +relate this new variable with the original bound: + + &'?b i32 <: &'a i32 + +This will result in a region constraint (see below) of `'?b: 'a`. + +One final interesting case is relating two unbound type variables, +like `?T <: ?U`. In that case, we can't make progress, so we enqueue +an obligation `Subtype(?T, ?U)` and return it via the `InferOk` +mechanism. You'll have to try again when more details about `?T` or +`?U` are known. + +## Region constraints + +Regions are inferred somewhat differently from types. Rather than +eagerly unifying things, we simply collect constraints as we go, but +make (almost) no attempt to solve regions. These constraints have the +form of an outlives constraint: + + 'a: 'b + +Actually the code tends to view them as a subregion relation, but it's the same +idea: + + 'b <= 'a + +(There are various other kinds of constriants, such as "verifys"; see +the `region_constraints` module for details.) + +There is one case where we do some amount of eager unification. If you have an equality constraint +between two regions + + 'a = 'b + +we will record that fact in a unification table. You can then use +`opportunistic_resolve_var` to convert `'b` to `'a` (or vice +versa). This is sometimes needed to ensure termination of fixed-point +algorithms. + +## Extracting region constraints + +Ultimately, region constraints are only solved at the very end of +type-checking, once all other constraints are known. There are two +ways to solve region constraints right now: lexical and +non-lexical. Eventually there will only be one. + +To solve **lexical** region constraints, you invoke +`resolve_regions_and_report_errors`. This will "close" the region +constraint process and invoke the `lexical_region_resolve` code. Once +this is done, any further attempt to equate or create a subtyping +relationship will yield an ICE. + +Non-lexical region constraints are not handled within the inference +context. Instead, the NLL solver (actually, the MIR type-checker) +invokes `take_and_reset_region_constraints` periodically. This +extracts all of the outlives constraints from the region solver, but +leaves the set of variables intact. This is used to get *just* the +region constraints that resulted from some particular point in the +program, since the NLL solver needs to know not just *what* regions +were subregions but *where*. Finally, the NLL solver invokes +`take_region_var_origins`, which "closes" the region constraint +process in the same way as normal solving. + +## Lexical region resolution + +Lexical region resolution is done by initially assigning each region +variable to an empty value. We then process each outlives constraint +repeatedly, growing region variables until a fixed-point is reached. +Region variables can be grown using a least-upper-bound relation on +the region lattice in a fairly straight-forward fashion. diff --git a/src/librustc/infer/lexical_region_resolve/README.md b/src/librustc/infer/lexical_region_resolve/README.md new file mode 100644 index 0000000000000..a53bfec80d981 --- /dev/null +++ b/src/librustc/infer/lexical_region_resolve/README.md @@ -0,0 +1,262 @@ +# Region inference + +## Terminology + +Note that we use the terms region and lifetime interchangeably. + +## Introduction + +See the [general inference README](../README.md) for an overview of +how lexical-region-solving fits into the bigger picture. + +Region constraint collect uses a somewhat more involved algorithm than +type inference. It is not the most efficient thing ever written though +it seems to work well enough in practice (famous last words). The +reason that we use a different algorithm is because, unlike with +types, it is impractical to hand-annotate with regions (in some cases, +there aren't even the requisite syntactic forms). So we have to get +it right, and it's worth spending more time on a more involved +analysis. Moreover, regions are a simpler case than types: they don't +have aggregate structure, for example. + +## The problem + +Basically our input is a directed graph where nodes can be divided +into two categories: region variables and concrete regions. Each edge +`R -> S` in the graph represents a constraint that the region `R` is a +subregion of the region `S`. + +Region variable nodes can have arbitrary degree. There is one region +variable node per region variable. + +Each concrete region node is associated with some, well, concrete +region: e.g., a free lifetime, or the region for a particular scope. +Note that there may be more than one concrete region node for a +particular region value. Moreover, because of how the graph is built, +we know that all concrete region nodes have either in-degree 1 or +out-degree 1. + +Before resolution begins, we build up the constraints in a hashmap +that maps `Constraint` keys to spans. During resolution, we construct +the actual `Graph` structure that we describe here. + +## Computing the values for region variables + +The algorithm is a simple dataflow algorithm. Each region variable +begins as empty. We iterate over the constraints, and for each constraint +we grow the relevant region variable to be as big as it must be to meet all the +constraints. This means the region variables can grow to be `'static` if +necessary. + +## Verification + +After all constraints are fully propoagated, we do a "verification" +step where we walk over the verify bounds and check that they are +satisfied. These bounds represent the "maximal" values that a region +variable can take on, basically. + +## The Region Hierarchy + +### Without closures + +Let's first consider the region hierarchy without thinking about +closures, because they add a lot of complications. The region +hierarchy *basically* mirrors the lexical structure of the code. +There is a region for every piece of 'evaluation' that occurs, meaning +every expression, block, and pattern (patterns are considered to +"execute" by testing the value they are applied to and creating any +relevant bindings). So, for example: + +```rust +fn foo(x: isize, y: isize) { // -+ +// +------------+ // | +// | +-----+ // | +// | +-+ +-+ +-+ // | +// | | | | | | | // | +// v v v v v v v // | + let z = x + y; // | + ... // | +} // -+ + +fn bar() { ... } +``` + +In this example, there is a region for the fn body block as a whole, +and then a subregion for the declaration of the local variable. +Within that, there are sublifetimes for the assignment pattern and +also the expression `x + y`. The expression itself has sublifetimes +for evaluating `x` and `y`. + +#s## Function calls + +Function calls are a bit tricky. I will describe how we handle them +*now* and then a bit about how we can improve them (Issue #6268). + +Consider a function call like `func(expr1, expr2)`, where `func`, +`arg1`, and `arg2` are all arbitrary expressions. Currently, +we construct a region hierarchy like: + + +----------------+ + | | + +--+ +---+ +---+| + v v v v v vv + func(expr1, expr2) + +Here you can see that the call as a whole has a region and the +function plus arguments are subregions of that. As a side-effect of +this, we get a lot of spurious errors around nested calls, in +particular when combined with `&mut` functions. For example, a call +like this one + +```rust +self.foo(self.bar()) +``` + +where both `foo` and `bar` are `&mut self` functions will always yield +an error. + +Here is a more involved example (which is safe) so we can see what's +going on: + +```rust +struct Foo { f: usize, g: usize } +// ... +fn add(p: &mut usize, v: usize) { + *p += v; +} +// ... +fn inc(p: &mut usize) -> usize { + *p += 1; *p +} +fn weird() { + let mut x: Box = box Foo { /* ... */ }; + 'a: add(&mut (*x).f, + 'b: inc(&mut (*x).f)) // (..) +} +``` + +The important part is the line marked `(..)` which contains a call to +`add()`. The first argument is a mutable borrow of the field `f`. The +second argument also borrows the field `f`. Now, in the current borrow +checker, the first borrow is given the lifetime of the call to +`add()`, `'a`. The second borrow is given the lifetime of `'b` of the +call to `inc()`. Because `'b` is considered to be a sublifetime of +`'a`, an error is reported since there are two co-existing mutable +borrows of the same data. + +However, if we were to examine the lifetimes a bit more carefully, we +can see that this error is unnecessary. Let's examine the lifetimes +involved with `'a` in detail. We'll break apart all the steps involved +in a call expression: + +```rust +'a: { + 'a_arg1: let a_temp1: ... = add; + 'a_arg2: let a_temp2: &'a mut usize = &'a mut (*x).f; + 'a_arg3: let a_temp3: usize = { + let b_temp1: ... = inc; + let b_temp2: &'b = &'b mut (*x).f; + 'b_call: b_temp1(b_temp2) + }; + 'a_call: a_temp1(a_temp2, a_temp3) // (**) +} +``` + +Here we see that the lifetime `'a` includes a number of substatements. +In particular, there is this lifetime I've called `'a_call` that +corresponds to the *actual execution of the function `add()`*, after +all arguments have been evaluated. There is a corresponding lifetime +`'b_call` for the execution of `inc()`. If we wanted to be precise +about it, the lifetime of the two borrows should be `'a_call` and +`'b_call` respectively, since the references that were created +will not be dereferenced except during the execution itself. + +However, this model by itself is not sound. The reason is that +while the two references that are created will never be used +simultaneously, it is still true that the first reference is +*created* before the second argument is evaluated, and so even though +it will not be *dereferenced* during the evaluation of the second +argument, it can still be *invalidated* by that evaluation. Consider +this similar but unsound example: + +```rust +struct Foo { f: usize, g: usize } +// ... +fn add(p: &mut usize, v: usize) { + *p += v; +} +// ... +fn consume(x: Box) -> usize { + x.f + x.g +} +fn weird() { + let mut x: Box = box Foo { ... }; + 'a: add(&mut (*x).f, consume(x)) // (..) +} +``` + +In this case, the second argument to `add` actually consumes `x`, thus +invalidating the first argument. + +So, for now, we exclude the `call` lifetimes from our model. +Eventually I would like to include them, but we will have to make the +borrow checker handle this situation correctly. In particular, if +there is a reference created whose lifetime does not enclose +the borrow expression, we must issue sufficient restrictions to ensure +that the pointee remains valid. + +### Modeling closures + +Integrating closures properly into the model is a bit of +work-in-progress. In an ideal world, we would model closures as +closely as possible after their desugared equivalents. That is, a +closure type would be modeled as a struct, and the region hierarchy of +different closure bodies would be completely distinct from all other +fns. We are generally moving in that direction but there are +complications in terms of the implementation. + +In practice what we currently do is somewhat different. The basis for +the current approach is the observation that the only time that +regions from distinct fn bodies interact with one another is through +an upvar or the type of a fn parameter (since closures live in the fn +body namespace, they can in fact have fn parameters whose types +include regions from the surrounding fn body). For these cases, there +are separate mechanisms which ensure that the regions that appear in +upvars/parameters outlive the dynamic extent of each call to the +closure: + +1. Types must outlive the region of any expression where they are used. + For a closure type `C` to outlive a region `'r`, that implies that the + types of all its upvars must outlive `'r`. +2. Parameters must outlive the region of any fn that they are passed to. + +Therefore, we can -- sort of -- assume that any region from an +enclosing fns is larger than any region from one of its enclosed +fn. And that is precisely what we do: when building the region +hierarchy, each region lives in its own distinct subtree, but if we +are asked to compute the `LUB(r1, r2)` of two regions, and those +regions are in disjoint subtrees, we compare the lexical nesting of +the two regions. + +*Ideas for improving the situation:* (FIXME #3696) The correctness +argument here is subtle and a bit hand-wavy. The ideal, as stated +earlier, would be to model things in such a way that it corresponds +more closely to the desugared code. The best approach for doing this +is a bit unclear: it may in fact be possible to *actually* desugar +before we start, but I don't think so. The main option that I've been +thinking through is imposing a "view shift" as we enter the fn body, +so that regions appearing in the types of fn parameters and upvars are +translated from being regions in the outer fn into free region +parameters, just as they would be if we applied the desugaring. The +challenge here is that type inference may not have fully run, so the +types may not be fully known: we could probably do this translation +lazilly, as type variables are instantiated. We would also have to +apply a kind of inverse translation to the return value. This would be +a good idea anyway, as right now it is possible for free regions +instantiated within the closure to leak into the parent: this +currently leads to type errors, since those regions cannot outlive any +expressions within the parent hierarchy. Much like the current +handling of closures, there are no known cases where this leads to a +type-checking accepting incorrect code (though it sometimes rejects +what might be considered correct code; see rust-lang/rust#22557), but +it still doesn't feel like the right approach. diff --git a/src/librustc/infer/region_constraints/README.md b/src/librustc/infer/region_constraints/README.md index b564faf3d0c24..67ad08c753033 100644 --- a/src/librustc/infer/region_constraints/README.md +++ b/src/librustc/infer/region_constraints/README.md @@ -1,22 +1,13 @@ -Region inference +# Region constraint collection -# Terminology +## Terminology Note that we use the terms region and lifetime interchangeably. -# Introduction +## Introduction -Region inference uses a somewhat more involved algorithm than type -inference. It is not the most efficient thing ever written though it -seems to work well enough in practice (famous last words). The reason -that we use a different algorithm is because, unlike with types, it is -impractical to hand-annotate with regions (in some cases, there aren't -even the requisite syntactic forms). So we have to get it right, and -it's worth spending more time on a more involved analysis. Moreover, -regions are a simpler case than types: they don't have aggregate -structure, for example. - -Unlike normal type inference, which is similar in spirit to H-M and thus +As described in the [inference README](../README.md), and unlike +normal type inference, which is similar in spirit to H-M and thus works progressively, the region type inference works by accumulating constraints over the course of a function. Finally, at the end of processing a function, we process and solve the constraints all at @@ -50,7 +41,7 @@ influence inference proper. These take the form of: (say, from where clauses), then we can conclude that `T: 'a` if `'b: 'a` *or* `'c: 'a`. -# Building up the constraints +## Building up the constraints Variables and constraints are created using the following methods: @@ -73,249 +64,7 @@ Alternatively, you can call `commit()` which ends all snapshots. Snapshots can be recursive---so you can start a snapshot when another is in progress, but only the root snapshot can "commit". -## The problem - -Basically our input is a directed graph where nodes can be divided -into two categories: region variables and concrete regions. Each edge -`R -> S` in the graph represents a constraint that the region `R` is a -subregion of the region `S`. - -Region variable nodes can have arbitrary degree. There is one region -variable node per region variable. - -Each concrete region node is associated with some, well, concrete -region: e.g., a free lifetime, or the region for a particular scope. -Note that there may be more than one concrete region node for a -particular region value. Moreover, because of how the graph is built, -we know that all concrete region nodes have either in-degree 1 or -out-degree 1. - -Before resolution begins, we build up the constraints in a hashmap -that maps `Constraint` keys to spans. During resolution, we construct -the actual `Graph` structure that we describe here. - -## Computing the values for region variables - -The algorithm is a simple dataflow algorithm. Each region variable -begins as empty. We iterate over the constraints, and for each constraint -we grow the relevant region variable to be as big as it must be to meet all the -constraints. This means the region variables can grow to be `'static` if -necessary. - -## Verification - -After all constraints are fully propoagated, we do a "verification" -step where we walk over the verify bounds and check that they are -satisfied. These bounds represent the "maximal" values that a region -variable can take on, basically. - -# The Region Hierarchy - -## Without closures - -Let's first consider the region hierarchy without thinking about -closures, because they add a lot of complications. The region -hierarchy *basically* mirrors the lexical structure of the code. -There is a region for every piece of 'evaluation' that occurs, meaning -every expression, block, and pattern (patterns are considered to -"execute" by testing the value they are applied to and creating any -relevant bindings). So, for example: - -```rust -fn foo(x: isize, y: isize) { // -+ -// +------------+ // | -// | +-----+ // | -// | +-+ +-+ +-+ // | -// | | | | | | | // | -// v v v v v v v // | - let z = x + y; // | - ... // | -} // -+ - -fn bar() { ... } -``` - -In this example, there is a region for the fn body block as a whole, -and then a subregion for the declaration of the local variable. -Within that, there are sublifetimes for the assignment pattern and -also the expression `x + y`. The expression itself has sublifetimes -for evaluating `x` and `y`. - -## Function calls - -Function calls are a bit tricky. I will describe how we handle them -*now* and then a bit about how we can improve them (Issue #6268). - -Consider a function call like `func(expr1, expr2)`, where `func`, -`arg1`, and `arg2` are all arbitrary expressions. Currently, -we construct a region hierarchy like: - - +----------------+ - | | - +--+ +---+ +---+| - v v v v v vv - func(expr1, expr2) - -Here you can see that the call as a whole has a region and the -function plus arguments are subregions of that. As a side-effect of -this, we get a lot of spurious errors around nested calls, in -particular when combined with `&mut` functions. For example, a call -like this one - -```rust -self.foo(self.bar()) -``` - -where both `foo` and `bar` are `&mut self` functions will always yield -an error. - -Here is a more involved example (which is safe) so we can see what's -going on: - -```rust -struct Foo { f: usize, g: usize } -// ... -fn add(p: &mut usize, v: usize) { - *p += v; -} -// ... -fn inc(p: &mut usize) -> usize { - *p += 1; *p -} -fn weird() { - let mut x: Box = box Foo { /* ... */ }; - 'a: add(&mut (*x).f, - 'b: inc(&mut (*x).f)) // (..) -} -``` - -The important part is the line marked `(..)` which contains a call to -`add()`. The first argument is a mutable borrow of the field `f`. The -second argument also borrows the field `f`. Now, in the current borrow -checker, the first borrow is given the lifetime of the call to -`add()`, `'a`. The second borrow is given the lifetime of `'b` of the -call to `inc()`. Because `'b` is considered to be a sublifetime of -`'a`, an error is reported since there are two co-existing mutable -borrows of the same data. - -However, if we were to examine the lifetimes a bit more carefully, we -can see that this error is unnecessary. Let's examine the lifetimes -involved with `'a` in detail. We'll break apart all the steps involved -in a call expression: - -```rust -'a: { - 'a_arg1: let a_temp1: ... = add; - 'a_arg2: let a_temp2: &'a mut usize = &'a mut (*x).f; - 'a_arg3: let a_temp3: usize = { - let b_temp1: ... = inc; - let b_temp2: &'b = &'b mut (*x).f; - 'b_call: b_temp1(b_temp2) - }; - 'a_call: a_temp1(a_temp2, a_temp3) // (**) -} -``` - -Here we see that the lifetime `'a` includes a number of substatements. -In particular, there is this lifetime I've called `'a_call` that -corresponds to the *actual execution of the function `add()`*, after -all arguments have been evaluated. There is a corresponding lifetime -`'b_call` for the execution of `inc()`. If we wanted to be precise -about it, the lifetime of the two borrows should be `'a_call` and -`'b_call` respectively, since the references that were created -will not be dereferenced except during the execution itself. - -However, this model by itself is not sound. The reason is that -while the two references that are created will never be used -simultaneously, it is still true that the first reference is -*created* before the second argument is evaluated, and so even though -it will not be *dereferenced* during the evaluation of the second -argument, it can still be *invalidated* by that evaluation. Consider -this similar but unsound example: - -```rust -struct Foo { f: usize, g: usize } -// ... -fn add(p: &mut usize, v: usize) { - *p += v; -} -// ... -fn consume(x: Box) -> usize { - x.f + x.g -} -fn weird() { - let mut x: Box = box Foo { ... }; - 'a: add(&mut (*x).f, consume(x)) // (..) -} -``` - -In this case, the second argument to `add` actually consumes `x`, thus -invalidating the first argument. - -So, for now, we exclude the `call` lifetimes from our model. -Eventually I would like to include them, but we will have to make the -borrow checker handle this situation correctly. In particular, if -there is a reference created whose lifetime does not enclose -the borrow expression, we must issue sufficient restrictions to ensure -that the pointee remains valid. - -## Modeling closures - -Integrating closures properly into the model is a bit of -work-in-progress. In an ideal world, we would model closures as -closely as possible after their desugared equivalents. That is, a -closure type would be modeled as a struct, and the region hierarchy of -different closure bodies would be completely distinct from all other -fns. We are generally moving in that direction but there are -complications in terms of the implementation. - -In practice what we currently do is somewhat different. The basis for -the current approach is the observation that the only time that -regions from distinct fn bodies interact with one another is through -an upvar or the type of a fn parameter (since closures live in the fn -body namespace, they can in fact have fn parameters whose types -include regions from the surrounding fn body). For these cases, there -are separate mechanisms which ensure that the regions that appear in -upvars/parameters outlive the dynamic extent of each call to the -closure: - -1. Types must outlive the region of any expression where they are used. - For a closure type `C` to outlive a region `'r`, that implies that the - types of all its upvars must outlive `'r`. -2. Parameters must outlive the region of any fn that they are passed to. - -Therefore, we can -- sort of -- assume that any region from an -enclosing fns is larger than any region from one of its enclosed -fn. And that is precisely what we do: when building the region -hierarchy, each region lives in its own distinct subtree, but if we -are asked to compute the `LUB(r1, r2)` of two regions, and those -regions are in disjoint subtrees, we compare the lexical nesting of -the two regions. - -*Ideas for improving the situation:* (FIXME #3696) The correctness -argument here is subtle and a bit hand-wavy. The ideal, as stated -earlier, would be to model things in such a way that it corresponds -more closely to the desugared code. The best approach for doing this -is a bit unclear: it may in fact be possible to *actually* desugar -before we start, but I don't think so. The main option that I've been -thinking through is imposing a "view shift" as we enter the fn body, -so that regions appearing in the types of fn parameters and upvars are -translated from being regions in the outer fn into free region -parameters, just as they would be if we applied the desugaring. The -challenge here is that type inference may not have fully run, so the -types may not be fully known: we could probably do this translation -lazilly, as type variables are instantiated. We would also have to -apply a kind of inverse translation to the return value. This would be -a good idea anyway, as right now it is possible for free regions -instantiated within the closure to leak into the parent: this -currently leads to type errors, since those regions cannot outlive any -expressions within the parent hierarchy. Much like the current -handling of closures, there are no known cases where this leads to a -type-checking accepting incorrect code (though it sometimes rejects -what might be considered correct code; see rust-lang/rust#22557), but -it still doesn't feel like the right approach. - -### Skolemization +## Skolemization For a discussion on skolemization and higher-ranked subtyping, please see the module `middle::infer::higher_ranked::doc`. From 5758d4b80d46a66797cc9046c5c93ad8f23d2768 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:54:22 -0500 Subject: [PATCH 53/68] WIP infer/outlives: add license --- src/librustc/infer/outlives/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index ae2fb5e2580e0..0976c5f1f2fc4 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -1,2 +1,12 @@ +// 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. + pub mod env; mod obligations; From 0b7ebac21cd8e6dfd31e16613ade95dbb9c28212 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:54:31 -0500 Subject: [PATCH 54/68] convert TODO in traits into a FIXME --- src/librustc/traits/mod.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index f0f35aa4bc7b6..226175c0dcf22 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -538,11 +538,14 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let region_scope_tree = region::ScopeTree::default(); let free_regions = FreeRegionMap::new(); - // TODO We should really... do something with these. But as of - // this writing we were ignoring them, just without knowing - // it, and it would take some refactoring to stop doing so. - // (In particular, the needed methods currently live in - // regionck.) -nmatsakis + // FIXME. We should really... do something with these region + // obligations. But this call just continues the older + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis let _ = infcx.ignore_region_obligations(body_id); infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); From b3cad88fd36da49a795f5fcce3b3c02784f04426 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:55:34 -0500 Subject: [PATCH 55/68] add FIXME for converting RegionVid to use `newtype_index!` --- src/librustc/ty/sty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index c3b375bf7a8d3..f160e6c79ab2b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -899,7 +899,7 @@ pub struct RegionVid { pub index: u32, } -// TODO after rebasing, should be able to use `newtype_index!` +// FIXME: We could convert this to use `newtype_index!` impl Idx for RegionVid { fn new(value: usize) -> Self { assert!(value < ::std::u32::MAX as usize); From d2b4f270a44a5a6bd42c8a8f1f148a549ae30a8c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 06:16:09 -0500 Subject: [PATCH 56/68] fix mir-opt NLL tests -- variable `'_#0r` is now `'static` --- src/test/mir-opt/nll/named-lifetimes-basic.rs | 10 +++++----- src/test/mir-opt/nll/reborrow-basic.rs | 8 ++++---- src/test/mir-opt/nll/region-liveness-basic.rs | 6 +++--- .../mir-opt/nll/region-liveness-drop-may-dangle.rs | 2 +- .../nll/region-liveness-drop-no-may-dangle.rs | 2 +- .../mir-opt/nll/region-liveness-two-disjoint-uses.rs | 12 ++++++------ src/test/mir-opt/nll/region-subtyping-basic.rs | 10 +++++----- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index e3f67d817f3c2..34d0482a623a9 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: {bb0[0], bb0[1], '_#0r, '_#1r} -// | '_#2r: {bb0[0], bb0[1], '_#2r} -// ... -// fn use_x(_1: &'_#0r mut i32, _2: &'_#1r u32, _3: &'_#0r u32, _4: &'_#2r u32) -> bool { +// | '_#0r: {bb0[0], bb0[1], '_#0r, '_#1r, '_#2r, '_#3r} +// | '_#1r: {bb0[0], bb0[1], '_#1r} +// | '_#2r: {bb0[0], bb0[1], '_#1r, '_#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/reborrow-basic.rs b/src/test/mir-opt/nll/reborrow-basic.rs index c3df0c840ded2..f51e839e4fc35 100644 --- a/src/test/mir-opt/nll/reborrow-basic.rs +++ b/src/test/mir-opt/nll/reborrow-basic.rs @@ -28,12 +28,12 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#5r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#6r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]} // ... -// | '_#7r: {bb0[11], bb0[12], bb0[13], bb0[14]} +// | '_#8r: {bb0[11], bb0[12], bb0[13], bb0[14]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#5r mut i32; +// let _2: &'_#6r mut i32; // ... -// let _4: &'_#7r mut i32; +// let _4: &'_#8r mut i32; // END rustc.main.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 f7276cb297904..ae059febc7109 100644 --- a/src/test/mir-opt/nll/region-liveness-basic.rs +++ b/src/test/mir-opt/nll/region-liveness-basic.rs @@ -31,15 +31,15 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#0r: {bb1[1], bb2[0], bb2[1]} // | '_#1r: {bb1[1], bb2[0], bb2[1]} +// | '_#2r: {bb1[1], bb2[0], bb2[1]} // ... -// let _2: &'_#1r usize; +// let _2: &'_#2r usize; // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir // bb1: { // | Live variables at bb1[0]: [_1, _3] -// _2 = &'_#0r _1[_3]; +// _2 = &'_#1r _1[_3]; // | Live variables at bb1[1]: [_2] // switchInt(const true) -> [0u8: bb3, otherwise: bb2]; // } diff --git a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs index 6527df26eae78..6d7aa0a26c8a8 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-may-dangle.rs @@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]} +// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]} // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs index aedb3f562a66d..2968ae7d485b1 100644 --- a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -46,5 +46,5 @@ impl Drop for Wrap { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]} +// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]} // END rustc.main.nll.0.mir 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 23809d176f698..736d2902ec63f 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,14 +36,14 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#0r: {bb1[1], bb2[0], bb2[1]} +// | '_#1r: {bb1[1], bb2[0], bb2[1]} // ... -// | '_#2r: {bb7[2], bb7[3], bb7[4]} -// | '_#3r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} +// | '_#3r: {bb7[2], bb7[3], bb7[4]} +// | '_#4r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]} // ... -// let mut _2: &'_#3r usize; +// let mut _2: &'_#4r usize; // ... -// _2 = &'_#0r _1[_3]; +// _2 = &'_#1r _1[_3]; // ... -// _2 = &'_#2r (*_11); +// _2 = &'_#3r (*_11); // END rustc.main.nll.0.mir diff --git a/src/test/mir-opt/nll/region-subtyping-basic.rs b/src/test/mir-opt/nll/region-subtyping-basic.rs index cada9c7b2548d..fb178b46b470e 100644 --- a/src/test/mir-opt/nll/region-subtyping-basic.rs +++ b/src/test/mir-opt/nll/region-subtyping-basic.rs @@ -32,16 +32,16 @@ fn main() { // END RUST SOURCE // START rustc.main.nll.0.mir -// | '_#0r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} // | '_#1r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} -// | '_#2r: {bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#2r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]} +// | '_#3r: {bb1[5], bb1[6], bb2[0], bb2[1]} // END rustc.main.nll.0.mir // START rustc.main.nll.0.mir -// let _2: &'_#1r usize; +// let _2: &'_#2r usize; // ... -// let _6: &'_#2r usize; +// let _6: &'_#3r usize; // ... -// _2 = &'_#0r _1[_3]; +// _2 = &'_#1r _1[_3]; // ... // _7 = _2; // ... From 2ccc3c7ff3d7a0d9623116963abd38d0284e58d6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 13:18:14 -0500 Subject: [PATCH 57/68] factor out `free_region_binding_scope` helper --- src/librustc/infer/error_reporting/mod.rs | 8 +------ src/librustc/ty/sty.rs | 29 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 84baece77fe59..d22eb20e70a88 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -177,13 +177,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::ReEarlyBound(_) | ty::ReFree(_) => { - let scope = match *region { - ty::ReEarlyBound(ref br) => { - self.parent_def_id(br.def_id).unwrap() - } - ty::ReFree(ref fr) => fr.scope, - _ => bug!() - }; + let scope = region.free_region_binding_scope(self); let prefix = match *region { ty::ReEarlyBound(ref br) => { format!("the lifetime {} as defined on", br.name) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index f160e6c79ab2b..cf834a3d2d492 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1050,6 +1050,35 @@ impl RegionKind { flags } + + /// Given an early-bound or free region, returns the def-id where it was bound. + /// For example, consider the regions in this snippet of code: + /// + /// ``` + /// impl<'a> Foo { + /// ^^ -- early bound, declared on an impl + /// + /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c + /// ^^ ^^ ^ anonymous, late-bound + /// | early-bound, appears in where-clauses + /// late-bound, appears only in fn args + /// {..} + /// } + /// ``` + /// + /// Here, `free_region_binding_scope('a)` would return the def-id + /// of the impl, and for all the other highlighted regions, it + /// would return the def-id of the function. In other cases (not shown), this + /// function might return the def-id of a closure. + pub fn free_region_binding_scope(&self, tcx: TyCtxt<'_, '_, '_>) -> DefId { + match self { + ty::ReEarlyBound(br) => { + tcx.parent_def_id(br.def_id).unwrap() + } + ty::ReFree(fr) => fr.scope, + _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), + } + } } /// Type utilities From 2ffd2e08d8c493079b553976fb144eb97084edff Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 13:18:42 -0500 Subject: [PATCH 58/68] leak the affects of closures on the free-region-map, like we used to This restores the behavior of regionck with respect to the free-region-map: that is, it collects all the relations from the fn and its closures. This feels a bit fishy but it's the behavior we've had for some time, and it will go away with NLL, so seems best to just keep it. --- src/librustc/infer/outlives/env.rs | 46 +++++++++++++++++++ src/librustc_typeck/check/regionck.rs | 4 +- .../implied-bounds-closure-arg-outlives.rs | 44 ++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/implied-bounds-closure-arg-outlives.rs diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index ef09479f75131..2099e923e0959 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -89,6 +89,52 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { self.free_region_map } + /// This is a hack to support the old-skool regionck, which + /// processes region constraints from the main function and the + /// closure together. In that context, when we enter a closure, we + /// want to be able to "save" the state of the surrounding a + /// function. We can then add implied bounds and the like from the + /// closure arguments into the environment -- these should only + /// apply in the closure body, so once we exit, we invoke + /// `pop_snapshot_post_closure` to remove them. + /// + /// Example: + /// + /// ``` + /// fn foo() { + /// callback(for<'a> |x: &'a T| { + /// // ^^^^^^^ not legal syntax, but probably should be + /// // within this closure body, `T: 'a` holds + /// }) + /// } + /// ``` + /// + /// This "containment" of closure's effects only works so well. In + /// particular, we (intentionally) leak relationships between free + /// regions that are created by the closure's bounds. The case + /// where this is useful is when you have (e.g.) a closure with a + /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this + /// case, we want to keep the relationship `'b: 'a` in the + /// free-region-map, so that later if we have to take `LUB('b, + /// 'a)` we can get the result `'b`. + /// + /// I have opted to keep **all modifications** to the + /// free-region-map, however, and not just those that concern free + /// variables bound in the closure. The latter seems more correct, + /// but it is not the existing behavior, and I could not find a + /// case where the existing behavior went wrong. In any case, it + /// seems like it'd be readily fixed if we wanted. There are + /// similar leaks around givens that seem equally suspicious, to + /// be honest. --nmatsakis + pub fn push_snapshot_pre_closure(&self) -> usize { + self.region_bound_pairs.len() + } + + /// See `push_snapshot_pre_closure`. + pub fn pop_snapshot_post_closure(&mut self, len: usize) { + self.region_bound_pairs.truncate(len); + } + /// This method adds "implied bounds" into the outlives environment. /// Implied bounds are outlives relationships that we can deduce /// on the basis that certain types must be well-formed -- these are diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 932cb12e81dfb..a17133d412c7a 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -428,17 +428,17 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // Save state of current function before invoking // `visit_fn_body`. We will restore afterwards. - let outlives_environment = self.outlives_environment.clone(); let old_body_id = self.body_id; let old_call_site_scope = self.call_site_scope; + let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); let body = self.tcx.hir.body(body_id); self.visit_fn_body(id, body, span); // Restore state from previous function. + self.outlives_environment.pop_snapshot_post_closure(env_snapshot); self.call_site_scope = old_call_site_scope; self.body_id = old_body_id; - self.outlives_environment = outlives_environment; } //visit_pat: visit_pat, // (..) see above diff --git a/src/test/run-pass/implied-bounds-closure-arg-outlives.rs b/src/test/run-pass/implied-bounds-closure-arg-outlives.rs new file mode 100644 index 0000000000000..0e5cc574f0022 --- /dev/null +++ b/src/test/run-pass/implied-bounds-closure-arg-outlives.rs @@ -0,0 +1,44 @@ +// 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 handle the relationships between free +// regions bound in a closure callback. + +#[derive(Copy, Clone)] +struct MyCx<'short, 'long: 'short> { + short: &'short u32, + long: &'long u32, +} + +impl<'short, 'long> MyCx<'short, 'long> { + fn short(self) -> &'short u32 { self.short } + fn long(self) -> &'long u32 { self.long } + fn set_short(&mut self, v: &'short u32) { self.short = v; } +} + +fn with(op: F) -> R +where + F: for<'short, 'long> FnOnce(MyCx<'short, 'long>) -> R, +{ + op(MyCx { + short: &22, + long: &22, + }) +} + +fn main() { + with(|mut cx| { + // For this to type-check, we need to be able to deduce that + // the lifetime of `l` can be `'short`, even though it has + // input from `'long`. + let l = if true { cx.long() } else { cx.short() }; + cx.set_short(l); + }); +} From d59af53686d54704473f51382f096db79dcb86d3 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sat, 4 Nov 2017 20:09:16 -0400 Subject: [PATCH 59/68] Initial attempt at refining liveness with "maybe initialized" analysis --- .../{borrow_check.rs => borrow_check/mod.rs} | 111 ++++++++++-------- .../nll/constraint_generation.rs | 31 +++-- .../{transform => borrow_check}/nll/mod.rs | 33 ++---- .../nll/region_infer.rs | 0 .../nll/renumber.rs | 0 src/librustc_mir/dataflow/impls/borrows.rs | 4 +- src/librustc_mir/transform/mod.rs | 1 - 7 files changed, 97 insertions(+), 83 deletions(-) rename src/librustc_mir/{borrow_check.rs => borrow_check/mod.rs} (96%) rename src/librustc_mir/{transform => borrow_check}/nll/constraint_generation.rs (87%) rename src/librustc_mir/{transform => borrow_check}/nll/mod.rs (86%) rename src/librustc_mir/{transform => borrow_check}/nll/region_infer.rs (100%) rename src/librustc_mir/{transform => borrow_check}/nll/renumber.rs (100%) diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check/mod.rs similarity index 96% rename from src/librustc_mir/borrow_check.rs rename to src/librustc_mir/borrow_check/mod.rs index 6abae2548d981..7f52b57642b0a 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -18,7 +18,6 @@ use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local} use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind}; use rustc::mir::transform::MirSource; -use transform::nll; use rustc_data_structures::indexed_set::{self, IdxSetBuf}; use rustc_data_structures::indexed_vec::{Idx}; @@ -38,6 +37,7 @@ use util::borrowck_errors::{BorrowckErrors, Origin}; use self::MutateMode::{JustWrite, WriteAndRead}; use self::ConsumeKind::{Consume}; +pub(crate) mod nll; pub fn provide(providers: &mut Providers) { *providers = Providers { @@ -77,7 +77,16 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let id = src.item_id(); - let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx, param_env) { + // 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; + + // Replace all regions with fresh inference variables. + let num_region_variables = nll::renumber::renumber_mir(infcx, mir); + + let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx, param_env) { Ok(move_data) => move_data, Err((move_data, move_errors)) => { for move_error in move_errors { @@ -105,31 +114,22 @@ 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 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])); // If we are in non-lexical mode, compute the non-lexical lifetimes. let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { None } else { - Some(nll::compute_regions(infcx, src, param_env, mir)) + Some(nll::compute_regions(infcx, src, param_env, mir, num_region_variables, &flow_inits, &mdpe.move_data)) }; - 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 mut mbcx = MirBorrowckCtxt { tcx: tcx, mir: mir, @@ -138,6 +138,10 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, param_env: param_env, }; + let flow_borrows = FlowInProgress::new(do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, + Borrows::new(tcx, mir, opt_regioncx.as_ref()), + |bd, i| bd.location(i))); + let mut state = InProgress::new(flow_borrows, flow_inits, flow_uninits); @@ -1401,14 +1405,14 @@ impl ContextKind { } impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { - pub(super) fn new(borrows: DataflowResults>, - inits: DataflowResults>, - uninits: DataflowResults>) + fn new(borrows: FlowInProgress>, + inits: FlowInProgress>, + uninits: FlowInProgress>) -> Self { InProgress { - borrows: FlowInProgress::new(borrows), - inits: FlowInProgress::new(inits), - uninits: FlowInProgress::new(uninits), + borrows, + inits, + uninits, } } @@ -1474,35 +1478,42 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { } } -impl<'b, 'gcx, 'tcx> FlowInProgress> { - fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { - let move_data = self.base_results.operator().move_data(); - - let mut todo = vec![mpi]; - let mut push_siblings = false; // don't look at siblings of original `mpi`. - while let Some(mpi) = todo.pop() { - if self.curr_state.contains(&mpi) { - return Some(mpi); - } - let move_path = &move_data.move_paths[mpi]; - if let Some(child) = move_path.first_child { - todo.push(child); - } - if push_siblings { - if let Some(sibling) = move_path.next_sibling { - todo.push(sibling); +macro_rules! has_any_child_of_impl { + ($MaybeTLvals:ident) => { + impl<'b, 'gcx, 'tcx> FlowInProgress<$MaybeTLvals<'b, 'gcx, 'tcx>> { + fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { + let move_data = self.base_results.operator().move_data(); + + let mut todo = vec![mpi]; + let mut push_siblings = false; // don't look at siblings of original `mpi`. + while let Some(mpi) = todo.pop() { + if self.curr_state.contains(&mpi) { + return Some(mpi); + } + let move_path = &move_data.move_paths[mpi]; + if let Some(child) = move_path.first_child { + todo.push(child); + } + if push_siblings { + if let Some(sibling) = move_path.next_sibling { + todo.push(sibling); + } + } else { + // after we've processed the original `mpi`, we should + // always traverse the siblings of any of its + // children. + push_siblings = true; + } } - } else { - // after we've processed the original `mpi`, we should - // always traverse the siblings of any of its - // children. - push_siblings = true; + return None; } } - return None; - } + }; } +has_any_child_of_impl!(MaybeInitializedLvals); +has_any_child_of_impl!(MaybeUninitializedLvals); + 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) diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs similarity index 87% rename from src/librustc_mir/transform/nll/constraint_generation.rs rename to src/librustc_mir/borrow_check/nll/constraint_generation.rs index cb3c4c54e97c6..b088c9d055941 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -21,6 +21,9 @@ 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::{MoveData, LookupResult}; use super::LivenessResults; use super::ToRegionVid; @@ -32,6 +35,8 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( mir: &Mir<'tcx>, mir_source: MirSource, liveness: &LivenessResults, + flow_inits: &FlowInProgress>, + move_data: &MoveData<'tcx>, ) { ConstraintGeneration { infcx, @@ -39,18 +44,22 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( mir, liveness, mir_source, + flow_inits, + move_data, }.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, +struct ConstraintGeneration<'a, 'cx: 'a, 'gcx: 'tcx, 'tcx: 'cx> { + infcx: &'a InferCtxt<'cx, 'gcx, 'tcx>, + regioncx: &'a mut RegionInferenceContext<'tcx>, + mir: &'a Mir<'tcx>, + liveness: &'a LivenessResults, mir_source: MirSource, + flow_inits: &'a FlowInProgress>, + move_data: &'a MoveData<'tcx>, } -impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { +impl<'a, 'cx, 'gcx, 'tcx> ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { fn add_constraints(&mut self) { self.add_liveness_constraints(); self.add_borrow_constraints(); @@ -78,8 +87,12 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { .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); + if let LookupResult::Exact(mpi) = self.move_data.rev_lookup.find(&Lvalue::Local(live_local)) { + if self.flow_inits.has_any_child_of(mpi).is_some() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); + } + } } }); } @@ -215,7 +228,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> { } } -impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> { +impl<'a, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs similarity index 86% rename from src/librustc_mir/transform/nll/mod.rs rename to src/librustc_mir/borrow_check/nll/mod.rs index 18dbd824f185e..9bbb6fbd82520 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -16,6 +16,9 @@ use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; 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; @@ -27,33 +30,20 @@ mod free_regions; pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; -mod renumber; +pub(super) 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>( +pub(super) fn compute_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, source: MirSource, + mir: &Mir<'tcx>, param_env: ty::ParamEnv<'gcx>, - mir: &mut Mir<'tcx>, -) -> RegionInferenceContext<'tcx> { - // Compute named region information. - let free_regions = &free_regions::free_regions(infcx, source); - - // Replace all regions with fresh inference variables. - renumber::renumber_mir(infcx, free_regions, mir); - - // Run the MIR type-checker. - let body_id = source.item_id(); - let constraint_sets = &type_check::type_check(infcx, body_id, param_env, mir); - - // 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); - + num_region_variables: usize, + flow_inits: &FlowInProgress>, + move_data: &MoveData<'tcx>, +) -> RegionInferenceContext { // Compute what is live where. let liveness = &LivenessResults { regular: liveness::liveness_of_locals( @@ -74,7 +64,8 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( }; // Generate non-subtyping constraints. - constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); + constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness, + flow_inits, move_data); // Solve the region constraints. regioncx.solve(infcx, &mir); diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs similarity index 100% rename from src/librustc_mir/transform/nll/region_infer.rs rename to src/librustc_mir/borrow_check/nll/region_infer.rs diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs similarity index 100% rename from src/librustc_mir/transform/nll/renumber.rs rename to src/librustc_mir/borrow_check/nll/renumber.rs diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index b8f49392c5771..af67ded8a3b21 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; diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 322f46cf02b63..73a221141eee0 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(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); From a164ae02557b7bf553abbf12b1c8e773a072f339 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sun, 5 Nov 2017 16:58:22 -0500 Subject: [PATCH 60/68] Take ownership of RegionInferenceContext in Borrows struct --- src/librustc_mir/borrow_check/mod.rs | 12 +++++-- .../nll/free_regions.rs | 0 src/librustc_mir/borrow_check/nll/mod.rs | 35 +++++++++++++++++-- .../nll/subtype_constraint_generation.rs | 0 src/librustc_mir/dataflow/impls/borrows.rs | 6 ++-- 5 files changed, 44 insertions(+), 9 deletions(-) rename src/librustc_mir/{transform => borrow_check}/nll/free_regions.rs (100%) rename src/librustc_mir/{transform => borrow_check}/nll/subtype_constraint_generation.rs (100%) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 7f52b57642b0a..5e8d27e1a9ec1 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -84,7 +84,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let mir = &mut mir; // Replace all regions with fresh inference variables. - let num_region_variables = nll::renumber::renumber_mir(infcx, mir); + let free_regions = nll::replace_regions_in_mir(infcx, src, mir); let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx, param_env) { Ok(move_data) => move_data, @@ -127,7 +127,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { None } else { - Some(nll::compute_regions(infcx, src, param_env, mir, num_region_variables, &flow_inits, &mdpe.move_data)) + Some(nll::compute_regions(infcx, + src, + &free_regions, + mir, + param_env, + &flow_inits, + &mdpe.move_data)) }; let mut mbcx = MirBorrowckCtxt { @@ -139,7 +145,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, }; let flow_borrows = FlowInProgress::new(do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, - Borrows::new(tcx, mir, opt_regioncx.as_ref()), + Borrows::new(tcx, mir, opt_regioncx), |bd, i| bd.location(i))); let mut state = InProgress::new(flow_borrows, diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/free_regions.rs similarity index 100% rename from src/librustc_mir/transform/nll/free_regions.rs rename to src/librustc_mir/borrow_check/nll/free_regions.rs diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 9bbb6fbd82520..106d5131e7422 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -26,11 +26,30 @@ use self::mir_util::PassWhere; mod constraint_generation; mod subtype_constraint_generation; mod free_regions; +use self::free_regions::FreeRegions; pub(crate) mod region_infer; use self::region_infer::RegionInferenceContext; -pub(super) mod renumber; +mod renumber; + +/// Rewrites the regions in the MIR to use NLL variables, also +/// scraping out the set of free regions (e.g., region parameters) +/// declared on the function. That set will need to be given to +/// `compute_regions`. +pub(super) fn replace_regions_in_mir<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx> +) -> FreeRegions<'tcx> { + // Compute named region information. + let free_regions = free_regions::free_regions(infcx, source); + + // Replace all regions with fresh inference variables. + renumber::renumber_mir(infcx, &free_regions, mir); + + free_regions +} /// Computes the (non-lexical) regions from the input MIR. /// @@ -38,12 +57,22 @@ pub(super) mod renumber; pub(super) fn compute_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, source: MirSource, + free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>, param_env: ty::ParamEnv<'gcx>, - num_region_variables: usize, flow_inits: &FlowInProgress>, move_data: &MoveData<'tcx>, -) -> RegionInferenceContext { +) -> RegionInferenceContext<'tcx> { + // Run the MIR type-checker. + let body_id = source.item_id(); + let constraint_sets = &type_check::type_check(infcx, body_id, param_env, mir); + + // 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); + // Compute what is live where. let liveness = &LivenessResults { regular: liveness::liveness_of_locals( 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 100% rename from src/librustc_mir/transform/nll/subtype_constraint_generation.rs rename to src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index af67ded8a3b21..5ead229e4943c 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -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 { idx_vec: IndexVec::new(), location_map: FxHashMap(), @@ -137,7 +137,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) { From ce8e8377687a676bc4c6e0dcdf9b02496b9ca76c Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sun, 5 Nov 2017 22:38:35 -0500 Subject: [PATCH 61/68] Fix tidy errors --- src/librustc_mir/borrow_check/mod.rs | 16 +++++++++------- .../borrow_check/nll/constraint_generation.rs | 14 +++++++++----- src/librustc_mir/borrow_check/nll/mod.rs | 11 +++++++++-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 5e8d27e1a9ec1..e2c1c3482a572 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -127,13 +127,15 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll { None } else { - Some(nll::compute_regions(infcx, - src, - &free_regions, - mir, - param_env, - &flow_inits, - &mdpe.move_data)) + Some(nll::compute_regions( + infcx, + src, + &free_regions, + mir, + param_env, + &flow_inits, + &mdpe.move_data + )) }; let mut mbcx = MirBorrowckCtxt { diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index b088c9d055941..3e1ae961a0bbd 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -83,18 +83,22 @@ impl<'a, 'cx, 'gcx, 'tcx> ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { } }); - self.liveness - .drop - .simulate_block(self.mir, bb, |location, live_locals| { + self.liveness.drop.simulate_block( + self.mir, + bb, + |location, live_locals| { for live_local in live_locals.iter() { - if let LookupResult::Exact(mpi) = self.move_data.rev_lookup.find(&Lvalue::Local(live_local)) { + if let LookupResult::Exact(mpi) = + self.move_data.rev_lookup.find(&Lvalue::Local(live_local)) + { if self.flow_inits.has_any_child_of(mpi).is_some() { let live_local_ty = self.mir.local_decls[live_local].ty; self.add_drop_live_constraint(live_local_ty, location); } } } - }); + }, + ); } } diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 106d5131e7422..0b14de4460b0f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -93,8 +93,15 @@ pub(super) fn compute_regions<'a, 'gcx, 'tcx>( }; // Generate non-subtyping constraints. - constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness, - flow_inits, move_data); + constraint_generation::generate_constraints( + infcx, + &mut regioncx, + &mir, + source, + liveness, + flow_inits, + move_data + ); // Solve the region constraints. regioncx.solve(infcx, &mir); From 0bb2b66fce66f39559578f034cd8f3c0be95c730 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Mon, 6 Nov 2017 23:52:45 -0500 Subject: [PATCH 62/68] Rename lifetimes on ConstraintGeneration, move macro impl to HasMoveData impl, ensure mir is immutably borrowed after renumbering, and add find_local fn to simplify mpi lookup in constraint generation --- src/librustc_mir/borrow_check/mod.rs | 64 +++++++++---------- .../borrow_check/nll/constraint_generation.rs | 38 ++++++----- src/librustc_mir/dataflow/move_paths/mod.rs | 4 ++ 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index e2c1c3482a572..6eeebf548b889 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -81,10 +81,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // 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 free_regions = { + let mir = &mut mir; - // Replace all regions with fresh inference variables. - let free_regions = nll::replace_regions_in_mir(infcx, src, mir); + // Replace all regions with fresh inference variables. + nll::replace_regions_in_mir(infcx, src, mir) + }; + let mir = &mir; let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx, param_env) { Ok(move_data) => move_data, @@ -1486,42 +1489,35 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> { } } -macro_rules! has_any_child_of_impl { - ($MaybeTLvals:ident) => { - impl<'b, 'gcx, 'tcx> FlowInProgress<$MaybeTLvals<'b, 'gcx, 'tcx>> { - fn has_any_child_of(&self, mpi: MovePathIndex) -> Option { - let move_data = self.base_results.operator().move_data(); - - let mut todo = vec![mpi]; - let mut push_siblings = false; // don't look at siblings of original `mpi`. - while let Some(mpi) = todo.pop() { - if self.curr_state.contains(&mpi) { - return Some(mpi); - } - let move_path = &move_data.move_paths[mpi]; - if let Some(child) = move_path.first_child { - todo.push(child); - } - if push_siblings { - if let Some(sibling) = move_path.next_sibling { - todo.push(sibling); - } - } else { - // after we've processed the original `mpi`, we should - // always traverse the siblings of any of its - // children. - push_siblings = true; - } +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]; + let mut push_siblings = false; // don't look at siblings of original `mpi`. + while let Some(mpi) = todo.pop() { + if self.curr_state.contains(&mpi) { + return Some(mpi); + } + let move_path = &move_data.move_paths[mpi]; + if let Some(child) = move_path.first_child { + todo.push(child); + } + if push_siblings { + if let Some(sibling) = move_path.next_sibling { + todo.push(sibling); } - return None; + } else { + // after we've processed the original `mpi`, we should + // always traverse the siblings of any of its + // children. + push_siblings = true; } } - }; + return None; + } } -has_any_child_of_impl!(MaybeInitializedLvals); -has_any_child_of_impl!(MaybeUninitializedLvals); - 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) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 3e1ae961a0bbd..bc6e9159f6f31 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -23,19 +23,19 @@ use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; use borrow_check::FlowInProgress; use dataflow::MaybeInitializedLvals; -use dataflow::move_paths::{MoveData, LookupResult}; +use dataflow::move_paths::MoveData; use super::LivenessResults; use super::ToRegionVid; use super::region_infer::RegionInferenceContext; -pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, regioncx: &mut RegionInferenceContext<'tcx>, mir: &Mir<'tcx>, mir_source: MirSource, liveness: &LivenessResults, - flow_inits: &FlowInProgress>, + flow_inits: &FlowInProgress>, move_data: &MoveData<'tcx>, ) { ConstraintGeneration { @@ -49,17 +49,18 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( }.add_constraints(); } -struct ConstraintGeneration<'a, 'cx: 'a, 'gcx: 'tcx, 'tcx: 'cx> { - infcx: &'a InferCtxt<'cx, 'gcx, 'tcx>, - regioncx: &'a mut RegionInferenceContext<'tcx>, - mir: &'a Mir<'tcx>, - liveness: &'a LivenessResults, +/// '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, mir_source: MirSource, - flow_inits: &'a FlowInProgress>, - move_data: &'a MoveData<'tcx>, + flow_inits: &'cg FlowInProgress>, + move_data: &'cg MoveData<'tcx>, } -impl<'a, 'cx, 'gcx, 'tcx> ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { +impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { fn add_constraints(&mut self) { self.add_liveness_constraints(); self.add_borrow_constraints(); @@ -88,13 +89,10 @@ impl<'a, 'cx, 'gcx, 'tcx> ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { bb, |location, live_locals| { for live_local in live_locals.iter() { - if let LookupResult::Exact(mpi) = - self.move_data.rev_lookup.find(&Lvalue::Local(live_local)) - { - if self.flow_inits.has_any_child_of(mpi).is_some() { - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_drop_live_constraint(live_local_ty, location); - } + let mpi = self.move_data.rev_lookup.find_local(live_local); + if self.flow_inits.has_any_child_of(mpi).is_some() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); } } }, @@ -232,7 +230,7 @@ impl<'a, 'cx, 'gcx, 'tcx> ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { } } -impl<'a, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'a, 'cx, 'gcx, 'tcx> { +impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 5bfecd01aaa82..4f35725034bdc 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -226,6 +226,10 @@ impl<'tcx> MovePathLookup<'tcx> { } } } + + pub fn find_local(&self, local: Local) -> MovePathIndex { + self.locals[local] + } } #[derive(Debug)] From e966e8cfa251d10211abae6a03d22e3d7b2cb515 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Tue, 7 Nov 2017 07:52:42 -0500 Subject: [PATCH 63/68] Add initial attempt at ui/nll test for maybe-initialized drop --- src/test/ui/nll/maybe-initialized-drop.rs | 65 +++++++++++++ src/test/ui/nll/maybe-initialized-drop.stderr | 97 +++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/test/ui/nll/maybe-initialized-drop.rs create mode 100644 src/test/ui/nll/maybe-initialized-drop.stderr 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..40a5f3aee693f --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -0,0 +1,65 @@ +// 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. +// revisions: mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + +struct Wrap<'p> { p: &'p mut i32 } + +impl<'p> Drop for Wrap<'p> { + fn drop(&mut self) { + *self.p += 1; + } +} + +fn foo() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + x += 1; //~ ERROR because of dtor +} + +fn bar() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + std::mem::drop(wrap); + x += 1; // OK, drop is inert +} + +struct Foo<'p> { a: String, b: Wrap<'p> } + +fn move_string(_a: String) { } + +fn move_wrap<'p>(_b: Wrap<'p>) { } + +fn baz_a() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + move_string(foo.a); +} + +fn baz_a_b() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + move_string(foo.a); + move_wrap(foo.b); +} + +fn baz_b() { + let mut x = 0; + let wrap = Wrap { p: &mut x }; + let s = String::from("str"); + let foo = Foo { a: s, b: wrap }; + move_wrap(foo.b); +} + +fn main() { } \ No newline at end of file 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..19fb00d6565a5 --- /dev/null +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -0,0 +1,97 @@ +error[E0506]: cannot assign to `x` because it is borrowed (Ast) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:24:5 + | +23 | let wrap = Wrap { p: &mut x }; + | - borrow of `x` occurs here +24 | x += 1; //~ ERROR because of dtor + | ^^^^^^ assignment to borrowed `x` occurs here + +error[E0506]: cannot assign to `x` because it is borrowed (Ast) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:31:5 + | +29 | let wrap = Wrap { p: &mut x }; + | - borrow of `x` occurs here +30 | std::mem::drop(wrap); +31 | x += 1; // OK, drop is inert + | ^^^^^^ assignment to borrowed `x` occurs here + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:24:5 + | +23 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +24 | x += 1; //~ ERROR because of dtor + | ^^^^^^ assignment to borrowed `x` occurs here + +error[E0503]: cannot use `x` because it was mutably borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:24:5 + | +23 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +24 | x += 1; //~ ERROR because of dtor + | ^^^^^^ use of borrowed `x` + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:25:2 + | +23 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +24 | x += 1; //~ ERROR because of dtor +25 | } + | ^ assignment to borrowed `x` occurs here + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:31:5 + | +29 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +30 | std::mem::drop(wrap); +31 | x += 1; // OK, drop is inert + | ^^^^^^ assignment to borrowed `x` occurs here + +error[E0503]: cannot use `x` because it was mutably borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:31:5 + | +29 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +30 | std::mem::drop(wrap); +31 | x += 1; // OK, drop is inert + | ^^^^^^ use of borrowed `x` + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:32:2 + | +29 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +32 | } + | ^ assignment to borrowed `x` occurs here + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:46:2 + | +42 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +46 | } + | ^ assignment to borrowed `x` occurs here + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:55:2 + | +50 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +55 | } + | ^ assignment to borrowed `x` occurs here + +error[E0506]: cannot assign to `x` because it is borrowed (Mir) + --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:63:2 + | +59 | let wrap = Wrap { p: &mut x }; + | ------ borrow of `x` occurs here +... +63 | } + | ^ assignment to borrowed `x` occurs here + +error: aborting due to 11 previous errors From ef236e45d4e910b8280b4a0a6b9a3fde5918e93f Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Wed, 8 Nov 2017 00:27:58 -0500 Subject: [PATCH 64/68] Disable certain code on nll runs, update constraint generation to fix bugs, and update tests based on feedback --- src/librustc_mir/borrow_check/mod.rs | 13 ++- .../borrow_check/nll/constraint_generation.rs | 38 +++++--- src/librustc_mir/borrow_check/nll/mod.rs | 10 +- src/test/ui/nll/maybe-initialized-drop.rs | 8 +- src/test/ui/nll/maybe-initialized-drop.stderr | 93 +++++-------------- 5 files changed, 63 insertions(+), 99 deletions(-) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 6eeebf548b889..b8c56cf16c925 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -81,11 +81,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // 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 = { + let free_regions = if !tcx.sess.opts.debugging_opts.nll { + None + } else { let mir = &mut mir; // Replace all regions with fresh inference variables. - nll::replace_regions_in_mir(infcx, src, mir) + Some(nll::replace_regions_in_mir(infcx, src, mir)) }; let mir = &mir; @@ -119,7 +121,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); - let flow_inits = FlowInProgress::new(do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, + 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, @@ -133,13 +135,14 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, Some(nll::compute_regions( infcx, src, - &free_regions, + &free_regions.unwrap(), mir, param_env, - &flow_inits, + &mut flow_inits, &mdpe.move_data )) }; + let flow_inits = flow_inits; // remove mut let mut mbcx = MirBorrowckCtxt { tcx: tcx, diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index bc6e9159f6f31..75d54d45eb631 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -13,7 +13,7 @@ use rustc::mir::{Location, Lvalue, Mir, Rvalue}; use rustc::mir::transform::MirSource; use rustc::mir::visit::Visitor; use rustc::mir::Lvalue::Projection; -use rustc::mir::{LvalueProjection, ProjectionElem}; +use rustc::mir::{LvalueProjection, ProjectionElem, Local}; use rustc::infer::InferCtxt; use rustc::traits::{self, ObligationCause}; use rustc::ty::{self, Ty}; @@ -35,7 +35,7 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( mir: &Mir<'tcx>, mir_source: MirSource, liveness: &LivenessResults, - flow_inits: &FlowInProgress>, + flow_inits: &mut FlowInProgress>, move_data: &MoveData<'tcx>, ) { ConstraintGeneration { @@ -56,7 +56,7 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> { mir: &'cg Mir<'tcx>, liveness: &'cg LivenessResults, mir_source: MirSource, - flow_inits: &'cg FlowInProgress>, + flow_inits: &'cg mut FlowInProgress>, move_data: &'cg MoveData<'tcx>, } @@ -84,19 +84,27 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { } }); - self.liveness.drop.simulate_block( - self.mir, - bb, - |location, live_locals| { - for live_local in live_locals.iter() { - let mpi = self.move_data.rev_lookup.find_local(live_local); - if self.flow_inits.has_any_child_of(mpi).is_some() { - let live_local_ty = self.mir.local_decls[live_local].ty; - self.add_drop_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())); + }); + + self.flow_inits.reset_to_entry_of(bb); + for index in 0 .. self.mir.basic_blocks()[bb].statements.len() { + let location = Location { block: bb, statement_index: index }; + let (location2, live_locals) = all_live_locals.pop().unwrap(); + assert_eq!(location, location2); + + for live_local in live_locals { + let mpi = self.move_data.rev_lookup.find_local(live_local); + if self.flow_inits.has_any_child_of(mpi).is_some() { + let live_local_ty = self.mir.local_decls[live_local].ty; + self.add_drop_live_constraint(live_local_ty, location); } - }, - ); + } + + self.flow_inits.reconstruct_statement_effect(location); + } } } diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 0b14de4460b0f..581f255bab7a1 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -37,8 +37,8 @@ mod renumber; /// 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(super) fn replace_regions_in_mir<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +pub(super) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, source: MirSource, mir: &mut Mir<'tcx> ) -> FreeRegions<'tcx> { @@ -54,13 +54,13 @@ pub(super) fn replace_regions_in_mir<'a, 'gcx, 'tcx>( /// Computes the (non-lexical) regions from the input MIR. /// /// This may result in errors being reported. -pub(super) fn compute_regions<'a, 'gcx, 'tcx>( - infcx: &InferCtxt<'a, 'gcx, 'tcx>, +pub(super) fn compute_regions<'cx, 'gcx, 'tcx>( + infcx: &InferCtxt<'cx, 'gcx, 'tcx>, source: MirSource, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>, param_env: ty::ParamEnv<'gcx>, - flow_inits: &FlowInProgress>, + flow_inits: &mut FlowInProgress>, move_data: &MoveData<'tcx>, ) -> RegionInferenceContext<'tcx> { // Run the MIR type-checker. diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs index 40a5f3aee693f..98d1c2d95ebba 100644 --- a/src/test/ui/nll/maybe-initialized-drop.rs +++ b/src/test/ui/nll/maybe-initialized-drop.rs @@ -7,8 +7,8 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -// revisions: mir -//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + +//compile-flags: -Z emit-end-regions -Z borrowck-mir -Z nll struct Wrap<'p> { p: &'p mut i32 } @@ -43,6 +43,7 @@ fn baz_a() { let s = String::from("str"); let foo = Foo { a: s, b: wrap }; move_string(foo.a); + x += 1; //~ ERROR because of Foo.b's Wrap dtor } fn baz_a_b() { @@ -52,6 +53,7 @@ fn baz_a_b() { let foo = Foo { a: s, b: wrap }; move_string(foo.a); move_wrap(foo.b); + x += 1; // OK, drops are inert } fn baz_b() { @@ -60,6 +62,8 @@ fn baz_b() { let s = String::from("str"); let foo = Foo { a: s, b: wrap }; move_wrap(foo.b); + x += 1; //~ ERROR because of Foo.a's implicit dtor + // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones } fn main() { } \ No newline at end of file diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr index 19fb00d6565a5..4cb4f2dea4923 100644 --- a/src/test/ui/nll/maybe-initialized-drop.stderr +++ b/src/test/ui/nll/maybe-initialized-drop.stderr @@ -1,5 +1,5 @@ error[E0506]: cannot assign to `x` because it is borrowed (Ast) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:24:5 + --> $DIR/maybe-initialized-drop.rs:24:5 | 23 | let wrap = Wrap { p: &mut x }; | - borrow of `x` occurs here @@ -7,7 +7,7 @@ error[E0506]: cannot assign to `x` because it is borrowed (Ast) | ^^^^^^ assignment to borrowed `x` occurs here error[E0506]: cannot assign to `x` because it is borrowed (Ast) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:31:5 + --> $DIR/maybe-initialized-drop.rs:31:5 | 29 | let wrap = Wrap { p: &mut x }; | - borrow of `x` occurs here @@ -15,83 +15,32 @@ error[E0506]: cannot assign to `x` because it is borrowed (Ast) 31 | x += 1; // OK, drop is inert | ^^^^^^ assignment to borrowed `x` occurs here -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:24:5 - | -23 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here -24 | x += 1; //~ ERROR because of dtor - | ^^^^^^ assignment to borrowed `x` occurs here - -error[E0503]: cannot use `x` because it was mutably borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:24:5 - | -23 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here -24 | x += 1; //~ ERROR because of dtor - | ^^^^^^ use of borrowed `x` - -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:25:2 - | -23 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here -24 | x += 1; //~ ERROR because of dtor -25 | } - | ^ assignment to borrowed `x` occurs here - -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:31:5 - | -29 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here -30 | std::mem::drop(wrap); -31 | x += 1; // OK, drop is inert - | ^^^^^^ assignment to borrowed `x` occurs here - -error[E0503]: cannot use `x` because it was mutably borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:31:5 - | -29 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here -30 | std::mem::drop(wrap); -31 | x += 1; // OK, drop is inert - | ^^^^^^ use of borrowed `x` - -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:32:2 - | -29 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here -... -32 | } - | ^ assignment to borrowed `x` occurs here - -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:46:2 +error[E0506]: cannot assign to `x` because it is borrowed (Ast) + --> $DIR/maybe-initialized-drop.rs:46:5 | 42 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here + | - borrow of `x` occurs here ... -46 | } - | ^ assignment to borrowed `x` occurs here +46 | x += 1; //~ ERROR because of Foo.b's Wrap dtor + | ^^^^^^ assignment to borrowed `x` occurs here -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:55:2 +error[E0506]: cannot assign to `x` because it is borrowed (Ast) + --> $DIR/maybe-initialized-drop.rs:56:5 | -50 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here +51 | let wrap = Wrap { p: &mut x }; + | - borrow of `x` occurs here ... -55 | } - | ^ assignment to borrowed `x` occurs here +56 | x += 1; // OK, drops are inert + | ^^^^^^ assignment to borrowed `x` occurs here -error[E0506]: cannot assign to `x` because it is borrowed (Mir) - --> /home/paulf/rust/src/test/ui/nll/maybe-initialized-drop.rs:63:2 +error[E0506]: cannot assign to `x` because it is borrowed (Ast) + --> $DIR/maybe-initialized-drop.rs:65:5 | -59 | let wrap = Wrap { p: &mut x }; - | ------ borrow of `x` occurs here +61 | let wrap = Wrap { p: &mut x }; + | - borrow of `x` occurs here ... -63 | } - | ^ assignment to borrowed `x` occurs here +65 | x += 1; //~ ERROR because of Foo.a's implicit dtor + | ^^^^^^ assignment to borrowed `x` occurs here + +error: aborting due to 5 previous errors -error: aborting due to 11 previous errors From 49d53daa0b6687a3eebbad3c3e7898be0b307ec0 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 9 Nov 2017 05:22:54 -0500 Subject: [PATCH 65/68] Simplify drop loop and account for terminators --- .../borrow_check/nll/constraint_generation.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 75d54d45eb631..2db18b282cc8e 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -89,11 +89,13 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { all_live_locals.push((location, live_locals.iter().collect())); }); + let terminator_index = self.mir.basic_blocks()[bb].statements.len(); self.flow_inits.reset_to_entry_of(bb); - for index in 0 .. self.mir.basic_blocks()[bb].statements.len() { - let location = Location { block: bb, statement_index: index }; - let (location2, live_locals) = all_live_locals.pop().unwrap(); - assert_eq!(location, location2); + while let Some((location, live_locals)) = all_live_locals.pop() { + if location.statement_index == terminator_index { + self.flow_inits.reconstruct_terminator_effect(location); + continue; + } for live_local in live_locals { let mpi = self.move_data.rev_lookup.find_local(live_local); From 581b19724a0fc14b16d1c23d3a70cd345752fdf1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Nov 2017 05:58:03 -0500 Subject: [PATCH 66/68] WIP add a bunch of debug statements --- .../borrow_check/nll/constraint_generation.rs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 2db18b282cc8e..23908cbccca87 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -23,7 +23,7 @@ use rustc_data_structures::fx::FxHashSet; use syntax::codemap::DUMMY_SP; use borrow_check::FlowInProgress; use dataflow::MaybeInitializedLvals; -use dataflow::move_paths::MoveData; +use dataflow::move_paths::{MoveData, HasMoveData}; use super::LivenessResults; use super::ToRegionVid; @@ -88,24 +88,37 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { 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() { - if location.statement_index == terminator_index { - self.flow_inits.reconstruct_terminator_effect(location); - continue; - } - 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 self.flow_inits.has_any_child_of(mpi).is_some() { + debug!("add_liveness_constraints: mpi={:?} has initialization children", mpi); let live_local_ty = self.mir.local_decls[live_local].ty; self.add_drop_live_constraint(live_local_ty, location); + } else { + debug!("add_liveness_constraints: mpi={:?} has no initialized children", mpi); } } - self.flow_inits.reconstruct_statement_effect(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); + } } } } From 69a0edf852d4d25d51313912bf643714a2c70a12 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 9 Nov 2017 07:54:33 -0500 Subject: [PATCH 67/68] Apply local effects for flow_init in constraint generation so we get the inits bits --- src/librustc_mir/borrow_check/nll/constraint_generation.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 23908cbccca87..1eef26607f3c8 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -119,6 +119,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}", location); self.flow_inits.reconstruct_statement_effect(location); } + self.flow_inits.apply_local_effect(); } } } From 71ab17df141bad87edf733df56a6d83bf50800d5 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 9 Nov 2017 21:49:34 -0500 Subject: [PATCH 68/68] Shorten line lengths to fix tidy errors --- .../borrow_check/nll/constraint_generation.rs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 1eef26607f3c8..b6cf8af6dd6fd 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -94,29 +94,38 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { 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); + 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]); + &self.flow_inits + .base_results + .operator() + .move_data() + .move_paths[mpi_init]); }); let mpi = self.move_data.rev_lookup.find_local(live_local); if self.flow_inits.has_any_child_of(mpi).is_some() { - debug!("add_liveness_constraints: mpi={:?} has initialization children", mpi); + debug!("add_liveness_constraints: mpi={:?} has initialization children", + mpi); let live_local_ty = self.mir.local_decls[live_local].ty; self.add_drop_live_constraint(live_local_ty, location); } else { - debug!("add_liveness_constraints: mpi={:?} has no initialized children", mpi); + debug!("add_liveness_constraints: mpi={:?} has no initialized children", + mpi); } } if location.statement_index == terminator_index { - debug!("add_liveness_constraints: reconstruct_terminator_effect from {:#?}", location); + 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); + debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}", + location); self.flow_inits.reconstruct_statement_effect(location); } self.flow_inits.apply_local_effect();