Auto merge of #50370 - nikomatsakis:nll-alias-analysis-flat, r=pnkfelix
introduce `-Znll-facts` to dump base-facts for the NLL analysis r? @pnkfelix
This commit is contained in:
commit
2d847dc90f
12 changed files with 687 additions and 104 deletions
|
|
@ -1978,6 +1978,11 @@ impl fmt::Debug for Location {
|
|||
}
|
||||
|
||||
impl Location {
|
||||
pub const START: Location = Location {
|
||||
block: START_BLOCK,
|
||||
statement_index: 0,
|
||||
};
|
||||
|
||||
/// Returns the location immediately after this one within the enclosing block.
|
||||
///
|
||||
/// Note that if this location represents a terminator, then the
|
||||
|
|
|
|||
|
|
@ -1250,6 +1250,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
|||
"choose which RELRO level to use"),
|
||||
nll_subminimal_causes: bool = (false, parse_bool, [UNTRACKED],
|
||||
"when tracking region error causes, accept subminimal results for faster execution."),
|
||||
nll_facts: bool = (false, parse_bool, [UNTRACKED],
|
||||
"dump facts from NLL analysis into side files"),
|
||||
disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED],
|
||||
"disable user provided type assertion in NLL"),
|
||||
trans_time_graph: bool = (false, parse_bool, [UNTRACKED],
|
||||
|
|
|
|||
123
src/librustc_mir/borrow_check/location.rs
Normal file
123
src/librustc_mir/borrow_check/location.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// 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 rustc::mir::{BasicBlock, Location, Mir};
|
||||
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||
|
||||
/// Maps between a MIR Location, which identifies the a particular
|
||||
/// statement within a basic block, to a "rich location", which
|
||||
/// identifies at a finer granularity. In particular, we distinguish
|
||||
/// the *start* of a statement and the *mid-point*. The mid-point is
|
||||
/// the point *just* before the statement takes effect; in particular,
|
||||
/// for an assignment `A = B`, it is the point where B is about to be
|
||||
/// written into A. This mid-point is a kind of hack to work around
|
||||
/// our inability to track the position information at sufficient
|
||||
/// granularity through outlives relations; however, the rich location
|
||||
/// table serves another purpose: it compresses locations from
|
||||
/// multiple words into a single u32.
|
||||
crate struct LocationTable {
|
||||
num_points: usize,
|
||||
statements_before_block: IndexVec<BasicBlock, usize>,
|
||||
}
|
||||
|
||||
newtype_index!(LocationIndex { DEBUG_FORMAT = "LocationIndex({})" });
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
crate enum RichLocation {
|
||||
Start(Location),
|
||||
Mid(Location),
|
||||
}
|
||||
|
||||
impl LocationTable {
|
||||
crate fn new(mir: &Mir<'_>) -> Self {
|
||||
let mut num_points = 0;
|
||||
let statements_before_block = mir.basic_blocks()
|
||||
.iter()
|
||||
.map(|block_data| {
|
||||
let v = num_points;
|
||||
num_points += (block_data.statements.len() + 1) * 2;
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
debug!(
|
||||
"LocationTable(statements_before_block={:#?})",
|
||||
statements_before_block
|
||||
);
|
||||
debug!("LocationTable: num_points={:#?}", num_points);
|
||||
|
||||
Self {
|
||||
num_points,
|
||||
statements_before_block,
|
||||
}
|
||||
}
|
||||
|
||||
crate fn all_points(&self) -> impl Iterator<Item = LocationIndex> {
|
||||
(0..self.num_points).map(LocationIndex::new)
|
||||
}
|
||||
|
||||
crate fn start_index(&self, location: Location) -> LocationIndex {
|
||||
let Location {
|
||||
block,
|
||||
statement_index,
|
||||
} = location;
|
||||
let start_index = self.statements_before_block[block];
|
||||
LocationIndex::new(start_index + statement_index * 2)
|
||||
}
|
||||
|
||||
crate fn mid_index(&self, location: Location) -> LocationIndex {
|
||||
let Location {
|
||||
block,
|
||||
statement_index,
|
||||
} = location;
|
||||
let start_index = self.statements_before_block[block];
|
||||
LocationIndex::new(start_index + statement_index * 2 + 1)
|
||||
}
|
||||
|
||||
crate fn to_location(&self, index: LocationIndex) -> RichLocation {
|
||||
let point_index = index.index();
|
||||
|
||||
// Find the basic block. We have a vector with the
|
||||
// starting index of the statement in each block. Imagine
|
||||
// we have statement #22, and we have a vector like:
|
||||
//
|
||||
// [0, 10, 20]
|
||||
//
|
||||
// In that case, this represents point_index 2 of
|
||||
// basic block BB2. We know this because BB0 accounts for
|
||||
// 0..10, BB1 accounts for 11..20, and BB2 accounts for
|
||||
// 20...
|
||||
//
|
||||
// To compute this, we could do a binary search, but
|
||||
// because I am lazy we instead iterate through to find
|
||||
// the last point where the "first index" (0, 10, or 20)
|
||||
// was less than the statement index (22). In our case, this will
|
||||
// be (BB2, 20).
|
||||
let (block, &first_index) = self.statements_before_block
|
||||
.iter_enumerated()
|
||||
.filter(|(_, first_index)| **first_index <= point_index)
|
||||
.last()
|
||||
.unwrap();
|
||||
|
||||
let statement_index = (point_index - first_index) / 2;
|
||||
if index.is_start() {
|
||||
RichLocation::Start(Location { block, statement_index })
|
||||
} else {
|
||||
RichLocation::Mid(Location { block, statement_index })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocationIndex {
|
||||
fn is_start(&self) -> bool {
|
||||
// even indices are start points; odd indices are mid points
|
||||
(self.index() % 2) == 0
|
||||
}
|
||||
}
|
||||
|
|
@ -50,12 +50,14 @@ use std::iter;
|
|||
|
||||
use self::borrow_set::{BorrowSet, BorrowData};
|
||||
use self::flows::Flows;
|
||||
use self::location::LocationTable;
|
||||
use self::prefixes::PrefixSet;
|
||||
use self::MutateMode::{JustWrite, WriteAndRead};
|
||||
|
||||
crate mod borrow_set;
|
||||
mod error_reporting;
|
||||
mod flows;
|
||||
mod location;
|
||||
crate mod place_ext;
|
||||
mod prefixes;
|
||||
|
||||
|
|
@ -110,6 +112,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||
let mut mir: Mir<'tcx> = input_mir.clone();
|
||||
let free_regions = nll::replace_regions_in_mir(infcx, def_id, param_env, &mut mir);
|
||||
let mir = &mir; // no further changes
|
||||
let location_table = &LocationTable::new(mir);
|
||||
|
||||
let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) {
|
||||
Ok(move_data) => move_data,
|
||||
|
|
@ -199,6 +202,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||
def_id,
|
||||
free_regions,
|
||||
mir,
|
||||
location_table,
|
||||
param_env,
|
||||
&mut flow_inits,
|
||||
&mdpe.move_data,
|
||||
|
|
|
|||
|
|
@ -8,28 +8,37 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use borrow_check::borrow_set::BorrowSet;
|
||||
use borrow_check::location::LocationTable;
|
||||
use borrow_check::nll::facts::AllFacts;
|
||||
use rustc::hir;
|
||||
use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::mir::visit::TyContext;
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::mir::Place::Projection;
|
||||
use rustc::mir::{Local, PlaceProjection, ProjectionElem};
|
||||
use rustc::mir::visit::TyContext;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::{self, CanonicalTy, ClosureSubsts};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::mir::{BasicBlock, BasicBlockData, Location, Mir, Place, Rvalue};
|
||||
use rustc::mir::{Local, PlaceProjection, ProjectionElem, Statement, Terminator};
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::{self, CanonicalTy, ClosureSubsts};
|
||||
|
||||
use super::region_infer::{Cause, RegionInferenceContext};
|
||||
use super::ToRegionVid;
|
||||
use super::region_infer::{RegionInferenceContext, Cause};
|
||||
|
||||
pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
location_table: &LocationTable,
|
||||
mir: &Mir<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let mut cg = ConstraintGeneration {
|
||||
borrow_set,
|
||||
infcx,
|
||||
regioncx,
|
||||
location_table,
|
||||
all_facts,
|
||||
mir,
|
||||
};
|
||||
|
||||
|
|
@ -41,8 +50,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
|
|||
/// 'cg = the duration of the constraint generation process itself.
|
||||
struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
all_facts: &'cg mut Option<AllFacts>,
|
||||
location_table: &'cg LocationTable,
|
||||
regioncx: &'cg mut RegionInferenceContext<'tcx>,
|
||||
mir: &'cg Mir<'tcx>,
|
||||
borrow_set: &'cg BorrowSet<'tcx>,
|
||||
}
|
||||
|
||||
impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
|
||||
|
|
@ -68,12 +80,14 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
|
|||
/// call. Make them live at the location where they appear.
|
||||
fn visit_ty(&mut self, ty: &ty::Ty<'tcx>, ty_context: TyContext) {
|
||||
match ty_context {
|
||||
TyContext::ReturnTy(source_info) |
|
||||
TyContext::YieldTy(source_info) |
|
||||
TyContext::LocalDecl { source_info, .. } => {
|
||||
span_bug!(source_info.span,
|
||||
"should not be visiting outside of the CFG: {:?}",
|
||||
ty_context);
|
||||
TyContext::ReturnTy(source_info)
|
||||
| TyContext::YieldTy(source_info)
|
||||
| TyContext::LocalDecl { source_info, .. } => {
|
||||
span_bug!(
|
||||
source_info.span,
|
||||
"should not be visiting outside of the CFG: {:?}",
|
||||
ty_context
|
||||
);
|
||||
}
|
||||
TyContext::Location(location) => {
|
||||
self.add_regular_live_constraint(*ty, location, Cause::LiveOther(location));
|
||||
|
|
@ -90,25 +104,117 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
|
|||
self.super_closure_substs(substs);
|
||||
}
|
||||
|
||||
fn visit_statement(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
statement: &Statement<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table
|
||||
.start_index(location.successor_within_block()),
|
||||
));
|
||||
}
|
||||
|
||||
self.super_statement(block, statement, location);
|
||||
}
|
||||
|
||||
fn visit_assign(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
place: &Place<'tcx>,
|
||||
rvalue: &Rvalue<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
// When we see `X = ...`, then kill borrows of
|
||||
// `(*X).foo` and so forth.
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
if let Place::Local(temp) = place {
|
||||
if let Some(borrow_indices) = self.borrow_set.local_map.get(temp) {
|
||||
for &borrow_index in borrow_indices {
|
||||
let location_index = self.location_table.mid_index(location);
|
||||
all_facts.killed.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.super_assign(block, place, rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_terminator(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
terminator: &Terminator<'tcx>,
|
||||
location: Location,
|
||||
) {
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
for successor_block in terminator.successors() {
|
||||
all_facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table
|
||||
.start_index(successor_block.start_location()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
self.super_terminator(block, terminator, location);
|
||||
}
|
||||
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
|
||||
|
||||
// Look for an rvalue like:
|
||||
//
|
||||
// & L
|
||||
//
|
||||
// where L is the path that is borrowed. In that case, we have
|
||||
// to add the reborrow constraints (which don't fall out
|
||||
// naturally from the type-checker).
|
||||
if let Rvalue::Ref(region, _bk, ref borrowed_lv) = *rvalue {
|
||||
self.add_reborrow_constraint(location, region, borrowed_lv);
|
||||
match rvalue {
|
||||
Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
|
||||
// In some cases, e.g. when borrowing from an unsafe
|
||||
// place, we don't bother to create a loan, since
|
||||
// there are no conditions to validate.
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
if let Some(borrow_index) = self.borrow_set.location_map.get(&location) {
|
||||
let region_vid = region.to_region_vid();
|
||||
all_facts.borrow_region.push((
|
||||
region_vid,
|
||||
*borrow_index,
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Look for an rvalue like:
|
||||
//
|
||||
// & L
|
||||
//
|
||||
// where L is the path that is borrowed. In that case, we have
|
||||
// to add the reborrow constraints (which don't fall out
|
||||
// naturally from the type-checker).
|
||||
self.add_reborrow_constraint(location, region, borrowed_place);
|
||||
}
|
||||
|
||||
_ => { }
|
||||
}
|
||||
|
||||
self.super_rvalue(rvalue, location);
|
||||
}
|
||||
|
||||
fn visit_user_assert_ty(&mut self, _c_ty: &CanonicalTy<'tcx>,
|
||||
_local: &Local, _location: Location) { }
|
||||
fn visit_user_assert_ty(
|
||||
&mut self,
|
||||
_c_ty: &CanonicalTy<'tcx>,
|
||||
_local: &Local,
|
||||
_location: Location,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
||||
|
|
@ -122,8 +228,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
|||
{
|
||||
debug!(
|
||||
"add_regular_live_constraint(live_ty={:?}, location={:?})",
|
||||
live_ty,
|
||||
location
|
||||
live_ty, location
|
||||
);
|
||||
|
||||
self.infcx
|
||||
|
|
@ -144,8 +249,10 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
|||
) {
|
||||
let mut borrowed_place = borrowed_place;
|
||||
|
||||
debug!("add_reborrow_constraint({:?}, {:?}, {:?})",
|
||||
location, borrow_region, borrowed_place);
|
||||
debug!(
|
||||
"add_reborrow_constraint({:?}, {:?}, {:?})",
|
||||
location, borrow_region, borrowed_place
|
||||
);
|
||||
while let Projection(box PlaceProjection { base, elem }) = borrowed_place {
|
||||
debug!("add_reborrow_constraint - iteration {:?}", borrowed_place);
|
||||
|
||||
|
|
@ -165,12 +272,20 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
|||
location.successor_within_block(),
|
||||
);
|
||||
|
||||
if let Some(all_facts) = self.all_facts {
|
||||
all_facts.outlives.push((
|
||||
ref_region.to_region_vid(),
|
||||
borrow_region.to_region_vid(),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
}
|
||||
|
||||
match mutbl {
|
||||
hir::Mutability::MutImmutable => {
|
||||
// Immutable reference. We don't need the base
|
||||
// to be valid for the entire lifetime of
|
||||
// the borrow.
|
||||
break
|
||||
break;
|
||||
}
|
||||
hir::Mutability::MutMutable => {
|
||||
// Mutable reference. We *do* need the base
|
||||
|
|
@ -199,19 +314,19 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
|||
}
|
||||
ty::TyRawPtr(..) => {
|
||||
// deref of raw pointer, guaranteed to be valid
|
||||
break
|
||||
break;
|
||||
}
|
||||
ty::TyAdt(def, _) if def.is_box() => {
|
||||
// deref of `Box`, need the base to be valid - propagate
|
||||
}
|
||||
_ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place)
|
||||
_ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place),
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(..) |
|
||||
ProjectionElem::Downcast(..) |
|
||||
ProjectionElem::Index(..) |
|
||||
ProjectionElem::ConstantIndex { .. } |
|
||||
ProjectionElem::Subslice { .. } => {
|
||||
ProjectionElem::Field(..)
|
||||
| ProjectionElem::Downcast(..)
|
||||
| ProjectionElem::Index(..)
|
||||
| ProjectionElem::ConstantIndex { .. }
|
||||
| ProjectionElem::Subslice { .. } => {
|
||||
// other field access
|
||||
}
|
||||
}
|
||||
|
|
|
|||
194
src/librustc_mir/borrow_check/nll/facts.rs
Normal file
194
src/librustc_mir/borrow_check/nll/facts.rs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// 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::{LocationIndex, LocationTable};
|
||||
use dataflow::indexes::BorrowIndex;
|
||||
use rustc::ty::RegionVid;
|
||||
use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
/// The "facts" which are the basis of the NLL borrow analysis.
|
||||
#[derive(Default)]
|
||||
crate struct AllFacts {
|
||||
// `borrow_region(R, B, P)` -- the region R may refer to data from borrow B
|
||||
// starting at the point P (this is usually the point *after* a borrow rvalue)
|
||||
crate borrow_region: Vec<(RegionVid, BorrowIndex, LocationIndex)>,
|
||||
|
||||
// universal_region(R) -- this is a "free region" within fn body
|
||||
crate universal_region: Vec<RegionVid>,
|
||||
|
||||
// `cfg_edge(P,Q)` for each edge P -> Q in the control flow
|
||||
crate cfg_edge: Vec<(LocationIndex, LocationIndex)>,
|
||||
|
||||
// `killed(B,P)` when some prefix of the path borrowed at B is assigned at point P
|
||||
crate killed: Vec<(BorrowIndex, LocationIndex)>,
|
||||
|
||||
// `outlives(R1, R2, P)` when we require `R1@P: R2@P`
|
||||
crate outlives: Vec<(RegionVid, RegionVid, LocationIndex)>,
|
||||
|
||||
// `region_live_at(R, P)` when the region R appears in a live variable at P
|
||||
crate region_live_at: Vec<(RegionVid, LocationIndex)>,
|
||||
}
|
||||
|
||||
impl AllFacts {
|
||||
crate fn write_to_dir(
|
||||
&self,
|
||||
dir: impl AsRef<Path>,
|
||||
location_table: &LocationTable,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let dir: &Path = dir.as_ref();
|
||||
fs::create_dir_all(dir)?;
|
||||
let wr = FactWriter { location_table, dir };
|
||||
macro_rules! write_facts_to_path {
|
||||
($wr:ident . write_facts_to_path($this:ident . [
|
||||
$($field:ident,)*
|
||||
])) => {
|
||||
$(
|
||||
$wr.write_facts_to_path(
|
||||
&$this.$field,
|
||||
&format!("{}.facts", stringify!($field))
|
||||
)?;
|
||||
)*
|
||||
}
|
||||
}
|
||||
write_facts_to_path! {
|
||||
wr.write_facts_to_path(self.[
|
||||
borrow_region,
|
||||
universal_region,
|
||||
cfg_edge,
|
||||
killed,
|
||||
outlives,
|
||||
region_live_at,
|
||||
])
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct FactWriter<'w> {
|
||||
location_table: &'w LocationTable,
|
||||
dir: &'w Path,
|
||||
}
|
||||
|
||||
impl<'w> FactWriter<'w> {
|
||||
fn write_facts_to_path<T>(
|
||||
&self,
|
||||
rows: &Vec<T>,
|
||||
file_name: &str,
|
||||
) -> Result<(), Box<dyn Error>>
|
||||
where
|
||||
T: FactRow,
|
||||
{
|
||||
let file = &self.dir.join(file_name);
|
||||
let mut file = File::create(file)?;
|
||||
for row in rows {
|
||||
row.write(&mut file, self.location_table)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
trait FactRow {
|
||||
fn write(
|
||||
&self,
|
||||
out: &mut File,
|
||||
location_table: &LocationTable,
|
||||
) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
impl FactRow for RegionVid {
|
||||
fn write(
|
||||
&self,
|
||||
out: &mut File,
|
||||
location_table: &LocationTable,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
write_row(out, location_table, &[self])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> FactRow for (A, B)
|
||||
where
|
||||
A: FactCell,
|
||||
B: FactCell,
|
||||
{
|
||||
fn write(
|
||||
&self,
|
||||
out: &mut File,
|
||||
location_table: &LocationTable,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
write_row(out, location_table, &[&self.0, &self.1])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, C> FactRow for (A, B, C)
|
||||
where
|
||||
A: FactCell,
|
||||
B: FactCell,
|
||||
C: FactCell,
|
||||
{
|
||||
fn write(
|
||||
&self,
|
||||
out: &mut File,
|
||||
location_table: &LocationTable,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
write_row(out, location_table, &[&self.0, &self.1, &self.2])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, C, D> FactRow for (A, B, C, D)
|
||||
where
|
||||
A: FactCell,
|
||||
B: FactCell,
|
||||
C: FactCell,
|
||||
D: FactCell,
|
||||
{
|
||||
fn write(
|
||||
&self,
|
||||
out: &mut File,
|
||||
location_table: &LocationTable,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3])
|
||||
}
|
||||
}
|
||||
|
||||
fn write_row(
|
||||
out: &mut dyn Write,
|
||||
location_table: &LocationTable,
|
||||
columns: &[&dyn FactCell],
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
for (index, c) in columns.iter().enumerate() {
|
||||
let tail = if index == columns.len() - 1 {
|
||||
"\n"
|
||||
} else {
|
||||
"\t"
|
||||
};
|
||||
write!(out, "{:?}{}", c.to_string(location_table), tail)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
trait FactCell {
|
||||
fn to_string(&self, location_table: &LocationTable) -> String;
|
||||
}
|
||||
|
||||
impl<A: Debug> FactCell for A {
|
||||
default fn to_string(&self, _location_table: &LocationTable) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FactCell for LocationIndex {
|
||||
fn to_string(&self, location_table: &LocationTable) -> String {
|
||||
format!("{:?}", location_table.to_location(*self))
|
||||
}
|
||||
}
|
||||
|
|
@ -9,36 +9,39 @@
|
|||
// except according to those terms.
|
||||
|
||||
use borrow_check::borrow_set::BorrowSet;
|
||||
use borrow_check::location::LocationTable;
|
||||
use dataflow::move_paths::MoveData;
|
||||
use dataflow::FlowAtLocation;
|
||||
use dataflow::MaybeInitializedPlaces;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Mir};
|
||||
use rustc::ty::{self, RegionKind, RegionVid};
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use transform::MirSource;
|
||||
use util::liveness::{LivenessResults, LocalSet};
|
||||
use dataflow::FlowAtLocation;
|
||||
use dataflow::MaybeInitializedPlaces;
|
||||
use dataflow::move_paths::MoveData;
|
||||
|
||||
use self::mir_util::PassWhere;
|
||||
use util as mir_util;
|
||||
use util::pretty::{self, ALIGN};
|
||||
use self::mir_util::PassWhere;
|
||||
|
||||
mod constraint_generation;
|
||||
pub mod explain_borrow;
|
||||
pub(crate) mod region_infer;
|
||||
mod facts;
|
||||
crate mod region_infer;
|
||||
mod renumber;
|
||||
mod subtype_constraint_generation;
|
||||
pub(crate) mod type_check;
|
||||
crate mod type_check;
|
||||
mod universal_regions;
|
||||
|
||||
use self::facts::AllFacts;
|
||||
use self::region_infer::RegionInferenceContext;
|
||||
use self::universal_regions::UniversalRegions;
|
||||
|
||||
|
||||
/// Rewrites the regions in the MIR to use NLL variables, also
|
||||
/// scraping out the set of universal regions (e.g., region parameters)
|
||||
/// declared on the function. That set will need to be given to
|
||||
|
|
@ -71,10 +74,11 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
|||
def_id: DefId,
|
||||
universal_regions: UniversalRegions<'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
param_env: ty::ParamEnv<'gcx>,
|
||||
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
_borrow_set: &BorrowSet<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) -> (
|
||||
RegionInferenceContext<'tcx>,
|
||||
Option<ClosureRegionRequirements<'gcx>>,
|
||||
|
|
@ -92,15 +96,47 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
|||
move_data,
|
||||
);
|
||||
|
||||
let mut all_facts = if infcx.tcx.sess.opts.debugging_opts.nll_facts {
|
||||
Some(AllFacts::default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(all_facts) = &mut all_facts {
|
||||
all_facts
|
||||
.universal_region
|
||||
.extend(universal_regions.universal_regions());
|
||||
}
|
||||
|
||||
// Create the region inference context, taking ownership of the region inference
|
||||
// data that was contained in `infcx`.
|
||||
let var_origins = infcx.take_region_var_origins();
|
||||
let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
|
||||
subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
|
||||
let mut regioncx =
|
||||
RegionInferenceContext::new(var_origins, universal_regions, mir);
|
||||
|
||||
// Generate various constraints.
|
||||
subtype_constraint_generation::generate(
|
||||
&mut regioncx,
|
||||
&mut all_facts,
|
||||
location_table,
|
||||
mir,
|
||||
constraint_sets,
|
||||
);
|
||||
constraint_generation::generate_constraints(
|
||||
infcx,
|
||||
&mut regioncx,
|
||||
&mut all_facts,
|
||||
location_table,
|
||||
&mir,
|
||||
borrow_set,
|
||||
);
|
||||
|
||||
// Generate non-subtyping constraints.
|
||||
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir);
|
||||
// Dump facts if requested.
|
||||
if let Some(all_facts) = all_facts {
|
||||
let def_path = infcx.tcx.hir.def_path(def_id);
|
||||
let dir_path = PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate());
|
||||
all_facts.write_to_dir(dir_path, location_table).unwrap();
|
||||
}
|
||||
|
||||
// Solve the region constraints.
|
||||
let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
|
||||
|
|
|
|||
|
|
@ -8,14 +8,17 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::mir::Mir;
|
||||
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::{TypeTest, RegionInferenceContext, RegionTest};
|
||||
use super::region_infer::{RegionInferenceContext, RegionTest, TypeTest};
|
||||
use super::type_check::Locations;
|
||||
use super::type_check::MirTypeckRegionConstraints;
|
||||
use super::type_check::OutlivesSet;
|
||||
|
|
@ -27,19 +30,30 @@ use super::type_check::OutlivesSet;
|
|||
/// 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, mir }.generate(constraints);
|
||||
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>) {
|
||||
fn generate(
|
||||
&mut self,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
) {
|
||||
let MirTypeckRegionConstraints {
|
||||
liveness_set,
|
||||
outlives_sets,
|
||||
|
|
@ -57,6 +71,17 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
|||
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 {
|
||||
|
|
@ -65,7 +90,11 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
|||
givens,
|
||||
} = data;
|
||||
|
||||
let span = self.mir.source_info(locations.from_location).span;
|
||||
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);
|
||||
|
|
@ -83,8 +112,24 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
|||
// reverse direction, because `regioncx` talks about
|
||||
// "outlives" (`>=`) whereas the region constraints
|
||||
// talk about `<=`.
|
||||
self.regioncx
|
||||
.add_outlives(span, b_vid, a_vid, locations.at_location);
|
||||
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 {
|
||||
|
|
@ -109,7 +154,7 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
|||
|
||||
let lower_bound = self.to_region_vid(verify.region);
|
||||
|
||||
let point = locations.at_location;
|
||||
let point = locations.at_location().unwrap_or(Location::START);
|
||||
|
||||
let test = self.verify_bound_to_region_test(&verify.bound);
|
||||
|
||||
|
|
@ -149,14 +194,6 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
|
|||
}
|
||||
|
||||
fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
|
||||
// Every region that we see in the constraints came from the
|
||||
// MIR or from the parameter environment. If the former, it
|
||||
// will be a region variable. If the latter, it will be in
|
||||
// the set of universal regions *somewhere*.
|
||||
if let ty::ReVar(vid) = r {
|
||||
*vid
|
||||
} else {
|
||||
self.regioncx.to_region_vid(r)
|
||||
}
|
||||
self.regioncx.to_region_vid(r)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use rustc::traits::PredicateObligations;
|
|||
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
use super::{AtLocation, TypeChecker};
|
||||
use super::{Locations, TypeChecker};
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||
pub(super) fn equate_inputs_and_outputs(
|
||||
|
|
@ -47,26 +47,21 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
} = universal_regions;
|
||||
let infcx = self.infcx;
|
||||
|
||||
let start_position = Location {
|
||||
block: START_BLOCK,
|
||||
statement_index: 0,
|
||||
};
|
||||
|
||||
// Equate expected input tys with those in the MIR.
|
||||
let argument_locals = (1..).map(Local::new);
|
||||
for (&unnormalized_input_ty, local) in unnormalized_input_tys.iter().zip(argument_locals) {
|
||||
let input_ty = self.normalize(&unnormalized_input_ty, start_position);
|
||||
let input_ty = self.normalize(&unnormalized_input_ty, Locations::All);
|
||||
let mir_input_ty = mir.local_decls[local].ty;
|
||||
self.equate_normalized_input_or_output(start_position, input_ty, mir_input_ty);
|
||||
self.equate_normalized_input_or_output(input_ty, mir_input_ty);
|
||||
}
|
||||
|
||||
assert!(
|
||||
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();
|
||||
self.equate_normalized_input_or_output(start_position, ur_yield_ty, mir_yield_ty);
|
||||
self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty);
|
||||
}
|
||||
|
||||
// Return types are a bit more complex. They may contain existential `impl Trait`
|
||||
|
|
@ -75,13 +70,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
"equate_inputs_and_outputs: unnormalized_output_ty={:?}",
|
||||
unnormalized_output_ty
|
||||
);
|
||||
let output_ty = self.normalize(&unnormalized_output_ty, start_position);
|
||||
let output_ty = self.normalize(&unnormalized_output_ty, Locations::All);
|
||||
debug!(
|
||||
"equate_inputs_and_outputs: normalized output_ty={:?}",
|
||||
output_ty
|
||||
);
|
||||
let mir_output_ty = mir.local_decls[RETURN_PLACE].ty;
|
||||
let anon_type_map = self.fully_perform_op(start_position.at_self(), |cx| {
|
||||
let anon_type_map = self.fully_perform_op(Locations::All, |cx| {
|
||||
let mut obligations = ObligationAccumulator::default();
|
||||
|
||||
let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types(
|
||||
|
|
@ -112,7 +107,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs);
|
||||
let anon_defn_ty = renumber::renumber_regions(
|
||||
cx.infcx,
|
||||
TyContext::Location(start_position),
|
||||
TyContext::Location(Location::START),
|
||||
&anon_defn_ty,
|
||||
);
|
||||
debug!(
|
||||
|
|
@ -134,7 +129,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}).unwrap_or_else(|terr| {
|
||||
span_mirbug!(
|
||||
self,
|
||||
start_position,
|
||||
Location::START,
|
||||
"equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
|
||||
output_ty,
|
||||
mir_output_ty,
|
||||
|
|
@ -148,7 +143,7 @@ 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(start_position.at_self(), |_cx| {
|
||||
self.fully_perform_op(Locations::All, |_cx| {
|
||||
infcx.constrain_anon_types(&anon_type_map, universal_regions);
|
||||
Ok(InferOk {
|
||||
value: (),
|
||||
|
|
@ -158,13 +153,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn equate_normalized_input_or_output(&mut self, location: Location, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
|
||||
debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b);
|
||||
|
||||
if let Err(terr) = self.eq_types(a, b, location.at_self()) {
|
||||
if let Err(terr) = self.eq_types(a, b, Locations::All) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
location,
|
||||
Location::START,
|
||||
"equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`",
|
||||
a,
|
||||
b,
|
||||
|
|
|
|||
|
|
@ -618,17 +618,72 @@ pub struct OutlivesSet<'tcx> {
|
|||
pub data: RegionConstraintData<'tcx>,
|
||||
}
|
||||
|
||||
/// The `Locations` type summarizes *where* region constraints are
|
||||
/// required to hold. Normally, this is at a particular point which
|
||||
/// created the obligation, but for constraints that the user gave, we
|
||||
/// want the constraint to hold at all points.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Locations {
|
||||
/// The location in the MIR that generated these constraints.
|
||||
/// This is intended for error reporting and diagnosis; the
|
||||
/// constraints may *take effect* at a distinct spot.
|
||||
pub from_location: Location,
|
||||
pub enum Locations {
|
||||
/// Indicates that a type constraint should always be true. This
|
||||
/// is particularly important in the new borrowck analysis for
|
||||
/// things like the type of the return slot. Consider this
|
||||
/// example:
|
||||
///
|
||||
/// ```
|
||||
/// fn foo<'a>(x: &'a u32) -> &'a u32 {
|
||||
/// let y = 22;
|
||||
/// return &y; // error
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here, we wind up with the signature from the return type being
|
||||
/// something like `&'1 u32` where `'1` is a universal region. But
|
||||
/// the type of the return slot `_0` is something like `&'2 u32`
|
||||
/// where `'2` is an existential region variable. The type checker
|
||||
/// requires that `&'2 u32 = &'1 u32` -- but at what point? In the
|
||||
/// older NLL analysis, we required this only at the entry point
|
||||
/// to the function. By the nature of the constraints, this wound
|
||||
/// up propagating to all points reachable from start (because
|
||||
/// `'1` -- as a universal region -- is live everywhere). In the
|
||||
/// newer analysis, though, this doesn't work: `_0` is considered
|
||||
/// dead at the start (it has no usable value) and hence this type
|
||||
/// equality is basically a no-op. Then, later on, when we do `_0
|
||||
/// = &'3 y`, that region `'3` never winds up related to the
|
||||
/// universal region `'1` and hence no error occurs. Therefore, we
|
||||
/// use Locations::All instead, which ensures that the `'1` and
|
||||
/// `'2` are equal everything. We also use this for other
|
||||
/// user-given type annotations; e.g., if the user wrote `let mut
|
||||
/// x: &'static u32 = ...`, we would ensure that all values
|
||||
/// assigned to `x` are of `'static` lifetime.
|
||||
All,
|
||||
|
||||
/// The constraints must be met at this location. In terms of the
|
||||
/// NLL RFC, when you have a constraint `R1: R2 @ P`, this field
|
||||
/// is the `P` value.
|
||||
pub at_location: Location,
|
||||
Pair {
|
||||
/// The location in the MIR that generated these constraints.
|
||||
/// This is intended for error reporting and diagnosis; the
|
||||
/// constraints may *take effect* at a distinct spot.
|
||||
from_location: Location,
|
||||
|
||||
/// The constraints must be met at this location. In terms of the
|
||||
/// NLL RFC, when you have a constraint `R1: R2 @ P`, this field
|
||||
/// is the `P` value.
|
||||
at_location: Location,
|
||||
}
|
||||
}
|
||||
|
||||
impl Locations {
|
||||
pub fn from_location(&self) -> Option<Location> {
|
||||
match self {
|
||||
Locations::All => None,
|
||||
Locations::Pair { from_location, .. } => Some(*from_location),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at_location(&self) -> Option<Location> {
|
||||
match self {
|
||||
Locations::All => None,
|
||||
Locations::Pair { at_location, .. } => Some(*at_location),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
||||
|
|
@ -770,7 +825,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
"check_stmt: user_assert_ty ty={:?} local_ty={:?}",
|
||||
ty, local_ty
|
||||
);
|
||||
if let Err(terr) = self.eq_types(ty, local_ty, location.at_self()) {
|
||||
if let Err(terr) = self.eq_types(ty, local_ty, Locations::All) {
|
||||
span_mirbug!(
|
||||
self,
|
||||
stmt,
|
||||
|
|
@ -820,7 +875,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
let place_ty = location.ty(mir, tcx).to_ty(tcx);
|
||||
let rv_ty = value.ty(mir, tcx);
|
||||
|
||||
let locations = Locations {
|
||||
let locations = Locations::Pair {
|
||||
from_location: term_location,
|
||||
at_location: target.start_location(),
|
||||
};
|
||||
|
|
@ -839,7 +894,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
// *both* blocks, so we need to ensure that it holds
|
||||
// at both locations.
|
||||
if let Some(unwind) = unwind {
|
||||
let locations = Locations {
|
||||
let locations = Locations::Pair {
|
||||
from_location: term_location,
|
||||
at_location: unwind.start_location(),
|
||||
};
|
||||
|
|
@ -971,7 +1026,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
match *destination {
|
||||
Some((ref dest, target_block)) => {
|
||||
let dest_ty = dest.ty(mir, tcx).to_ty(tcx);
|
||||
let locations = Locations {
|
||||
let locations = Locations::Pair {
|
||||
from_location: term_location,
|
||||
at_location: target_block.start_location(),
|
||||
};
|
||||
|
|
@ -1375,7 +1430,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_successor_within_block())
|
||||
self.sub_types(operand_ty, field_ty, location.at_self())
|
||||
{
|
||||
span_mirbug!(
|
||||
self,
|
||||
|
|
@ -1514,12 +1569,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn normalize<T>(&mut self, value: &T, location: Location) -> T
|
||||
fn normalize<T>(&mut self, value: &T, location: impl ToLocations) -> T
|
||||
where
|
||||
T: fmt::Debug + TypeFoldable<'tcx>,
|
||||
{
|
||||
debug!("normalize(value={:?}, location={:?})", value, location);
|
||||
self.fully_perform_op(location.at_self(), |this| {
|
||||
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)
|
||||
|
|
@ -1585,16 +1640,32 @@ trait AtLocation {
|
|||
|
||||
impl AtLocation for Location {
|
||||
fn at_self(self) -> Locations {
|
||||
Locations {
|
||||
Locations::Pair {
|
||||
from_location: self,
|
||||
at_location: self,
|
||||
}
|
||||
}
|
||||
|
||||
fn at_successor_within_block(self) -> Locations {
|
||||
Locations {
|
||||
Locations::Pair {
|
||||
from_location: self,
|
||||
at_location: self.successor_within_block(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ToLocations: fmt::Debug + Copy {
|
||||
fn to_locations(self) -> Locations;
|
||||
}
|
||||
|
||||
impl ToLocations for Locations {
|
||||
fn to_locations(self) -> Locations {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToLocations for Location {
|
||||
fn to_locations(self) -> Locations {
|
||||
self.at_self()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
|
|||
#![feature(inclusive_range_methods)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(never_type)]
|
||||
#![feature(specialization)]
|
||||
#![cfg_attr(stage0, feature(try_trait))]
|
||||
|
||||
extern crate arena;
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
|||
}
|
||||
|
||||
fn drop_flags_on_init(&mut self) {
|
||||
let loc = Location { block: START_BLOCK, statement_index: 0 };
|
||||
let loc = Location::START;
|
||||
let span = self.patch.source_info_for_location(self.mir, loc).span;
|
||||
let false_ = self.constant_bool(span, false);
|
||||
for flag in self.drop_flags.values() {
|
||||
|
|
@ -576,7 +576,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
|
|||
}
|
||||
|
||||
fn drop_flags_for_args(&mut self) {
|
||||
let loc = Location { block: START_BLOCK, statement_index: 0 };
|
||||
let loc = Location::START;
|
||||
dataflow::drop_flag_effects_for_function_entry(
|
||||
self.tcx, self.mir, self.env, |path, ds| {
|
||||
self.set_drop_flag(loc, path, ds);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue