From 6a5a4874a07767eb2f21b9fa2a3a7f71daaeb130 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Jun 2018 11:06:38 -0400 Subject: [PATCH] convert type-check constraints into NLL constraints on the fly We used to accumulate a vector of type-check constraints, but now we generate them as we go, and just store the NLL-style `OutlivesConstraint` and `TypeTest` information. --- src/librustc_data_structures/indexed_vec.rs | 5 + .../borrow_check/nll/constraint_generation.rs | 47 ++++- src/librustc_mir/borrow_check/nll/mod.rs | 56 ++--- .../borrow_check/nll/region_infer/mod.rs | 53 +++-- .../nll/subtype_constraint_generation.rs | 199 ------------------ .../nll/type_check/constraint_conversion.rs | 190 +++++++++++++++++ .../borrow_check/nll/type_check/mod.rs | 63 ++++-- 7 files changed, 343 insertions(+), 270 deletions(-) delete mode 100644 src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs create mode 100644 src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 1fb63afc72f..ad3710e9536 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 4f87a2b30ae..72db9f8da98 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 c3d9dd8378d..dcb52a3b18a 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,19 +91,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( Option>>, Option>, ) { - // Run the MIR type-checker. - let liveness = &LivenessResults::compute(mir); - let constraint_sets = &type_check::type_check( - infcx, - param_env, - mir, - def_id, - &universal_regions, - &liveness, - flow_inits, - move_data, - ); - let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts || infcx.tcx.sess.opts.debugging_opts.polonius { @@ -112,25 +99,45 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( None }; + // Run the MIR type-checker. + let liveness = &LivenessResults::compute(mir); + 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, + ); + 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/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index d974a60c15c..f123d421700 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -123,15 +123,14 @@ pub struct OutlivesConstraint { // 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: @@ -141,10 +140,10 @@ pub struct OutlivesConstraint { /// 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({})" }); @@ -240,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(); @@ -262,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, }; @@ -346,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) @@ -354,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"); @@ -397,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. @@ -596,7 +600,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); @@ -635,8 +640,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, @@ -664,7 +672,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"); @@ -845,7 +854,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"); @@ -912,7 +922,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); 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 fd445c62e4e..00000000000 --- 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 00000000000..06aaf6810fa --- /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/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index a8ae614d619..bed0fec4d4d 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; @@ -63,6 +65,7 @@ macro_rules! span_mirbug_and_err { }) } +mod constraint_conversion; mod input_output; mod liveness; @@ -101,7 +104,9 @@ 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> { @@ -114,6 +119,11 @@ pub(crate) fn type_check<'gcx, 'tcx>( 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); @@ -129,6 +139,7 @@ fn type_check_internal<'gcx, 'tcx>( 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( @@ -137,6 +148,8 @@ fn type_check_internal<'gcx, 'tcx>( param_env, region_bound_pairs, implicit_region_bound, + borrowck_context, + mir, ); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); @@ -587,12 +600,20 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { 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 @@ -600,24 +621,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)>, + crate 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 outlives_constraints: 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: Rc>, + crate type_tests: Vec>, } /// The `Locations` type summarizes *where* region constraints are @@ -695,6 +703,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { 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, @@ -703,6 +713,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { param_env, region_bound_pairs, implicit_region_bound, + borrowck_context, + mir, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), } @@ -750,9 +762,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { locations, data ); - self.constraints - .outlives_sets - .push(OutlivesSet { 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 @@ -1712,7 +1731,7 @@ 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, id, param_env, mir, &[], None, None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =)