From 4afe423fbfeae115e8ff2fb9e9aa99e00cf90095 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 12:26:31 -0400 Subject: [PATCH 01/65] 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 cc2506d1afc5..8afc525b9f87 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 64206b44b9925a8eb5d9e2614c5e6175a1b5533a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 13:41:07 -0400 Subject: [PATCH 02/65] 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 79eeebfb2503..3010123f4956 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 8afc525b9f87..6767fbae3d8b 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 62d2fe79b217..69a57ac0b553 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 fd93aa162a61..56c2a38501ef 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 ad7978480a6b..b1ac3abe2302 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 0d78e40e88dbd53619cd5ec04b0f53cea871c07d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 14:23:30 -0400 Subject: [PATCH 03/65] 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 e9916bd77e75..c262e966576b 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 1f0fd7b01d37..e46613b3e4da 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 3010123f4956..110c49d820ab 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 855cc069d117..75446586365d 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 106b1b08656f..7c38cf75b8d5 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 session::DiagnosticMessageId; @@ -481,30 +480,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); @@ -543,15 +526,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 69a57ac0b553..297974f1c9a9 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 56c2a38501ef..923199501806 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 1a8ad9718cfa..97c34a1c3027 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 2c44c40d83d4..a7db51a540b3 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::ty::util::ExplicitSelf; use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; @@ -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 82d59ecfc92c..19ea1b179500 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 5996c8e54387..f7f84e6452dd 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 c925008a5ce44fd5f4755279793c64bb9ccb50f4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 1 Nov 2017 14:52:24 -0400 Subject: [PATCH 04/65] 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 110c49d820ab..ba99ff5291a1 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 297974f1c9a9..cd4a6878851c 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 d73be851fbcaa2887d390192c6774b3792411c9f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 2 Nov 2017 06:06:09 -0400 Subject: [PATCH 05/65] 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 ba99ff5291a1..c76d098bd699 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 19ea1b179500..0c4a5512dd60 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 b1ac3abe2302..77a34023df3d 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 000000000000..9bd7384a48e1 --- /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 5227955d7b90..014b8b14edb8 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 22cd041ba0747e680e3faea7b6db2bbd40b41198 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 Nov 2017 05:31:19 -0400 Subject: [PATCH 06/65] 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 c76d098bd699..41e1bf303b38 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 9bd7384a48e1..edcabd531814 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 cd4a6878851c..0489a316cb3e 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 0c4a5512dd60..c8b2032a4987 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 77a34023df3d..20487dda2019 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 e0630e868320bc298da6f93ae06c8b32a829d01b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 3 Nov 2017 18:03:43 -0400 Subject: [PATCH 07/65] 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 edcabd531814..4cd9c3f9ae39 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 bf1cc682a8aa..8973d1e0c5a5 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 20487dda2019..7f1547c0c44d 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 3cc44a569ded1484b0ab4fcc5b8cfd545ea3a4b5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 05:37:18 -0400 Subject: [PATCH 08/65] 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 4cd9c3f9ae39..05e14daa2813 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 8973d1e0c5a5..a9efb042f3d8 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 b587c1a024f6946ee31186447564a2e5cb4e7602 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 05:58:57 -0400 Subject: [PATCH 09/65] 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 7f1547c0c44d..06e0e6ccdb59 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 0c81d0158f70a48945203daa8447bb84a5d0f5cf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 07:13:46 -0400 Subject: [PATCH 10/65] 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 6767fbae3d8b..297feead6176 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 c8b2032a4987..7d98a1c9246b 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 06e0e6ccdb59..5954a0f2ecdc 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 000000000000..f84e0dd880fc --- /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 56e5eb5fd4c3977fb6abdb867af4a85076045d59 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 07:23:21 -0400 Subject: [PATCH 11/65] 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 41e1bf303b38..e3db08c0adff 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 000000000000..321c4b87fb30 --- /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 15a2dfa324d6c10f6dfae0f874d050a009ab8f3d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 07:36:00 -0400 Subject: [PATCH 12/65] 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 e3db08c0adff..e73d74c22ba7 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 f84e0dd880fc..ef09479f7513 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 321c4b87fb30..ae2fb5e2580e 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 05e14daa2813..2fb085bc1d86 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 7d98a1c9246b..c8b2032a4987 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 5954a0f2ecdc..932cb12e81df 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 6d672961fbb0f5b69d39003757bddef1d1d4469a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 09:08:11 -0400 Subject: [PATCH 13/65] 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 18c26500dbe1..bea273c84a92 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 dc462cd9c74f..0a9f673abe06 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 9e8abd704a918d8023d3f0cc2268ae37439237c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 17:56:27 -0400 Subject: [PATCH 14/65] apply rustfmt to `type_check` --- src/librustc_mir/transform/type_check.rs | 620 +++++++++++++---------- 1 file changed, 364 insertions(+), 256 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 0a9f673abe06..2adb36434d8a 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); } @@ -707,7 +803,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.assert_iscleanup(mir, block_data, cleanup, true); } } - TerminatorKind::FalseEdges { real_target, ref imaginary_targets } => { + TerminatorKind::FalseEdges { + real_target, + ref imaginary_targets, + } => { self.assert_iscleanup(mir, block, real_target, is_cleanup); for target in imaginary_targets { self.assert_iscleanup(mir, block, *target, is_cleanup); @@ -716,15 +815,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 +842,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 +855,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 +876,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 +895,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 +916,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 +924,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 def_id = src.def_id; let id = tcx.hir.as_local_node_id(def_id).unwrap(); debug!("run_pass: {:?}", def_id); From efa09dbea5bd886389f994d511e502422b8d5149 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 4 Nov 2017 20:21:28 -0400 Subject: [PATCH 15/65] 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 2adb36434d8a..f45dd4f3c71e 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> { @@ -898,26 +905,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() } } @@ -946,7 +940,6 @@ impl MirPass for TypeckMir { } } checker.typeck_mir(mir); - checker.verify_obligations(mir); }); } } From 467f2ea6531437d7cbc51712ba01886f41e2cf40 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:05:31 -0500 Subject: [PATCH 16/65] 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 000000000000..f32cd45a7409 --- /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 f5327fad3123..59e9c4d3d001 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -20,13 +20,11 @@ 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::collections::BTreeMap; use std::cell::{Cell, RefCell}; @@ -34,6 +32,7 @@ use std::fmt; use std::mem; use std::u32; +mod lexical_resolve; mod graphviz; /// A constraint that influences the inference process. @@ -63,10 +62,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)] @@ -178,6 +177,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> { @@ -803,21 +808,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)) @@ -892,644 +882,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 { @@ -1618,31 +970,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 58c77600a53aacdf47d0b7cdcf74153b56581cc3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Nov 2017 16:58:14 -0500 Subject: [PATCH 17/65] 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 f32cd45a7409..8c5d7a7fc9b0 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 e73d74c22ba7..cf32113343df 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 59e9c4d3d001..e197f4f27ef1 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -32,9 +32,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, PartialOrd, Ord)] pub enum Constraint<'tcx> { @@ -186,8 +183,8 @@ 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 @@ -198,14 +195,14 @@ pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { /// which in turn affects the way that region errors are reported, /// leading to small variations in error output across runs and /// platforms. - 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 @@ -224,7 +221,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>, @@ -246,7 +243,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 ef5de07fc5b62ddd914c710a47d4a6be9a4f77cc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Nov 2017 17:00:03 -0500 Subject: [PATCH 18/65] fix rename to block_data in type_check.rs --- src/librustc_mir/transform/type_check.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index f45dd4f3c71e..fa90835c294b 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -814,9 +814,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { 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); } } } From 9d63330b6d8720ef9a6fd0df42c06ea4915981d1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:23:30 -0500 Subject: [PATCH 19/65] 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 c262e966576b..072f76eb6aa8 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 efe364166e4b..403ff3c4dfa2 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -212,13 +212,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 8c5d7a7fc9b0..682f5743e4bf 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 e197f4f27ef1..ef888ea4b9f0 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; @@ -36,20 +33,20 @@ use std::u32; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] 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 @@ -98,13 +95,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, @@ -138,7 +135,7 @@ pub enum UndoLogEntry<'tcx> { } #[derive(Copy, Clone, PartialEq)] -pub enum CombineMapType { +enum CombineMapType { Lub, Glb, } @@ -168,19 +165,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>, @@ -426,7 +417,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"); @@ -578,13 +569,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, @@ -725,16 +716,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); } } } @@ -817,13 +808,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 8e9e15446f110b01fc06ef0dc40b57f991acc0f3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:23:53 -0500 Subject: [PATCH 20/65] 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 ef888ea4b9f0..91989d7b106d 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -29,6 +29,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, PartialOrd, Ord)] pub enum Constraint<'tcx> { @@ -268,89 +270,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 { @@ -863,11 +782,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 000000000000..acc930bfa3b1 --- /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 b76978530ca40eaa72f16486edbc12145fd1ddd0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:28:22 -0500 Subject: [PATCH 21/65] 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 36370e23f216..c64bd610962e 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 072f76eb6aa8..9ce0e503280e 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 e0b8a193ede0..6af7415ba537 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 682f5743e4bf..217387befd89 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 91989d7b106d..d5997aa0662f 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -142,31 +142,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 ec48b018d6c9e8e24be70f7c7a1ef8afc1cdc5da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:48:16 -0500 Subject: [PATCH 22/65] 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 217387befd89..7dbb5b1ff11a 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 cf32113343df..d50d31d34bb8 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 d5997aa0662f..26c206f1b568 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -142,12 +142,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> { @@ -208,10 +202,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 { @@ -250,7 +240,6 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { RegionVarBindings { tcx, var_origins: RefCell::new(Vec::new()), - values: RefCell::new(None), constraints: RefCell::new(BTreeMap::new()), verifys: RefCell::new(Vec::new()), givens: RefCell::new(FxHashSet()), @@ -517,14 +506,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); // never overwrite an existing (constraint, origin) - only insert one if it isn't @@ -540,8 +523,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 @@ -560,8 +541,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); @@ -591,8 +570,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, @@ -644,8 +621,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) => { @@ -670,8 +645,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 10899e42afb8..f01be0cd9e4a 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 498e1aa3520d..31c09c4cd575 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 daceedf314f882f07f040a78dde173c3b12ad6bd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 05:52:59 -0500 Subject: [PATCH 23/65] region_inference: rustfmt --- src/librustc/infer/region_inference/mod.rs | 335 ++++++++++++--------- 1 file changed, 188 insertions(+), 147 deletions(-) diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs index 26c206f1b568..c1518ddeda57 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::collections::BTreeMap; use std::cell::{Cell, RefCell}; @@ -144,7 +144,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>, @@ -223,15 +223,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, + } } } @@ -271,10 +280,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 { @@ -282,7 +293,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) { @@ -296,7 +309,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); } @@ -340,19 +354,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; } @@ -379,42 +397,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 { @@ -432,13 +452,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); @@ -448,33 +468,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> { @@ -513,12 +525,15 @@ impl<'a, 'gcx, 'tcx> RegionVarBindings<'a, 'gcx, 'tcx> { // never overwrite an existing (constraint, origin) - only insert one if it isn't // present in the map yet. This prevents origins from outside the snapshot being // replaced with "less informative" origins e.g. during calls to `can_eq` - self.constraints.borrow_mut().entry(constraint).or_insert_with(|| { - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddConstraint(constraint)); - } - origin - }); + self.constraints + .borrow_mut() + .entry(constraint) + .or_insert_with(|| { + if self.in_snapshot() { + self.undo_log.borrow_mut().push(AddConstraint(constraint)); + } + origin + }); } fn add_verify(&self, verify: Verify<'tcx>) { @@ -527,8 +542,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(); @@ -549,10 +566,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. @@ -565,23 +584,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 @@ -602,11 +626,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, @@ -615,11 +641,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) { @@ -631,19 +658,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) { @@ -655,11 +685,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), + ), } } @@ -675,14 +707,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) { @@ -702,11 +736,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() } @@ -719,21 +751,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(); } @@ -741,8 +780,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 + ) } } @@ -776,13 +819,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 63d658d87c97cecbd72b9c9cf1d96f936a8a1a06 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:07:22 -0500 Subject: [PATCH 24/65] 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 d7afeba7dc96..9b32c1ff385b 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 6736751a5a2c..bf0694976ca9 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 403ff3c4dfa2..af8a2fcfc3bf 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -57,12 +57,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; } @@ -117,7 +118,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 7dbb5b1ff11a..f15d4785da9c 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 04b470b29fc5..68cecf216d8a 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 d50d31d34bb8..b176646ac5fb 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 c1518ddeda57..93360706149b 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -144,8 +144,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 @@ -244,10 +243,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(BTreeMap::new()), verifys: RefCell::new(Vec::new()), @@ -397,21 +395,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()); @@ -489,7 +496,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. @@ -515,7 +526,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>) { @@ -643,6 +654,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>, @@ -658,18 +670,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>, @@ -685,19 +692,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> { @@ -707,30 +712,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 { @@ -753,6 +760,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, @@ -769,7 +777,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 acc930bfa3b1..ee45f7bd8280 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 f01be0cd9e4a..b9501773e02b 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 31c09c4cd575..66113ffef3d6 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 cff191d4440df2befeb661f5ce7b9a53a03ce548 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:25:23 -0500 Subject: [PATCH 25/65] 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 | 215 +++++++++--------- src/librustc/infer/resolve.rs | 6 +- src/librustc/infer/sub.rs | 2 +- 11 files changed, 151 insertions(+), 156 deletions(-) diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index f9ffaee81f15..dbe3905a8c61 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 9cad6ce6f9fa..b6637069408e 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 9b32c1ff385b..c0e136941061 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 bf0694976ca9..e2a214ed0cbf 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 af8a2fcfc3bf..dd775fb5e17c 100644 --- a/src/librustc/infer/lexical_region_resolve/graphviz.rs +++ b/src/librustc/infer/lexical_region_resolve/graphviz.rs @@ -113,8 +113,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 f15d4785da9c..ac3ba57d980c 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 68cecf216d8a..e3a16ecb444a 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 b176646ac5fb..4aef09aa935a 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 93360706149b..0731a2cfed6c 100644 --- a/src/librustc/infer/region_inference/mod.rs +++ b/src/librustc/infer/region_inference/mod.rs @@ -24,7 +24,6 @@ use ty::ReStatic; use ty::{BrFresh, ReLateBound, ReSkolemized, ReVar}; use std::collections::BTreeMap; -use std::cell::{Cell, RefCell}; use std::fmt; use std::mem; use std::u32; @@ -145,7 +144,7 @@ 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 @@ -156,14 +155,14 @@ pub struct RegionVarBindings<'tcx> { /// which in turn affects the way that region errors are reported, /// leading to small variations in error output across runs and /// platforms. - pub(in infer) constraints: RefCell, SubregionOrigin<'tcx>>>, + pub(in infer) constraints: BTreeMap, 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 @@ -182,12 +181,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. /// @@ -198,9 +197,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 { @@ -246,73 +245,70 @@ impl TaintDirections { impl<'tcx> RegionVarBindings<'tcx> { pub fn new() -> RegionVarBindings<'tcx> { RegionVarBindings { - var_origins: RefCell::new(Vec::new()), - constraints: RefCell::new(BTreeMap::new()), - 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: BTreeMap::new(), + 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"); @@ -321,48 +317,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 {:?}", @@ -373,7 +367,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 @@ -396,16 +390,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)) } @@ -414,7 +408,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, @@ -422,23 +416,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() ); @@ -453,13 +447,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() @@ -468,11 +460,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>( @@ -497,7 +489,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn new_bound( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, debruijn: ty::DebruijnIndex, ) -> Region<'tcx> { @@ -519,35 +511,36 @@ 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); // never overwrite an existing (constraint, origin) - only insert one if it isn't // present in the map yet. This prevents origins from outside the snapshot being // replaced with "less informative" origins e.g. during calls to `can_eq` + let in_snapshot = self.in_snapshot(); + let undo_log = &mut self.undo_log; self.constraints - .borrow_mut() .entry(constraint) .or_insert_with(|| { - if self.in_snapshot() { - self.undo_log.borrow_mut().push(AddConstraint(constraint)); + if in_snapshot { + undo_log.push(AddConstraint(constraint)); } origin }); } - 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); @@ -559,26 +552,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>, @@ -590,13 +581,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>, @@ -638,7 +629,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>, @@ -653,7 +644,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn lub_regions( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, origin: SubregionOrigin<'tcx>, a: Region<'tcx>, @@ -675,7 +666,7 @@ impl<'tcx> RegionVarBindings<'tcx> { } pub fn glb_regions( - &self, + &mut self, tcx: TyCtxt<'_, '_, 'tcx>, origin: SubregionOrigin<'tcx>, a: Region<'tcx>, @@ -697,23 +688,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>, @@ -721,13 +712,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] { @@ -741,7 +732,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), @@ -778,8 +769,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 b9501773e02b..8a99eb080143 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 405699968135..bba4328e6ea8 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 23abd8513857fdaef35ee48b2df0c69b6f784fcf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:34:22 -0500 Subject: [PATCH 26/65] 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 9ce0e503280e..67fdd68a826c 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 e2a214ed0cbf..5f898856d31f 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 dd775fb5e17c..baf7f0aac3df 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 ac3ba57d980c..aeeacfb132a6 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 4aef09aa935a..e5e6f292f687 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 a7882992d61c..d3aa80e5585e 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}; @@ -320,7 +320,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, @@ -407,7 +407,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, @@ -646,7 +646,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 272f13b28030..ea0fa945c378 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 ae4adbfb1f49..3162ef54f39b 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 48d8f7210b99299eddeead7ac381cc11a4a2049e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 06:55:40 -0500 Subject: [PATCH 27/65] 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 dbe3905a8c61..0c59fa703bb6 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 b6637069408e..729e67437ba3 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 c0e136941061..d63036eecff9 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 5f898856d31f..2aef9fece87a 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 e3a16ecb444a..0c4f4efe9947 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 e5e6f292f687..fe15f9acdb5a 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 8a99eb080143..53d963832426 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 bba4328e6ea8..bc4bb0c4712f 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 326ec52eacf34a0a446ca1775e514cf7e6016de4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 07:01:48 -0500 Subject: [PATCH 28/65] 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 baf7f0aac3df..880c0e229474 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; @@ -57,7 +57,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; @@ -113,7 +113,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 aeeacfb132a6..9a02b274b5fb 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 fe15f9acdb5a..d42419d7dc64 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 0731a2cfed6c..446acac4fd18 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -143,7 +143,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 @@ -242,9 +242,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: BTreeMap::new(), verifys: Vec::new(), @@ -264,7 +264,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, @@ -274,7 +274,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!( @@ -294,7 +294,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 { @@ -523,7 +523,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); // never overwrite an existing (constraint, origin) - only insert one if it isn't // present in the map yet. This prevents origins from outside the snapshot being @@ -542,7 +542,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 { @@ -594,7 +594,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 @@ -651,7 +651,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 @@ -673,7 +673,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 adf1519941deb21bd12b5923a45fdd0c21f79140 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 07:22:39 -0500 Subject: [PATCH 29/65] 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 0c59fa703bb6..2ae8f8ae9335 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 729e67437ba3..756a6947ee3f 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 d63036eecff9..8b42314ed97c 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 2aef9fece87a..c49b3b4b9c87 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 0c4f4efe9947..4a2a7a6bdfec 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 d42419d7dc64..a5cae839aa70 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 53d963832426..5e70c0ce368f 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 bc4bb0c4712f..f891f692c7d8 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 f6037f2816af15ebd2a78ff5f504b9358be91424 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 11:59:49 -0500 Subject: [PATCH 30/65] 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 | 203 +++++++++--------- 4 files changed, 123 insertions(+), 119 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/graphviz.rs b/src/librustc/infer/lexical_region_resolve/graphviz.rs index 880c0e229474..412094873957 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; @@ -57,7 +57,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; @@ -113,7 +113,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); @@ -267,15 +267,15 @@ impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> { pub type ConstraintMap<'tcx> = BTreeMap, 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 9a02b274b5fb..3522420a5dc1 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 a5cae839aa70..53112bcd8807 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 446acac4fd18..d2cd52c73e2c 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -30,6 +30,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: BTreeMap, 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, PartialOrd, Ord)] pub enum Constraint<'tcx> { @@ -143,65 +206,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. - /// - /// Using `BTreeMap` because the order in which we iterate over - /// these constraints can affect the way we build the region graph, - /// which in turn affects the way that region errors are reported, - /// leading to small variations in error output across runs and - /// platforms. - pub(in infer) constraints: BTreeMap, 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, @@ -245,10 +249,7 @@ impl TaintDirections { impl<'tcx> RegionConstraintCollector<'tcx> { pub fn new() -> RegionConstraintCollector<'tcx> { RegionConstraintCollector { - var_origins: Vec::new(), - constraints: BTreeMap::new(), - verifys: Vec::new(), - givens: FxHashSet(), + data: RegionConstraintData::default(), lubs: FxHashMap(), glbs: FxHashMap(), skolemization_count: 0, @@ -258,6 +259,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() } @@ -289,8 +295,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } else { (*self.undo_log)[snapshot.length] = CommitedSnapshot; } - self.unification_table - .commit(snapshot.region_snapshot); + self.unification_table.commit(snapshot.region_snapshot); } pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { @@ -304,8 +309,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { let c = self.undo_log.pop().unwrap(); assert!(c == OpenSnapshot); self.skolemization_count = snapshot.skolemization_count; - self.unification_table - .rollback_to(snapshot.region_snapshot); + self.unification_table.rollback_to(snapshot.region_snapshot); } fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) { @@ -317,18 +321,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); @@ -339,18 +343,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 }); @@ -367,7 +364,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 @@ -523,21 +520,22 @@ impl<'tcx> RegionConstraintCollector<'tcx> { fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { // cannot add constraints once regions are resolved - debug!("RegionConstraintCollector: add_constraint({:?})", constraint); + debug!( + "RegionConstraintCollector: add_constraint({:?})", + constraint + ); // never overwrite an existing (constraint, origin) - only insert one if it isn't // present in the map yet. This prevents origins from outside the snapshot being // replaced with "less informative" origins e.g. during calls to `can_eq` let in_snapshot = self.in_snapshot(); let undo_log = &mut self.undo_log; - self.constraints - .entry(constraint) - .or_insert_with(|| { - if in_snapshot { - undo_log.push(AddConstraint(constraint)); - } - origin - }); + self.data.constraints.entry(constraint).or_insert_with(|| { + if in_snapshot { + undo_log.push(AddConstraint(constraint)); + } + origin + }); } fn add_verify(&mut self, verify: Verify<'tcx>) { @@ -552,8 +550,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)); } @@ -561,7 +559,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)); @@ -767,11 +765,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // 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( - tcx, - &self.undo_log[mark.length..], - &self.verifys, - ); + taint_set.fixed_point(tcx, &self.undo_log[mark.length..], &self.data.verifys); debug!("tainted: result={:?}", taint_set); return taint_set.into_set(); } @@ -866,3 +860,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 bea6b94273bc616f806b111c8f1f6b3d2e3e5380 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 12:03:49 -0500 Subject: [PATCH 31/65] 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 e58251c846f8..f871f034a924 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 95db68fea5cf..1df83c7fb0c3 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 524e23ae2e842a6f52d5fc4d5819473b73a335ef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:06:46 -0500 Subject: [PATCH 32/65] 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 3522420a5dc1..3aeecaf166ac 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 d2cd52c73e2c..634e46426006 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}; @@ -51,6 +52,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 @@ -58,7 +61,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). @@ -344,10 +347,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 }); @@ -364,7 +364,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 @@ -862,10 +862,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 a60cad0de9f7..a73b234ffbea 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, PartialOrd, Ord)] 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 a733e9de5a1a..622e8c51bee9 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 a8daa37df694279d0e9241ccba46533926dfd70d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 18:46:37 -0500 Subject: [PATCH 33/65] 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 634e46426006..749a47004da3 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -562,7 +562,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 1efcf1a115f4f4501e1a9de1e57dd3b00c0bc863 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:37:55 -0500 Subject: [PATCH 34/65] 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 3aeecaf166ac..e097bf27c473 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 53112bcd8807..a1ad65a6c4a4 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 749a47004da3..36e5e3039572 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -32,10 +32,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. @@ -49,20 +65,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: BTreeMap, SubregionOrigin<'tcx>>, @@ -252,6 +273,7 @@ impl TaintDirections { impl<'tcx> RegionConstraintCollector<'tcx> { pub fn new() -> RegionConstraintCollector<'tcx> { RegionConstraintCollector { + var_origins: VarOrigins::default(), data: RegionConstraintData::default(), lubs: FxHashMap(), glbs: FxHashMap(), @@ -263,8 +285,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 { @@ -324,8 +346,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); @@ -347,7 +369,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 }); @@ -363,8 +385,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 @@ -862,9 +885,3 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { } } } - -impl<'tcx> RegionConstraintData<'tcx> { - pub fn num_vars(&self) -> usize { - self.var_origins.len() - } -} From 1430a600ded4b3697fee8ce16fdb6714dbbc06ba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:42:56 -0500 Subject: [PATCH 35/65] 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 a1ad65a6c4a4..21f427fa80c1 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 36e5e3039572..057f1b35ac10 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -285,10 +285,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 034018cd9036ae3375e9b59d940b98b6aec766da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 14:46:58 -0500 Subject: [PATCH 36/65] 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 e097bf27c473..0692d284d7c1 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 37945fe3e89951a92c5abd110e8fa1a368c7eed9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:40:35 -0500 Subject: [PATCH 37/65] 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 fa90835c294b..6384fc56b300 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 ad93b695d1fc8340d520aea1ad22e1d35930e1e2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 5 Nov 2017 18:47:09 -0500 Subject: [PATCH 38/65] MIR typeck: refactor to track region constraints --- src/librustc/infer/region_constraints/mod.rs | 8 + src/librustc_mir/transform/type_check.rs | 314 ++++++++++++++----- 2 files changed, 250 insertions(+), 72 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 057f1b35ac10..a9e36a5740fd 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -896,3 +896,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 6384fc56b300..a3dbcefd0e0c 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 transform::{MirPass, MirSource}; 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, @@ -834,7 +971,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 @@ -850,7 +987,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 @@ -868,7 +1011,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); @@ -894,16 +1037,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() } @@ -924,16 +1066,44 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let mut checker = TypeChecker::new(&infcx, 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, 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 89c1b6009bf99fab4882492c6b9677030227b045 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:04:45 -0500 Subject: [PATCH 39/65] 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 5e65398e2b90..af309342dc52 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(core_intrinsics)] #![feature(decl_macro)] #![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 554d212880ea..36c14b55568f 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::middle::free_region::FreeRegionMap; 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>, @@ -78,9 +81,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 553d5ad4a320..f5731255449e 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 a3ff7a041ca0..c0d6e0cbe657 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 72675d82d7147c7de54f17e59b654d2f046d1cbd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:15:38 -0500 Subject: [PATCH 40/65] replace `RegionIndex` with `RegionVid` (which now impls Idx) --- 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 ++++---- 7 files changed, 44 insertions(+), 54 deletions(-) diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 928c07b7fbc0..2e4dddc212b3 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; @@ -145,7 +145,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 b095a198d8f0..627bc7a83245 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -23,7 +23,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>( @@ -102,7 +102,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); }); } @@ -197,8 +197,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()); } @@ -227,8 +227,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 36c14b55568f..101fed3cfa63 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::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::middle::free_region::FreeRegionMap; -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>, @@ -81,9 +79,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 f27d0a8da16c..f3e24e925890 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -11,9 +11,8 @@ use rustc::hir::def_id::DefId; use rustc::mir::Mir; use rustc::infer::InferCtxt; -use rustc::ty::{self, RegionKind}; +use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; -use rustc_data_structures::indexed_vec::Idx; use std::collections::BTreeSet; use transform::MirSource; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; @@ -152,23 +151,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 f5731255449e..add48a9600a5 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 c0d6e0cbe657..7cdcb106c8c2 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 953fc0eb733c..bb41477dcbf9 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 09b44bbe77dd345e9c0f76dea0ea55e835729ef8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:29:23 -0500 Subject: [PATCH 41/65] 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 622e8c51bee9..e2f50c8c8891 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 3a20343033c2..8862ba3545eb 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 a87d1bbb9326d28f73d4b837a9363ca53df7affb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:29:50 -0500 Subject: [PATCH 42/65] 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 21f427fa80c1..10734802a6d0 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 a9e36a5740fd..402fdeba56a7 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -33,7 +33,7 @@ mod taint; pub struct RegionConstraintCollector<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. - pub var_origins: IndexVec, + var_origins: IndexVec, data: RegionConstraintData<'tcx>, @@ -284,6 +284,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 109c9a79ed9e17d54bd4ca2a4a8b9fbdfd29f650 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 04:33:15 -0500 Subject: [PATCH 43/65] 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 f3e24e925890..a0334ed078f4 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -41,7 +41,7 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( let free_regions = &free_regions::free_regions(infcx, def_id); // 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 { @@ -64,7 +64,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); let param_env = infcx.tcx.param_env(def_id); constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, 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 add48a9600a5..f1160d42155a 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 7cdcb106c8c2..c053dab123d7 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 51ce1f9493b5aec30616b15fc34a7d96307ad9ad Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 05:21:48 -0500 Subject: [PATCH 44/65] 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 67fdd68a826c..84baece77fe5 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 10734802a6d0..2b080c54da1f 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 00863abc84de..b75163dbaa60 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 a0334ed078f4..15c74f49c116 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -64,7 +64,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); let param_env = infcx.tcx.param_env(def_id); constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, 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 f1160d42155a..24821529bade 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 c053dab123d7..a7c8deb18544 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 a3dbcefd0e0c..837c3d42fe83 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 ef392bc11b99a005da7c07f5f67e7bd75608828f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:44:41 -0500 Subject: [PATCH 45/65] 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 4f0fff589180..93fd3e7637f8 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -148,13 +148,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.) @@ -177,12 +177,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), @@ -437,12 +435,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>) { let (sd, rw) = kind; @@ -501,7 +499,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 => { @@ -522,7 +520,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 { @@ -579,7 +577,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) @@ -592,7 +590,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 = @@ -619,11 +617,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). @@ -674,7 +672,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); @@ -802,7 +800,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 { @@ -1015,11 +1013,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 { @@ -1119,11 +1117,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)] @@ -1137,21 +1135,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, @@ -1244,7 +1242,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_or_uninitialized(&mut self, _context: Context, desired_action: &str, @@ -1481,7 +1479,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(); @@ -1616,7 +1614,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 8d3219ed5e19e63599245ad895827ea764d1cb8b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 11:52:39 -0500 Subject: [PATCH 46/65] 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 93fd3e7637f8..70f4e41a9cbf 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, @@ -154,7 +153,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.) @@ -592,9 +590,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 5592bb7c3359a94d8c9dd4cb3fc6c401504361c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:29:10 -0500 Subject: [PATCH 47/65] 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 5dc7a324c2d4..e71f4fbef265 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -348,7 +348,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 12534e9159a2795109e5bc435754c0cf495fdbba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:30:06 -0500 Subject: [PATCH 48/65] 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 b75163dbaa60..b09ab8da7c12 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 a7c8deb18544..139679258441 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 32f964cc98dbd7a5a1f978fdb510bf38f7fe9a4b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:30:43 -0500 Subject: [PATCH 49/65] 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 139679258441..1076b774de65 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 d9e841e756eab869ea3dcbe468c1ad95f80ae24b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 04:31:21 -0500 Subject: [PATCH 50/65] 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 24821529bade..566a3b23ff13 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 4b743da596e94fbf56418e82ab92a4952d9bfb8b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 6 Nov 2017 07:26:34 -0500 Subject: [PATCH 51/65] 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 | 29 +++-- .../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, 256 insertions(+), 162 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 3bcdc4f7e2c6..da505f167241 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 70f4e41a9cbf..a72320cdbdd6 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -112,7 +112,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, def_id, mir)) + Some(nll::compute_regions(infcx, def_id, 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 627bc7a83245..1f905d32f84e 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::visit::Visitor; use rustc::mir::Lvalue::Projection; use rustc::mir::{LvalueProjection, ProjectionElem}; @@ -21,7 +21,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; @@ -179,29 +178,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, @@ -237,35 +213,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 101fed3cfa63..92a8a714d525 100644 --- a/src/librustc_mir/transform/nll/free_regions.rs +++ b/src/librustc_mir/transform/nll/free_regions.rs @@ -50,6 +50,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 15c74f49c116..147f061ad113 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -15,14 +15,15 @@ use rustc::ty::{self, RegionKind, RegionVid}; use rustc::util::nodemap::FxHashMap; use std::collections::BTreeSet; use transform::MirSource; +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; @@ -35,6 +36,7 @@ mod renumber; pub fn compute_regions<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, def_id: DefId, + param_env: ty::ParamEnv<'gcx>, mir: &mut Mir<'tcx>, ) -> RegionInferenceContext<'tcx> { // Compute named region information. @@ -43,6 +45,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 mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap(); + let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir); + + // 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( @@ -62,12 +74,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); - let param_env = infcx.tcx.param_env(def_id); + // Generate non-subtyping constraints. constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness); + + // Solve the region constraints. regioncx.solve(infcx, &mir); // Dump MIR results into a file, if that is enabled. This let us @@ -123,12 +133,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => for region in regioncx.regions() { - writeln!( - out, - "| {:?}: {:?}", - region, - regioncx.region_value(region) - )?; + writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?; }, // Before each basic block, dump out the values diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 566a3b23ff13..f218cc51d344 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 bb41477dcbf9..000000000000 --- 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 000000000000..c1850c76541d --- /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 000000000000..5605206221a1 --- /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 000000000000..9586f4267203 --- /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 b383ab79c96e4f3d38fcc536ac35cafcabfdd0c5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:41:57 -0500 Subject: [PATCH 52/65] 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 6c1478531f14..e7daff3e2c37 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 000000000000..a53bfec80d98 --- /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 b564faf3d0c2..67ad08c75303 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 013f88b9fb22496b8ec58b350447834edce6fc0e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:54:22 -0500 Subject: [PATCH 53/65] 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 ae2fb5e2580e..0976c5f1f2fc 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 267574c8dfce8c7e6cc910e18d280c278544df26 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:54:31 -0500 Subject: [PATCH 54/65] 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 0489a316cb3e..9314c9d051de 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 9da54c1ac83655088a6d1f1abd63a77770e5e1cd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 05:55:34 -0500 Subject: [PATCH 55/65] 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 a73b234ffbea..c68907e689a6 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 8cea0539d1cee8497bcaf48a22fe61e2aca17fa4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 06:16:09 -0500 Subject: [PATCH 56/65] 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 e3f67d817f3c..34d0482a623a 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 c3df0c840ded..f51e839e4fc3 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 f7276cb29790..ae059febc710 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 6527df26eae7..6d7aa0a26c8a 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 aedb3f562a66..2968ae7d485b 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 23809d176f69..736d2902ec63 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 cada9c7b2548..fb178b46b470 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 c7c2603e2c7440d4ef2e8ab5fc570d1dab2b0cbb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 13:18:14 -0500 Subject: [PATCH 57/65] 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 84baece77fe5..d22eb20e70a8 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 c68907e689a6..65406c3d16cc 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 5a8c1eb61ab5f82dc9a328c025aca7394db45751 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 7 Nov 2017 13:18:42 -0500 Subject: [PATCH 58/65] 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 ef09479f7513..2099e923e095 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 932cb12e81df..a17133d412c7 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 000000000000..0e5cc574f002 --- /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 f72259101f275c5e0013cea7cff34b718e240866 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 14 Nov 2017 15:09:01 -0500 Subject: [PATCH 59/65] Nit: fix wording in README --- .../infer/lexical_region_resolve/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/README.md b/src/librustc/infer/lexical_region_resolve/README.md index a53bfec80d98..a90230870a6c 100644 --- a/src/librustc/infer/lexical_region_resolve/README.md +++ b/src/librustc/infer/lexical_region_resolve/README.md @@ -9,15 +9,15 @@ Note that we use the terms region and lifetime interchangeably. 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. +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. ## The problem From 15739b820f95ea213870790e67ee04dc359192d1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 14 Nov 2017 15:12:36 -0500 Subject: [PATCH 60/65] Nit: rework region obligations to a snapshotted vector --- src/librustc/infer/mod.rs | 13 +++++++-- src/librustc/infer/outlives/obligations.rs | 32 +++++++++++++--------- src/librustc/lib.rs | 1 + src/librustc/traits/mod.rs | 3 +- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 2b080c54da1f..f734ff84f639 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -35,7 +35,7 @@ use std::fmt; use syntax::ast; use errors::DiagnosticBuilder; use syntax_pos::{self, Span, DUMMY_SP}; -use util::nodemap::{NodeMap, FxHashMap}; +use util::nodemap::FxHashMap; use arena::DroplessArena; use self::combine::CombineFields; @@ -179,7 +179,7 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // 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>>>, + region_obligations: RefCell)>>, } /// A map returned by `skolemize_late_bound_regions()` indicating the skolemized @@ -450,7 +450,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()), + region_obligations: RefCell::new(vec![]), })) } } @@ -478,6 +478,7 @@ pub struct CombinedSnapshot<'a, 'tcx:'a> { int_snapshot: unify::Snapshot, float_snapshot: unify::Snapshot, region_constraints_snapshot: RegionSnapshot, + region_obligations_snapshot: usize, was_in_snapshot: bool, _in_progress_tables: Option>>, } @@ -786,6 +787,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), region_constraints_snapshot: self.borrow_region_constraints().start_snapshot(), + region_obligations_snapshot: self.region_obligations.borrow().len(), was_in_snapshot: in_snapshot, // Borrow tables "in progress" (i.e. during typeck) // to ban writes from within a snapshot to them. @@ -802,6 +804,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_snapshot, float_snapshot, region_constraints_snapshot, + region_obligations_snapshot, was_in_snapshot, _in_progress_tables } = snapshot; @@ -819,6 +822,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.float_unification_table .borrow_mut() .rollback_to(float_snapshot); + self.region_obligations + .borrow_mut() + .truncate(region_obligations_snapshot); self.borrow_region_constraints() .rollback_to(region_constraints_snapshot); } @@ -830,6 +836,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { int_snapshot, float_snapshot, region_constraints_snapshot, + region_obligations_snapshot: _, was_in_snapshot, _in_progress_tables } = snapshot; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 2fb085bc1d86..32f09795668b 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -86,9 +86,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ) { self.region_obligations .borrow_mut() - .entry(body_id) - .or_insert(vec![]) - .push(obligation); + .push((body_id, obligation)); } /// Process the region obligations that must be proven (during @@ -131,10 +129,16 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, ) { - let region_obligations = match self.region_obligations.borrow_mut().remove(&body_id) { - None => vec![], - Some(vec) => vec, - }; + assert!(!self.in_snapshot.get(), "cannot process registered region obligations in a snapshot"); + + // pull out the region obligations with the given `body_id` (leaving the rest) + let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len()); + { + let mut r_o = self.region_obligations.borrow_mut(); + for (_, obligation) in r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id) { + my_region_obligations.push(obligation); + } + } let outlives = TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env); @@ -143,7 +147,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { sup_type, sub_region, cause, - } in region_obligations + } in my_region_obligations { let origin = SubregionOrigin::from_obligation_cause( &cause, @@ -170,11 +174,13 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { outlives.type_must_outlive(origin, ty, region); } - /// 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); + /// Ignore the region obligations, 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) { + assert!(!self.in_snapshot.get(), "cannot ignore registered region obligations in a snapshot"); + + self.region_obligations.borrow_mut().clear(); } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 66113ffef3d6..5e9019c92c5b 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -45,6 +45,7 @@ #![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(drain_filter)] #![feature(i128_type)] #![feature(match_default_bindings)] #![feature(inclusive_range_syntax)] diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 9314c9d051de..55b1a913f0d1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -511,7 +511,6 @@ 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, @@ -546,7 +545,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // 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); + let _ = infcx.ignore_region_obligations(); infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &free_regions); let predicates = match infcx.fully_resolve(&predicates) { From 11c84c6625d5e9374aa4f8a60b67c3c4a6f233b6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 14 Nov 2017 17:12:16 -0500 Subject: [PATCH 61/65] Nit: improve comment to explain why we wait until regionck --- src/librustc/infer/outlives/obligations.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 32f09795668b..6680de522e53 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -55,15 +55,19 @@ //! 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 +//! Note that by delaying the gathering of implied bounds until all +//! inference information is known, we may find relationships between +//! bound regions and other regions in the environment. For example, +//! when we first check a closure like the one expected as argument +//! to `foo`: +//! +//! ``` +//! fn foo FnMut(&'a U)>(_f: F) {} +//! ``` +//! +//! the type of the closure's first argument would be `&'a ?U`. We +//! might later infer `?U` to something like `&'b u32`, which would +//! imply that `'b: 'a`. use hir::def_id::DefId; use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; From a73d6203236fb251a9d530a5903999d204c0d449 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 14 Nov 2017 17:17:53 -0500 Subject: [PATCH 62/65] Nit: reset more state after `take_and_reset_data` --- src/librustc/infer/region_constraints/mod.rs | 54 ++++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 402fdeba56a7..096037ebe880 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -296,12 +296,54 @@ impl<'tcx> RegionConstraintCollector<'tcx> { (self.var_origins, self.data) } - /// Takes (and clears) the current set of constraints. Note that the set of - /// variables remains intact. + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. /// /// Not legal during a snapshot. pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { - mem::replace(&mut self.data, RegionConstraintData::default()) + assert!(!self.in_snapshot()); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintCollector { + var_origins, + data, + lubs, + glbs, + skolemization_count, + bound_count: _, + undo_log: _, + unification_table, + } = self; + + assert_eq!(*skolemization_count, 0); + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + *unification_table = UnificationTable::new(); + for vid in var_origins.indices() { + unification_table.new_key(unify_key::RegionVidKey { min_vid: vid }); + } + + mem::replace(data, RegionConstraintData::default()) } fn in_snapshot(&self) -> bool { @@ -904,7 +946,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; + let RegionConstraintData { + constraints, + verifys, + givens, + } = self; constraints.is_empty() && verifys.is_empty() && givens.is_empty() } } From a94d2a6b883186782c1152c9aca7742d32692930 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Nov 2017 16:37:34 -0500 Subject: [PATCH 63/65] Nit: fix typo --- src/librustc_mir/transform/nll/region_infer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index f218cc51d344..1609c1236b0c 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -92,7 +92,7 @@ impl Region { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Constraint { // NB. The ordering here is not significant for correctness, but - // it is for conenience. Before we dump the constraints in the + // it is for convenience. Before we dump the constraints in the // debugging logs, we sort them, and we'd like the "super region" // to be first, etc. (In particular, span should remain last.) From 9e29662a9855461318067ba5e4c5c4154dad01ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Nov 2017 20:44:02 -0500 Subject: [PATCH 64/65] obligations.rs: rustfmt --- src/librustc/infer/outlives/obligations.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 6680de522e53..5d6217ebc80b 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -133,7 +133,10 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, ) { - assert!(!self.in_snapshot.get(), "cannot process registered region obligations in a snapshot"); + assert!( + !self.in_snapshot.get(), + "cannot process registered region obligations in a snapshot" + ); // pull out the region obligations with the given `body_id` (leaving the rest) let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len()); @@ -182,7 +185,10 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// 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) { - assert!(!self.in_snapshot.get(), "cannot ignore registered region obligations in a snapshot"); + assert!( + !self.in_snapshot.get(), + "cannot ignore registered region obligations in a snapshot" + ); self.region_obligations.borrow_mut().clear(); } @@ -496,10 +502,7 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // 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 = - self.collect_outlives_from_predicate_list( - generic.to_ty(tcx), - self.param_env.caller_bounds); + let mut param_bounds = 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 @@ -588,7 +591,8 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); self.collect_outlives_from_predicate_list( identity_proj, - traits::elaborate_predicates(tcx, trait_predicates.predicates)) + traits::elaborate_predicates(tcx, trait_predicates.predicates), + ) } /// Searches through a predicate list for a predicate `T: 'a`. From 8c109f5681c08ed70f85926a2a18f8ed98fef77c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 15 Nov 2017 20:45:03 -0500 Subject: [PATCH 65/65] infer/outlives/obligations.rs: wrap some long lines --- src/librustc/infer/outlives/obligations.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 5d6217ebc80b..c7081e59ec36 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -502,7 +502,9 @@ impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> { // 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 = self.collect_outlives_from_predicate_list(generic.to_ty(tcx), self.param_env.caller_bounds); + let generic_ty = generic.to_ty(tcx); + let c_b = self.param_env.caller_bounds; + let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list