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.
This commit is contained in:
Niko Matsakis 2018-06-05 11:06:38 -04:00
parent ba6a7f7500
commit 6a5a4874a0
7 changed files with 343 additions and 270 deletions

View File

@ -367,6 +367,11 @@ impl<I: Idx, T> IndexVec<I, T> {
IndexVec { raw: Vec::new(), _marker: PhantomData }
}
#[inline]
pub fn from_raw(raw: Vec<T>) -> Self {
IndexVec { raw, _marker: PhantomData }
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData }

View File

@ -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

View File

@ -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<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
Option<ClosureRegionRequirements<'gcx>>,
) {
// 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,

View File

@ -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<ConstraintIndex>,
pub next: Option<ConstraintIndex>,
/// 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<OutlivesConstraint>,
type_tests: Vec<TypeTest<'tcx>>,
) -> 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<ClosureOutlivesSubject<'gcx>> {
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);

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<AllFacts>,
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<AllFacts>,
) {
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)
}
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<OutlivesConstraint>,
type_tests: &'a mut Vec<TypeTest<'tcx>>,
all_facts: &'a mut Option<AllFacts>,
}
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<OutlivesConstraint>,
type_tests: &'a mut Vec<TypeTest<'tcx>>,
all_facts: &'a mut Option<AllFacts>,
) -> 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);
}
}

View File

@ -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<AllFacts>,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
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<ty::Region<'tcx>>,
borrowck_context: Option<BorrowCheckContext<'_, 'tcx>>,
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<ty::Region<'tcx>>,
reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
constraints: MirTypeckRegionConstraints<'tcx>,
borrowck_context: Option<BorrowCheckContext<'a, 'tcx>>,
mir: &'a Mir<'tcx>,
}
struct BorrowCheckContext<'a, 'tcx: 'a> {
universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a LocationTable,
all_facts: &'a mut Option<AllFacts>,
}
/// 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<OutlivesSet<'tcx>>,
}
crate outlives_constraints: Vec<OutlivesConstraint>,
/// 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<RegionConstraintData<'tcx>>,
crate type_tests: Vec<TypeTest<'tcx>>,
}
/// 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<ty::Region<'tcx>>,
borrowck_context: Option<BorrowCheckContext<'a, 'tcx>>,
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. =)