diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 99d7b4eaf7d68..296808cea2bd7 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -69,6 +69,10 @@ pub struct RegionConstraintCollector<'tcx> { /// would wind up with a fresh stream of region variables that /// have been equated but appear distinct. unification_table: ut::UnificationTable>, + + /// a flag set to true when we perform any unifications; this is used + /// to micro-optimize `take_and_reset_data` + any_unifications: bool, } pub type VarInfos = IndexVec; @@ -234,6 +238,7 @@ pub struct RegionVariableInfo { pub struct RegionSnapshot { length: usize, region_snapshot: ut::Snapshot>, + any_unifications: bool, } /// When working with skolemized regions, we often wish to find all of @@ -280,6 +285,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { bound_count: 0, undo_log: Vec::new(), unification_table: ut::UnificationTable::new(), + any_unifications: false, } } @@ -325,6 +331,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { bound_count: _, undo_log: _, unification_table, + any_unifications, } = self; // Clear the tables of (lubs, glbs), so that we will create @@ -338,7 +345,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // 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.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); + if *any_unifications { + unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); + *any_unifications = false; + } mem::replace(data, RegionConstraintData::default()) } @@ -358,6 +368,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { RegionSnapshot { length, region_snapshot: self.unification_table.snapshot(), + any_unifications: self.any_unifications, } } @@ -385,6 +396,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { let c = self.undo_log.pop().unwrap(); assert!(c == OpenSnapshot); self.unification_table.rollback_to(snapshot.region_snapshot); + self.any_unifications = snapshot.any_unifications; } fn rollback_undo_entry(&mut self, undo_entry: UndoLogEntry<'tcx>) { @@ -623,6 +635,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { self.unification_table.union(sub, sup); + self.any_unifications = true; } } } diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 1fb63afc72fa7..ad3710e9536f1 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -367,6 +367,11 @@ impl IndexVec { IndexVec { raw: Vec::new(), _marker: PhantomData } } + #[inline] + pub fn from_raw(raw: Vec) -> Self { + IndexVec { raw, _marker: PhantomData } + } + #[inline] pub fn with_capacity(capacity: usize) -> Self { IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData } diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs index 4f87a2b30ae84..72db9f8da9872 100644 --- a/src/librustc_mir/borrow_check/nll/constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/constraint_generation.rs @@ -11,6 +11,8 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::LocationTable; use borrow_check::nll::facts::AllFacts; +use borrow_check::nll::region_infer::{Cause, RegionInferenceContext}; +use borrow_check::nll::ToRegionVid; use rustc::hir; use rustc::infer::InferCtxt; use rustc::mir::visit::TyContext; @@ -21,9 +23,7 @@ use rustc::mir::{Local, PlaceProjection, ProjectionElem, Statement, Terminator}; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::Substs; use rustc::ty::{self, CanonicalTy, ClosureSubsts, GeneratorSubsts}; - -use super::region_infer::{Cause, RegionInferenceContext}; -use super::ToRegionVid; +use std::iter; pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( infcx: &InferCtxt<'cx, 'gcx, 'tcx>, @@ -32,6 +32,7 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( location_table: &LocationTable, mir: &Mir<'tcx>, borrow_set: &BorrowSet<'tcx>, + liveness_set_from_typeck: &[(ty::Region<'tcx>, Location, Cause)], ) { let mut cg = ConstraintGeneration { borrow_set, @@ -42,6 +43,8 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>( mir, }; + cg.add_region_liveness_constraints_from_type_check(liveness_set_from_typeck); + for (bb, data) in mir.basic_blocks().iter_enumerated() { cg.visit_basic_block_data(bb, data); } @@ -209,7 +212,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx self.add_reborrow_constraint(location, region, borrowed_place); } - _ => { } + _ => {} } self.super_rvalue(rvalue, location); @@ -225,6 +228,42 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx } impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> { + /// The MIR type checker generates region liveness constraints + /// that we also have to respect. + fn add_region_liveness_constraints_from_type_check( + &mut self, + liveness_set: &[(ty::Region<'tcx>, Location, Cause)], + ) { + debug!( + "add_region_liveness_constraints_from_type_check(liveness_set={} items)", + liveness_set.len(), + ); + + let ConstraintGeneration { + regioncx, + location_table, + all_facts, + .. + } = self; + + for (region, location, cause) in liveness_set { + debug!("generate: {:#?} is live at {:#?}", region, location); + let region_vid = regioncx.to_region_vid(region); + regioncx.add_live_point(region_vid, *location, &cause); + } + + if let Some(all_facts) = all_facts { + all_facts + .region_live_at + .extend(liveness_set.into_iter().flat_map(|(region, location, _)| { + let r = regioncx.to_region_vid(region); + let p1 = location_table.start_index(*location); + let p2 = location_table.mid_index(*location); + iter::once((r, p1)).chain(iter::once((r, p2))) + })); + } + } + /// Some variable with type `live_ty` is "regular live" at /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index c3d9dd8378d4a..dcb52a3b18a72 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -11,6 +11,7 @@ use borrow_check::borrow_set::BorrowSet; use borrow_check::location::{LocationIndex, LocationTable}; use borrow_check::nll::facts::AllFactsExt; +use borrow_check::nll::type_check::MirTypeckRegionConstraints; use dataflow::indexes::BorrowIndex; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; @@ -41,7 +42,6 @@ mod facts; mod invalidation; crate mod region_infer; mod renumber; -mod subtype_constraint_generation; crate mod type_check; mod universal_regions; @@ -91,46 +91,53 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( Option>>, Option>, ) { + let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts + || infcx.tcx.sess.opts.debugging_opts.polonius + { + Some(AllFacts::default()) + } else { + None + }; + // Run the MIR type-checker. let liveness = &LivenessResults::compute(mir); - let constraint_sets = &type_check::type_check( + let constraint_sets = type_check::type_check( infcx, param_env, mir, def_id, &universal_regions, + location_table, &liveness, + &mut all_facts, flow_inits, move_data, ); - let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts - || infcx.tcx.sess.opts.debugging_opts.polonius - { - Some(AllFacts::default()) - } else { - None - }; - if let Some(all_facts) = &mut all_facts { all_facts .universal_region .extend(universal_regions.universal_regions()); } - // Create the region inference context, taking ownership of the region inference - // data that was contained in `infcx`. + // Create the region inference context, taking ownership of the + // region inference data that was contained in `infcx`, and the + // base constraints generated by the type-check. let var_origins = infcx.take_region_var_origins(); - let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir); - - // Generate various constraints. - subtype_constraint_generation::generate( - &mut regioncx, - &mut all_facts, - location_table, + let MirTypeckRegionConstraints { + liveness_set, + outlives_constraints, + type_tests, + } = constraint_sets; + let mut regioncx = RegionInferenceContext::new( + var_origins, + universal_regions, mir, - constraint_sets, + outlives_constraints, + type_tests, ); + + // Generate various additional constraints. constraint_generation::generate_constraints( infcx, &mut regioncx, @@ -138,6 +145,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( location_table, &mir, borrow_set, + &liveness_set, ); invalidation::generate_invalidates( infcx, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs index b0346abee5a5f..6c796ea4c73ab 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs @@ -14,7 +14,7 @@ //! context internal state. use std::io::{self, Write}; -use super::{Constraint, RegionInferenceContext}; +use super::{OutlivesConstraint, RegionInferenceContext}; // Room for "'_#NNNNr" before things get misaligned. // Easy enough to fix if this ever doesn't seem like @@ -79,7 +79,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let mut constraints: Vec<_> = self.constraints.iter().collect(); constraints.sort(); for constraint in &constraints { - let Constraint { + let OutlivesConstraint { sup, sub, point, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs index 6c4c02a36a0d7..c02e4ff315687 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs @@ -27,7 +27,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> { type Node = RegionVid; - type Edge = Constraint; + type Edge = OutlivesConstraint; fn graph_id(&'this self) -> dot::Id<'this> { dot::Id::new(format!("RegionInferenceContext")).unwrap() @@ -41,31 +41,31 @@ impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> { fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> { dot::LabelText::LabelStr(format!("{:?}", n).into_cow()) } - fn edge_label(&'this self, e: &Constraint) -> dot::LabelText<'this> { + fn edge_label(&'this self, e: &OutlivesConstraint) -> dot::LabelText<'this> { dot::LabelText::LabelStr(format!("{:?}", e.point).into_cow()) } } impl<'this, 'tcx> dot::GraphWalk<'this> for RegionInferenceContext<'tcx> { type Node = RegionVid; - type Edge = Constraint; + type Edge = OutlivesConstraint; fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> { let vids: Vec = self.definitions.indices().collect(); vids.into_cow() } - fn edges(&'this self) -> dot::Edges<'this, Constraint> { + fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint> { (&self.constraints.raw[..]).into_cow() } // Render `a: b` as `a <- b`, indicating the flow // of data during inference. - fn source(&'this self, edge: &Constraint) -> RegionVid { + fn source(&'this self, edge: &OutlivesConstraint) -> RegionVid { edge.sub } - fn target(&'this self, edge: &Constraint) -> RegionVid { + fn target(&'this self, edge: &OutlivesConstraint) -> RegionVid { edge.sup } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index dea2683789bdf..0eeacda467e03 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -68,7 +68,7 @@ pub struct RegionInferenceContext<'tcx> { dependency_map: Option>>, /// The constraints we have accumulated and used during solving. - constraints: IndexVec, + constraints: IndexVec, /// Type constraints that we check after solving. type_tests: Vec>, @@ -118,19 +118,19 @@ pub(crate) enum Cause { } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Constraint { +pub struct OutlivesConstraint { // NB. The ordering here is not significant for correctness, but // it is for convenience. Before we dump the constraints in the // debugging logs, we sort them, and we'd like the "super region" // to be first, etc. (In particular, span should remain last.) /// The region SUP must outlive SUB... - sup: RegionVid, + pub sup: RegionVid, /// Region that must be outlived. - sub: RegionVid, + pub sub: RegionVid, /// At this location. - point: Location, + pub point: Location, /// Later on, we thread the constraints onto a linked list /// grouped by their `sub` field. So if you had: @@ -140,10 +140,10 @@ pub struct Constraint { /// 0 | `'a: 'b` | Some(2) /// 1 | `'b: 'c` | None /// 2 | `'c: 'b` | None - next: Option, + pub next: Option, /// Where did this constraint arise? - span: Span, + pub span: Span, } newtype_index!(ConstraintIndex { DEBUG_FORMAT = "ConstraintIndex({})" }); @@ -239,11 +239,19 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `num_region_variables` valid inference variables; the first N /// of those will be constant regions representing the free /// regions defined in `universal_regions`. + /// + /// The `outlives_constraints` and `type_tests` are an initial set + /// of constraints produced by the MIR type check. pub(crate) fn new( var_infos: VarInfos, universal_regions: UniversalRegions<'tcx>, mir: &Mir<'tcx>, + outlives_constraints: Vec, + type_tests: Vec>, ) -> Self { + // The `next` field should not yet have been initialized: + debug_assert!(outlives_constraints.iter().all(|c| c.next.is_none())); + let num_region_variables = var_infos.len(); let num_universal_regions = universal_regions.len(); @@ -261,8 +269,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { liveness_constraints: RegionValues::new(elements, num_region_variables), inferred_values: None, dependency_map: None, - constraints: IndexVec::new(), - type_tests: Vec::new(), + constraints: IndexVec::from_raw(outlives_constraints), + type_tests, universal_regions, }; @@ -345,7 +353,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { where R: ToRegionVid, { - let inferred_values = self.inferred_values + let inferred_values = self + .inferred_values .as_ref() .expect("region values not yet inferred"); inferred_values.contains(r.to_region_vid(), p) @@ -353,7 +362,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Returns access to the value of `r` for debugging purposes. crate fn region_value_str(&self, r: RegionVid) -> String { - let inferred_values = self.inferred_values + let inferred_values = self + .inferred_values .as_ref() .expect("region values not yet inferred"); @@ -387,7 +397,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); assert!(self.inferred_values.is_none(), "values already inferred"); - self.constraints.push(Constraint { + self.constraints.push(OutlivesConstraint { span, sup, sub, @@ -396,11 +406,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); } - /// Add a "type test" that must be satisfied. - pub(super) fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { - self.type_tests.push(type_test); - } - /// Perform region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -465,7 +470,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } - #[inline(never)] // ensure dfs is identifiable in profiles fn compute_region_values(&self, _mir: &Mir<'tcx>) -> RegionValues { debug!("compute_region_values()"); debug!("compute_region_values: constraints={:#?}", { @@ -516,7 +520,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// indices of constraints that need to be re-evaluated when X changes. /// These are constraints like Y: X @ P -- so if X changed, we may /// need to grow Y. - #[inline(never)] // ensure dfs is identifiable in profiles fn build_dependency_map(&mut self) -> IndexVec> { let mut map = IndexVec::from_elem(None, &self.definitions); @@ -595,7 +598,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { if self.universal_regions.is_universal_region(r) { return self.definitions[r].external_name; } else { - let inferred_values = self.inferred_values + let inferred_values = self + .inferred_values .as_ref() .expect("region values not yet inferred"); let upper_bound = self.universal_upper_bound(r); @@ -634,8 +638,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // region, which ensures it can be encoded in a `ClosureOutlivesRequirement`. let lower_bound_plus = self.non_local_universal_upper_bound(*lower_bound); assert!(self.universal_regions.is_universal_region(lower_bound_plus)); - assert!(!self.universal_regions - .is_local_free_region(lower_bound_plus)); + assert!( + !self + .universal_regions + .is_local_free_region(lower_bound_plus) + ); propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject, @@ -663,7 +670,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> Option> { let tcx = infcx.tcx; let gcx = tcx.global_tcx(); - let inferred_values = self.inferred_values + let inferred_values = self + .inferred_values .as_ref() .expect("region values not yet inferred"); @@ -844,7 +852,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { sup_region, sub_region, point ); - let inferred_values = self.inferred_values + let inferred_values = self + .inferred_values .as_ref() .expect("values for regions not yet inferred"); @@ -911,7 +920,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { // The universal regions are always found in a prefix of the // full list. - let universal_definitions = self.definitions + let universal_definitions = self + .definitions .iter_enumerated() .take_while(|(_, fr_definition)| fr_definition.is_universal); @@ -1139,7 +1149,7 @@ impl<'tcx> RegionDefinition<'tcx> { } } -impl fmt::Debug for Constraint { +impl fmt::Debug for OutlivesConstraint { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs deleted file mode 100644 index 9db19085a3965..0000000000000 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ /dev/null @@ -1,199 +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 borrow_check::location::LocationTable; -use borrow_check::nll::facts::AllFacts; -use rustc::infer::region_constraints::Constraint; -use rustc::infer::region_constraints::RegionConstraintData; -use rustc::infer::region_constraints::{Verify, VerifyBound}; -use rustc::mir::{Location, Mir}; -use rustc::ty; -use std::iter; -use syntax::codemap::Span; - -use super::region_infer::{RegionInferenceContext, RegionTest, TypeTest}; -use super::type_check::Locations; -use super::type_check::MirTypeckRegionConstraints; -use super::type_check::OutlivesSet; - -/// 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>, - all_facts: &mut Option, - location_table: &LocationTable, - mir: &Mir<'tcx>, - constraints: &MirTypeckRegionConstraints<'tcx>, -) { - SubtypeConstraintGenerator { - regioncx, - location_table, - mir, - }.generate(constraints, all_facts); -} - -struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> { - regioncx: &'cx mut RegionInferenceContext<'tcx>, - location_table: &'cx LocationTable, - mir: &'cx Mir<'tcx>, -} - -impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> { - fn generate( - &mut self, - constraints: &MirTypeckRegionConstraints<'tcx>, - all_facts: &mut Option, - ) { - let MirTypeckRegionConstraints { - liveness_set, - outlives_sets, - } = constraints; - - debug!( - "generate(liveness_set={} items, outlives_sets={} items)", - liveness_set.len(), - outlives_sets.len() - ); - - for (region, location, cause) 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, &cause); - } - - if let Some(all_facts) = all_facts { - all_facts - .region_live_at - .extend(liveness_set.into_iter().flat_map(|(region, location, _)| { - let r = self.to_region_vid(region); - let p1 = self.location_table.start_index(*location); - let p2 = self.location_table.mid_index(*location); - iter::once((r, p1)).chain(iter::once((r, p2))) - })); - } - - for OutlivesSet { locations, data } in outlives_sets { - debug!("generate: constraints at: {:#?}", locations); - let RegionConstraintData { - constraints, - verifys, - givens, - } = data; - - let span = self.mir - .source_info(locations.from_location().unwrap_or(Location::START)) - .span; - - let at_location = locations.at_location().unwrap_or(Location::START); - - 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 `<=`. - self.regioncx.add_outlives(span, b_vid, a_vid, at_location); - - // In the new analysis, all outlives relations etc - // "take effect" at the mid point of the statement - // that requires them, so ignore the `at_location`. - if let Some(all_facts) = all_facts { - if let Some(from_location) = locations.from_location() { - all_facts.outlives.push(( - b_vid, - a_vid, - self.location_table.mid_index(from_location), - )); - } else { - for location in self.location_table.all_points() { - all_facts.outlives.push((b_vid, a_vid, location)); - } - } - } - } - - for verify in verifys { - let type_test = self.verify_to_type_test(verify, span, locations); - self.regioncx.add_type_test(type_test); - } - - assert!( - givens.is_empty(), - "MIR type-checker does not use givens (thank goodness)" - ); - } - } - - fn verify_to_type_test( - &self, - verify: &Verify<'tcx>, - span: Span, - locations: &Locations, - ) -> TypeTest<'tcx> { - let generic_kind = verify.kind; - - let lower_bound = self.to_region_vid(verify.region); - - let point = locations.at_location().unwrap_or(Location::START); - - let test = self.verify_bound_to_region_test(&verify.bound); - - TypeTest { - generic_kind, - lower_bound, - point, - span, - test, - } - } - - fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { - match verify_bound { - VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( - regions.iter().map(|r| self.to_region_vid(r)).collect(), - ), - - VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn( - regions.iter().map(|r| self.to_region_vid(r)).collect(), - ), - - VerifyBound::AnyBound(bounds) => RegionTest::Any( - bounds - .iter() - .map(|b| self.verify_bound_to_region_test(b)) - .collect(), - ), - - VerifyBound::AllBounds(bounds) => RegionTest::All( - bounds - .iter() - .map(|b| self.verify_bound_to_region_test(b)) - .collect(), - ), - } - } - - fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { - self.regioncx.to_region_vid(r) - } -} diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs new file mode 100644 index 0000000000000..06aaf6810faa3 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -0,0 +1,190 @@ +// 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 borrow_check::location::LocationTable; +use borrow_check::nll::facts::AllFacts; +use borrow_check::nll::region_infer::{OutlivesConstraint, RegionTest, TypeTest}; +use borrow_check::nll::type_check::Locations; +use borrow_check::nll::universal_regions::UniversalRegions; +use rustc::infer::region_constraints::Constraint; +use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::region_constraints::{Verify, VerifyBound}; +use rustc::mir::{Location, Mir}; +use rustc::ty; +use syntax::codemap::Span; + +crate struct ConstraintConversion<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + universal_regions: &'a UniversalRegions<'tcx>, + location_table: &'a LocationTable, + outlives_constraints: &'a mut Vec, + type_tests: &'a mut Vec>, + all_facts: &'a mut Option, + +} + +impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { + crate fn new( + mir: &'a Mir<'tcx>, + universal_regions: &'a UniversalRegions<'tcx>, + location_table: &'a LocationTable, + outlives_constraints: &'a mut Vec, + type_tests: &'a mut Vec>, + all_facts: &'a mut Option, + ) -> Self { + Self { + mir, + universal_regions, + location_table, + outlives_constraints, + type_tests, + all_facts, + } + } + + crate fn convert( + &mut self, + locations: Locations, + data: &RegionConstraintData<'tcx>, + ) { + debug!("generate: constraints at: {:#?}", locations); + let RegionConstraintData { + constraints, + verifys, + givens, + } = data; + + let span = self + .mir + .source_info(locations.from_location().unwrap_or(Location::START)) + .span; + + let at_location = locations.at_location().unwrap_or(Location::START); + + 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 `<=`. + self.add_outlives(span, b_vid, a_vid, at_location); + + // In the new analysis, all outlives relations etc + // "take effect" at the mid point of the statement + // that requires them, so ignore the `at_location`. + if let Some(all_facts) = &mut self.all_facts { + if let Some(from_location) = locations.from_location() { + all_facts.outlives.push(( + b_vid, + a_vid, + self.location_table.mid_index(from_location), + )); + } else { + for location in self.location_table.all_points() { + all_facts.outlives.push((b_vid, a_vid, location)); + } + } + } + } + + for verify in verifys { + let type_test = self.verify_to_type_test(verify, span, locations); + self.add_type_test(type_test); + } + + assert!( + givens.is_empty(), + "MIR type-checker does not use givens (thank goodness)" + ); + } + + fn verify_to_type_test( + &self, + verify: &Verify<'tcx>, + span: Span, + locations: Locations, + ) -> TypeTest<'tcx> { + let generic_kind = verify.kind; + + let lower_bound = self.to_region_vid(verify.region); + + let point = locations.at_location().unwrap_or(Location::START); + + let test = self.verify_bound_to_region_test(&verify.bound); + + TypeTest { + generic_kind, + lower_bound, + point, + span, + test, + } + } + + fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { + match verify_bound { + VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn( + regions.iter().map(|r| self.to_region_vid(r)).collect(), + ), + + VerifyBound::AnyBound(bounds) => RegionTest::Any( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + + VerifyBound::AllBounds(bounds) => RegionTest::All( + bounds + .iter() + .map(|b| self.verify_bound_to_region_test(b)) + .collect(), + ), + } + } + + fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid { + self.universal_regions.to_region_vid(r) + } + + fn add_outlives( + &mut self, + span: Span, + sup: ty::RegionVid, + sub: ty::RegionVid, + point: Location, + ) { + self.outlives_constraints.push(OutlivesConstraint { + span, + sub, + sup, + point, + next: None, + }); + } + + fn add_type_test(&mut self, type_test: TypeTest<'tcx>) { + self.type_tests.push(type_test); + } +} diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs index 2b1878c33e90b..d44eed65201cd 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs @@ -21,11 +21,11 @@ use borrow_check::nll::renumber; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::hir::def_id::DefId; use rustc::infer::InferOk; -use rustc::ty::Ty; -use rustc::ty::subst::Subst; -use rustc::mir::*; use rustc::mir::visit::TyContext; -use rustc::traits::PredicateObligations; +use rustc::mir::*; +use rustc::traits::{ObligationCause, PredicateObligations}; +use rustc::ty::subst::Subst; +use rustc::ty::Ty; use rustc_data_structures::indexed_vec::Idx; @@ -56,8 +56,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } assert!( - mir.yield_ty.is_some() && universal_regions.yield_ty.is_some() || - mir.yield_ty.is_none() && universal_regions.yield_ty.is_none() + mir.yield_ty.is_some() && universal_regions.yield_ty.is_some() + || mir.yield_ty.is_none() && universal_regions.yield_ty.is_none() ); if let Some(mir_yield_ty) = mir.yield_ty { let ur_yield_ty = universal_regions.yield_ty.unwrap(); @@ -76,57 +76,67 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { output_ty ); let mir_output_ty = mir.local_decls[RETURN_PLACE].ty; - let anon_type_map = self.fully_perform_op(Locations::All, |cx| { - let mut obligations = ObligationAccumulator::default(); - - let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types( - mir_def_id, - cx.body_id, - cx.param_env, - &output_ty, - )); - debug!( - "equate_inputs_and_outputs: instantiated output_ty={:?}", - output_ty - ); - debug!( - "equate_inputs_and_outputs: anon_type_map={:#?}", - anon_type_map - ); - - debug!( - "equate_inputs_and_outputs: mir_output_ty={:?}", - mir_output_ty - ); - obligations.add(infcx - .at(&cx.misc(cx.last_span), cx.param_env) - .eq(output_ty, mir_output_ty)?); - - for (&anon_def_id, anon_decl) in &anon_type_map { - let anon_defn_ty = tcx.type_of(anon_def_id); - let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); - let anon_defn_ty = renumber::renumber_regions( - cx.infcx, - TyContext::Location(Location::START), - &anon_defn_ty, - ); - debug!( - "equate_inputs_and_outputs: concrete_ty={:?}", - anon_decl.concrete_ty - ); - debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); - obligations.add(infcx - .at(&cx.misc(cx.last_span), cx.param_env) - .eq(anon_decl.concrete_ty, anon_defn_ty)?); - } - - debug!("equate_inputs_and_outputs: equated"); - - Ok(InferOk { - value: Some(anon_type_map), - obligations: obligations.into_vec(), - }) - }).unwrap_or_else(|terr| { + let anon_type_map = + self.fully_perform_op( + Locations::All, + || format!("input_output"), + |cx| { + let mut obligations = ObligationAccumulator::default(); + + let dummy_body_id = ObligationCause::dummy().body_id; + let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types( + mir_def_id, + dummy_body_id, + cx.param_env, + &output_ty, + )); + debug!( + "equate_inputs_and_outputs: instantiated output_ty={:?}", + output_ty + ); + debug!( + "equate_inputs_and_outputs: anon_type_map={:#?}", + anon_type_map + ); + + debug!( + "equate_inputs_and_outputs: mir_output_ty={:?}", + mir_output_ty + ); + obligations.add( + infcx + .at(&ObligationCause::dummy(), cx.param_env) + .eq(output_ty, mir_output_ty)?, + ); + + for (&anon_def_id, anon_decl) in &anon_type_map { + let anon_defn_ty = tcx.type_of(anon_def_id); + let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs); + let anon_defn_ty = renumber::renumber_regions( + cx.infcx, + TyContext::Location(Location::START), + &anon_defn_ty, + ); + debug!( + "equate_inputs_and_outputs: concrete_ty={:?}", + anon_decl.concrete_ty + ); + debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty); + obligations.add( + infcx + .at(&ObligationCause::dummy(), cx.param_env) + .eq(anon_decl.concrete_ty, anon_defn_ty)?, + ); + } + + debug!("equate_inputs_and_outputs: equated"); + + Ok(InferOk { + value: Some(anon_type_map), + obligations: obligations.into_vec(), + }) + }, + ).unwrap_or_else(|terr| { span_mirbug!( self, Location::START, @@ -143,13 +153,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // prove that `T: Iterator` where `T` is the type we // instantiated it with). if let Some(anon_type_map) = anon_type_map { - self.fully_perform_op(Locations::All, |_cx| { - infcx.constrain_anon_types(&anon_type_map, universal_regions); - Ok(InferOk { - value: (), - obligations: vec![], - }) - }).unwrap(); + self.fully_perform_op( + Locations::All, + || format!("anon_type_map"), + |_cx| { + infcx.constrain_anon_types(&anon_type_map, universal_regions); + Ok(InferOk { + value: (), + obligations: vec![], + }) + }, + ).unwrap(); } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs index 76320c6a2eacc..80f5fe4184f9d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness.rs @@ -8,15 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use dataflow::{FlowAtLocation, FlowsAtLocation}; use borrow_check::nll::region_infer::Cause; -use dataflow::MaybeInitializedPlaces; +use borrow_check::nll::type_check::AtLocation; use dataflow::move_paths::{HasMoveData, MoveData}; -use rustc::mir::{BasicBlock, Location, Mir}; +use dataflow::MaybeInitializedPlaces; +use dataflow::{FlowAtLocation, FlowsAtLocation}; +use rustc::infer::region_constraints::RegionConstraintData; use rustc::mir::Local; -use rustc::ty::{Ty, TyCtxt, TypeFoldable}; -use rustc::infer::InferOk; -use borrow_check::nll::type_check::AtLocation; +use rustc::mir::{BasicBlock, Location, Mir}; +use rustc::traits::ObligationCause; +use rustc::ty::subst::Kind; +use rustc::ty::{Ty, TypeFoldable}; +use rustc_data_structures::fx::FxHashMap; +use std::rc::Rc; use util::liveness::LivenessResults; use super::TypeChecker; @@ -36,14 +40,13 @@ pub(super) fn generate<'gcx, 'tcx>( flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) { - let tcx = cx.tcx(); let mut generator = TypeLivenessGenerator { cx, - tcx, mir, liveness, flow_inits, move_data, + drop_data: FxHashMap(), }; for bb in mir.basic_blocks().indices() { @@ -59,11 +62,16 @@ where 'gcx: 'tcx, { cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>, - tcx: TyCtxt<'typeck, 'gcx, 'tcx>, mir: &'gen Mir<'tcx>, liveness: &'gen LivenessResults, flow_inits: &'gen mut FlowAtLocation>, move_data: &'gen MoveData<'tcx>, + drop_data: FxHashMap, DropData<'tcx>>, +} + +struct DropData<'tcx> { + dropped_kinds: Vec>, + region_constraint_data: Option>>, } impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> { @@ -80,7 +88,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo for live_local in live_locals.iter() { let live_local_ty = self.mir.local_decls[live_local].ty; let cause = Cause::LiveVar(live_local, location); - self.push_type_live_constraint(live_local_ty, location, cause); + Self::push_type_live_constraint(&mut self.cx, live_local_ty, location, cause); } }); @@ -148,8 +156,12 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo /// `location` -- i.e., it may be used later. This means that all /// regions appearing in the type `live_ty` must be live at /// `location`. - fn push_type_live_constraint(&mut self, value: T, location: Location, cause: Cause) - where + fn push_type_live_constraint( + cx: &mut TypeChecker<'_, 'gcx, 'tcx>, + value: T, + location: Location, + cause: Cause, + ) where T: TypeFoldable<'tcx>, { debug!( @@ -157,8 +169,8 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo value, location ); - self.tcx.for_each_free_region(&value, |live_region| { - self.cx + cx.tcx().for_each_free_region(&value, |live_region| { + cx .constraints .liveness_set .push((live_region, location, cause.clone())); @@ -181,47 +193,44 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo dropped_local, dropped_ty, location ); - // If we end visiting the same type twice (usually due to a cycle involving - // associated types), we need to ensure that its region types match up with the type - // we added to the 'known' map the first time around. For this reason, we need - // our infcx to hold onto its calculated region constraints after each call - // to dtorck_constraint_for_ty. Otherwise, normalizing the corresponding associated - // type will end up instantiating the type with a new set of inference variables - // Since this new type will never be in 'known', we end up looping forever. - // - // For this reason, we avoid calling TypeChecker.normalize, instead doing all normalization - // ourselves in one large 'fully_perform_op' callback. - let kind_constraints = self.cx - .fully_perform_op(location.at_self(), |cx| { - let span = cx.last_span; - - let mut final_obligations = Vec::new(); - let mut kind_constraints = Vec::new(); - - let InferOk { - value: kinds, - obligations, - } = cx.infcx - .at(&cx.misc(span), cx.param_env) - .dropck_outlives(dropped_ty); - for kind in kinds { - // All things in the `outlives` array may be touched by - // the destructor and must be live at this point. - let cause = Cause::DropVar(dropped_local, location); - kind_constraints.push((kind, location, cause)); - } + let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ + let cx = &mut self.cx; + move || Self::compute_drop_data(cx, dropped_ty) + }); - final_obligations.extend(obligations); + if let Some(data) = &drop_data.region_constraint_data { + self.cx + .push_region_constraints(location.at_self(), data.clone()); + } - Ok(InferOk { - value: kind_constraints, - obligations: final_obligations, - }) - }) - .unwrap(); + // All things in the `outlives` array may be touched by + // the destructor and must be live at this point. + let cause = Cause::DropVar(dropped_local, location); + for &kind in &drop_data.dropped_kinds { + Self::push_type_live_constraint(&mut self.cx, kind, location, cause); + } + } - for (kind, location, cause) in kind_constraints { - self.push_type_live_constraint(kind, location, cause); + fn compute_drop_data( + cx: &mut TypeChecker<'_, 'gcx, 'tcx>, + dropped_ty: Ty<'tcx>, + ) -> DropData<'tcx> { + debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,); + + let (dropped_kinds, region_constraint_data) = + cx.fully_perform_op_and_get_region_constraint_data( + || format!("compute_drop_data(dropped_ty={:?})", dropped_ty), + |cx| { + Ok(cx + .infcx + .at(&ObligationCause::dummy(), cx.param_env) + .dropck_outlives(dropped_ty)) + }, + ).unwrap(); + + DropData { + dropped_kinds, + region_constraint_data, } } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 04f5024b76946..d25cec7979140 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -11,8 +11,10 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] +use borrow_check::location::LocationTable; +use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::Cause; -use borrow_check::nll::region_infer::ClosureRegionRequirementsExt; +use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, OutlivesConstraint, TypeTest}; use borrow_check::nll::universal_regions::UniversalRegions; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; @@ -20,17 +22,17 @@ use dataflow::MaybeInitializedPlaces; use rustc::hir::def_id::DefId; use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; +use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; -use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::*; use rustc::traits::query::NoSolution; -use rustc::traits::{self, Normalized, TraitEngine}; +use rustc::traits::{self, ObligationCause, Normalized, TraitEngine}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants}; use std::fmt; -use syntax::ast; +use std::rc::Rc; use syntax_pos::{Span, DUMMY_SP}; use transform::{MirPass, MirSource}; use util::liveness::LivenessResults; @@ -45,7 +47,7 @@ macro_rules! span_mirbug { $context.last_span, &format!( "broken MIR in {:?} ({:?}): {}", - $context.body_id, + $context.mir_def_id, $elem, format_args!($($message)*), ), @@ -62,6 +64,7 @@ macro_rules! span_mirbug_and_err { }) } +mod constraint_conversion; mod input_output; mod liveness; @@ -100,19 +103,25 @@ pub(crate) fn type_check<'gcx, 'tcx>( mir: &Mir<'tcx>, mir_def_id: DefId, universal_regions: &UniversalRegions<'tcx>, + location_table: &LocationTable, liveness: &LivenessResults, + all_facts: &mut Option, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>, ) -> MirTypeckRegionConstraints<'tcx> { - let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap(); let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); type_check_internal( infcx, - body_id, + mir_def_id, param_env, mir, &universal_regions.region_bound_pairs, Some(implicit_region_bound), + Some(BorrowCheckContext { + universal_regions, + location_table, + all_facts, + }), &mut |cx| { liveness::generate(cx, mir, liveness, flow_inits, move_data); @@ -123,19 +132,22 @@ pub(crate) fn type_check<'gcx, 'tcx>( fn type_check_internal<'gcx, 'tcx>( infcx: &InferCtxt<'_, 'gcx, 'tcx>, - body_id: ast::NodeId, + mir_def_id: DefId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, + borrowck_context: Option>, extra: &mut dyn FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { let mut checker = TypeChecker::new( infcx, - body_id, + mir_def_id, param_env, region_bound_pairs, implicit_region_bound, + borrowck_context, + mir, ); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); @@ -173,7 +185,7 @@ 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, + mir_def_id: DefId, errors_reported: bool, } @@ -221,7 +233,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { TypeVerifier { mir, - body_id: cx.body_id, + mir_def_id: cx.mir_def_id, cx, last_span: mir.span, errors_reported: false, @@ -300,7 +312,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { debug!("sanitize_constant: expected_ty={:?}", expected_ty); - if let Err(terr) = self.cx + if let Err(terr) = self + .cx .eq_types(expected_ty, constant.ty, location.at_self()) { span_mirbug!( @@ -580,17 +593,25 @@ 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, + mir_def_id: DefId, region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, constraints: MirTypeckRegionConstraints<'tcx>, + borrowck_context: Option>, + mir: &'a Mir<'tcx>, +} + +struct BorrowCheckContext<'a, 'tcx: 'a> { + universal_regions: &'a UniversalRegions<'tcx>, + location_table: &'a LocationTable, + all_facts: &'a mut Option, } /// A collection of region constraints that must be satisfied for the /// program to be considered well-typed. #[derive(Default)] -pub(crate) struct MirTypeckRegionConstraints<'tcx> { +crate 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 @@ -598,24 +619,11 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { /// 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, Cause)>, - - /// 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>, -} + crate liveness_set: Vec<(ty::Region<'tcx>, Location, Cause)>, -/// 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, + crate outlives_constraints: Vec, - /// 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>, + crate type_tests: Vec>, } /// The `Locations` type summarizes *where* region constraints are @@ -667,7 +675,7 @@ pub enum Locations { /// NLL RFC, when you have a constraint `R1: R2 @ P`, this field /// is the `P` value. at_location: Location, - } + }, } impl Locations { @@ -689,37 +697,99 @@ impl Locations { impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn new( infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, - body_id: ast::NodeId, + mir_def_id: DefId, param_env: ty::ParamEnv<'gcx>, region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, + borrowck_context: Option>, + mir: &'a Mir<'tcx>, ) -> Self { TypeChecker { infcx, last_span: DUMMY_SP, - body_id, + mir_def_id, param_env, region_bound_pairs, implicit_region_bound, + borrowck_context, + mir, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), } } - fn misc(&self, span: Span) -> traits::ObligationCause<'tcx> { - traits::ObligationCause::misc(span, self.body_id) + /// Given some operation `op` that manipulates types, proves + /// predicates, or otherwise uses the inference context, executes + /// `op` and then executes all the further obligations that `op` + /// returns. This will yield a set of outlives constraints amongst + /// regions which are extracted and stored as having occured at + /// `locations`. + /// + /// **Any `rustc::infer` operations that might generate region + /// constraints should occur within this method so that those + /// constraints can be properly localized!** + fn fully_perform_op( + &mut self, + locations: Locations, + describe_op: impl Fn() -> String, + op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>, + ) -> Result> { + let (r, opt_data) = self.fully_perform_op_and_get_region_constraint_data( + || format!("{} at {:?}", describe_op(), locations), + op, + )?; + + if let Some(data) = opt_data { + self.push_region_constraints(locations, data); + } + + Ok(r) } - fn fully_perform_op( + fn push_region_constraints( &mut self, locations: Locations, - op: OP, - ) -> Result> - where - OP: FnOnce(&mut Self) -> InferResult<'tcx, R>, - { + data: Rc>, + ) { + debug!( + "push_region_constraints: constraints generated at {:?} are {:#?}", + locations, data + ); + + if let Some(borrowck_context) = &mut self.borrowck_context { + constraint_conversion::ConstraintConversion::new( + self.mir, + borrowck_context.universal_regions, + borrowck_context.location_table, + &mut self.constraints.outlives_constraints, + &mut self.constraints.type_tests, + &mut borrowck_context.all_facts, + ).convert(locations, &data); + } + } + + /// Helper for `fully_perform_op`, but also used on its own + /// sometimes to enable better caching: executes `op` fully (along + /// with resulting obligations) and returns the full set of region + /// obligations. If the same `op` were to be performed at some + /// other location, then the same set of region obligations would + /// be generated there, so this can be useful for caching. + fn fully_perform_op_and_get_region_constraint_data( + &mut self, + describe_op: impl Fn() -> String, + op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>, + ) -> Result<(R, Option>>), TypeError<'tcx>> { + if cfg!(debug_assertions) { + info!( + "fully_perform_op_and_get_region_constraint_data({})", + describe_op(), + ); + } + let mut fulfill_cx = TraitEngine::new(self.infcx.tcx); + let dummy_body_id = ObligationCause::dummy().body_id; let InferOk { value, obligations } = self.infcx.commit_if_ok(|_| op(self))?; + debug_assert!(obligations.iter().all(|o| o.cause.body_id == dummy_body_id)); 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); @@ -729,21 +799,15 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.region_bound_pairs, self.implicit_region_bound, self.param_env, - self.body_id, + dummy_body_id, ); let data = self.infcx.take_and_reset_region_constraints(); - if !data.is_empty() { - debug!( - "fully_perform_op: constraints generated at {:?} are {:#?}", - locations, data - ); - self.constraints - .outlives_sets - .push(OutlivesSet { locations, data }); + if data.is_empty() { + Ok((value, None)) + } else { + Ok((value, Some(Rc::new(data)))) } - - Ok(value) } fn sub_types( @@ -752,19 +816,37 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, '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) - }) + // Micro-optimization. + if sub == sup { + return Ok(()); + } + + self.fully_perform_op( + locations, + || format!("sub_types({:?} <: {:?})", sub, sup), + |this| { + this.infcx + .at(&ObligationCause::dummy(), this.param_env) + .sup(sup, sub) + }, + ) } fn eq_types(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, locations: Locations) -> UnitResult<'tcx> { - self.fully_perform_op(locations, |this| { - this.infcx - .at(&this.misc(this.last_span), this.param_env) - .eq(b, a) - }) + // Micro-optimization. + if a == b { + return Ok(()); + } + + self.fully_perform_op( + locations, + || format!("eq_types({:?} = {:?})", a, b), + |this| { + this.infcx + .at(&ObligationCause::dummy(), this.param_env) + .eq(b, a) + }, + ) } fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { @@ -819,7 +901,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } StatementKind::UserAssertTy(ref c_ty, ref local) => { let local_ty = mir.local_decls()[*local].ty; - let (ty, _) = self.infcx + let (ty, _) = self + .infcx .instantiate_canonical_with_fresh_inference_vars(stmt.source_info.span, c_ty); debug!( "check_stmt: user_assert_ty ty={:?} local_ty={:?}", @@ -1431,9 +1514,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } }; let operand_ty = operand.ty(mir, tcx); - if let Err(terr) = - self.sub_types(operand_ty, field_ty, location.at_self()) - { + if let Err(terr) = self.sub_types(operand_ty, field_ty, location.at_self()) { span_mirbug!( self, rvalue, @@ -1487,9 +1568,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id).closure_requirements { + let dummy_body_id = ObligationCause::dummy().body_id; closure_region_requirements.apply_requirements( self.infcx, - self.body_id, + dummy_body_id, location, *def_id, *substs, @@ -1522,27 +1604,44 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn prove_predicates(&mut self, predicates: T, location: Location) where - T: IntoIterator>, - T::IntoIter: Clone, + T: IntoIterator> + Clone, { - let predicates = predicates.into_iter(); + let cause = ObligationCause::dummy(); + let obligations: Vec<_> = predicates + .into_iter() + .map(|p| traits::Obligation::new(cause.clone(), self.param_env, p)) + .collect(); + + // Micro-optimization + if obligations.is_empty() { + return; + } + + // This intermediate vector is mildly unfortunate, in that we + // sometimes create it even when logging is disabled, but only + // if debug-info is enabled, and I doubt it is actually + // expensive. -nmatsakis + let predicates_vec: Vec<_> = if cfg!(debug_assertions) { + obligations.iter().map(|o| o.predicate).collect() + } else { + Vec::new() + }; debug!( "prove_predicates(predicates={:?}, location={:?})", - predicates.clone().collect::>(), - location, + predicates_vec, location, ); - self.fully_perform_op(location.at_self(), |this| { - let cause = this.misc(this.last_span); - let obligations = predicates - .into_iter() - .map(|p| traits::Obligation::new(cause.clone(), this.param_env, p)) - .collect(); - Ok(InferOk { - value: (), - obligations, - }) - }).unwrap() + + self.fully_perform_op( + location.at_self(), + || format!("prove_predicates({:?})", predicates_vec), + |_this| { + Ok(InferOk { + value: (), + obligations, + }) + }, + ).unwrap() } fn typeck_mir(&mut self, mir: &Mir<'tcx>) { @@ -1575,21 +1674,31 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { where T: fmt::Debug + TypeFoldable<'tcx>, { + // Micro-optimization: avoid work when we don't have to + if !value.has_projections() { + return value.clone(); + } + debug!("normalize(value={:?}, location={:?})", value, location); - self.fully_perform_op(location.to_locations(), |this| { - let Normalized { value, obligations } = this.infcx - .at(&this.misc(this.last_span), this.param_env) - .normalize(value) - .unwrap_or_else(|NoSolution| { - span_bug!( - this.last_span, - "normalization of `{:?}` failed at {:?}", - value, - location, - ); - }); - Ok(InferOk { value, obligations }) - }).unwrap() + self.fully_perform_op( + location.to_locations(), + || format!("normalize(value={:?})", value), + |this| { + let Normalized { value, obligations } = this + .infcx + .at(&ObligationCause::dummy(), this.param_env) + .normalize(value) + .unwrap_or_else(|NoSolution| { + span_bug!( + this.last_span, + "normalization of `{:?}` failed at {:?}", + value, + location, + ); + }); + Ok(InferOk { value, obligations }) + }, + ).unwrap() } } @@ -1598,7 +1707,6 @@ pub struct TypeckMir; impl MirPass for TypeckMir { 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); // When NLL is enabled, the borrow checker runs the typeck @@ -1614,7 +1722,16 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _ = type_check_internal(&infcx, id, param_env, mir, &[], None, &mut |_| ()); + let _ = type_check_internal( + &infcx, + def_id, + param_env, + mir, + &[], + None, + None, + &mut |_| (), + ); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 4081ec65f8030..5dd1889ef95bc 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -24,16 +24,15 @@ LL | | }); = note: where '_#1r: '_#0r error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_no_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic` - --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:47 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:5 | -LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - | _______________________________________________^ +LL | / establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { LL | | //~^ ERROR does not outlive free region LL | | LL | | // Only works if 'x: 'y: LL | | demand_y(x, y, x.get()) //~ WARNING not reporting region error due to nll LL | | }); - | |_____^ + | |______^ note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1 diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 7a745bb09642f..e44df9c7af1c2 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -24,16 +24,15 @@ LL | | }); = note: where '_#1r: '_#0r error: free region `ReFree(DefId(0/0:6 ~ propagate_approximated_shorter_to_static_wrong_bound[317d]::supply[0]), BrNamed(crate0:DefIndex(1:16), 'a))` does not outlive free region `ReStatic` - --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:47 + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:5 | -LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | _______________________________________________^ +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { LL | | //~^ ERROR does not outlive free region LL | | // Only works if 'x: 'y: LL | | demand_y(x, y, x.get()) LL | | //~^ WARNING not reporting region error due to nll LL | | }); - | |_____^ + | |______^ note: No external requirements --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:47:1