Auto merge of #51460 - nikomatsakis:nll-perf-examination-refactor-1, r=pnkfelix
Improve memoization and refactor NLL type check I have a big branch that is refactoring NLL type check with the goal of introducing canonicalization-based memoization for all of the operations it does. This PR contains an initial prefix of that branch which, I believe, stands alone. It does introduce a few smaller optimizations of its own: - Skip operations that are trivially a no-op - Cache the results of the dropck-outlives computations done by liveness - Skip resetting unifications if nothing changed r? @pnkfelix
This commit is contained in:
commit
b36917b331
|
@ -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<ut::InPlace<ty::RegionVid>>,
|
||||
|
||||
/// 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<RegionVid, RegionVariableInfo>;
|
||||
|
@ -234,6 +238,7 @@ pub struct RegionVariableInfo {
|
|||
pub struct RegionSnapshot {
|
||||
length: usize,
|
||||
region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<RegionVid> = 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ pub struct RegionInferenceContext<'tcx> {
|
|||
dependency_map: Option<IndexVec<RegionVid, Option<ConstraintIndex>>>,
|
||||
|
||||
/// The constraints we have accumulated and used during solving.
|
||||
constraints: IndexVec<ConstraintIndex, Constraint>,
|
||||
constraints: IndexVec<ConstraintIndex, OutlivesConstraint>,
|
||||
|
||||
/// Type constraints that we check after solving.
|
||||
type_tests: Vec<TypeTest<'tcx>>,
|
||||
|
@ -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<ConstraintIndex>,
|
||||
pub next: Option<ConstraintIndex>,
|
||||
|
||||
/// 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<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();
|
||||
|
||||
|
@ -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<RegionVid, Option<ConstraintIndex>> {
|
||||
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<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");
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 anon_type_map =
|
||||
self.fully_perform_op(
|
||||
Locations::All,
|
||||
|| format!("input_output"),
|
||||
|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
|
||||
);
|
||||
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(&cx.misc(cx.last_span), cx.param_env)
|
||||
.eq(output_ty, mir_output_ty)?);
|
||||
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(&cx.misc(cx.last_span), cx.param_env)
|
||||
.eq(anon_decl.concrete_ty, anon_defn_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");
|
||||
debug!("equate_inputs_and_outputs: equated");
|
||||
|
||||
Ok(InferOk {
|
||||
value: Some(anon_type_map),
|
||||
obligations: obligations.into_vec(),
|
||||
})
|
||||
}).unwrap_or_else(|terr| {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 dataflow::move_paths::{HasMoveData, MoveData};
|
||||
use rustc::mir::{BasicBlock, Location, Mir};
|
||||
use rustc::mir::Local;
|
||||
use rustc::ty::{Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::infer::InferOk;
|
||||
use borrow_check::nll::type_check::AtLocation;
|
||||
use dataflow::move_paths::{HasMoveData, MoveData};
|
||||
use dataflow::MaybeInitializedPlaces;
|
||||
use dataflow::{FlowAtLocation, FlowsAtLocation};
|
||||
use rustc::infer::region_constraints::RegionConstraintData;
|
||||
use rustc::mir::Local;
|
||||
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<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
|
||||
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<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
|
||||
move_data: &'gen MoveData<'tcx>,
|
||||
drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
|
||||
}
|
||||
|
||||
struct DropData<'tcx> {
|
||||
dropped_kinds: Vec<Kind<'tcx>>,
|
||||
region_constraint_data: Option<Rc<RegionConstraintData<'tcx>>>,
|
||||
}
|
||||
|
||||
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<T>(&mut self, value: T, location: Location, cause: Cause)
|
||||
where
|
||||
fn push_type_live_constraint<T>(
|
||||
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 drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
|
||||
let cx = &mut self.cx;
|
||||
move || Self::compute_drop_data(cx, dropped_ty)
|
||||
});
|
||||
|
||||
let mut final_obligations = Vec::new();
|
||||
let mut kind_constraints = Vec::new();
|
||||
if let Some(data) = &drop_data.region_constraint_data {
|
||||
self.cx
|
||||
.push_region_constraints(location.at_self(), data.clone());
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
final_obligations.extend(obligations);
|
||||
fn compute_drop_data(
|
||||
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
|
||||
dropped_ty: Ty<'tcx>,
|
||||
) -> DropData<'tcx> {
|
||||
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
|
||||
|
||||
Ok(InferOk {
|
||||
value: kind_constraints,
|
||||
obligations: final_obligations,
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
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();
|
||||
|
||||
for (kind, location, cause) in kind_constraints {
|
||||
self.push_type_live_constraint(kind, location, cause);
|
||||
DropData {
|
||||
dropped_kinds,
|
||||
region_constraint_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<AllFacts>,
|
||||
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
|
||||
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<ty::Region<'tcx>>,
|
||||
borrowck_context: Option<BorrowCheckContext<'_, 'tcx>>,
|
||||
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<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
|
||||
|
@ -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)>,
|
||||
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: RegionConstraintData<'tcx>,
|
||||
crate type_tests: Vec<TypeTest<'tcx>>,
|
||||
}
|
||||
|
||||
/// 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<ty::Region<'tcx>>,
|
||||
borrowck_context: Option<BorrowCheckContext<'a, 'tcx>>,
|
||||
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)
|
||||
}
|
||||
|
||||
fn fully_perform_op<OP, R>(
|
||||
/// 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<R>(
|
||||
&mut self,
|
||||
locations: Locations,
|
||||
op: OP,
|
||||
) -> Result<R, TypeError<'tcx>>
|
||||
where
|
||||
OP: FnOnce(&mut Self) -> InferResult<'tcx, R>,
|
||||
{
|
||||
describe_op: impl Fn() -> String,
|
||||
op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>,
|
||||
) -> Result<R, TypeError<'tcx>> {
|
||||
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 push_region_constraints(
|
||||
&mut self,
|
||||
locations: Locations,
|
||||
data: Rc<RegionConstraintData<'tcx>>,
|
||||
) {
|
||||
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<R>(
|
||||
&mut self,
|
||||
describe_op: impl Fn() -> String,
|
||||
op: impl FnOnce(&mut Self) -> InferResult<'tcx, R>,
|
||||
) -> Result<(R, Option<Rc<RegionConstraintData<'tcx>>>), 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<T>(&mut self, predicates: T, location: Location)
|
||||
where
|
||||
T: IntoIterator<Item = ty::Predicate<'tcx>>,
|
||||
T::IntoIter: Clone,
|
||||
T: IntoIterator<Item = ty::Predicate<'tcx>> + 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::<Vec<_>>(),
|
||||
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. =)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue