integrate NLL with MIR type-checker

This commit is contained in:
Niko Matsakis 2017-11-06 07:26:34 -05:00
parent d9e841e756
commit 4b743da596
10 changed files with 256 additions and 162 deletions

View File

@ -192,7 +192,7 @@ impl<'tcx> FreeRegionMap<'tcx> {
///
/// if `r_a` represents `'a`, this function would return `{'b, 'c}`.
pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> {
assert!(is_free(r_a));
assert!(is_free(r_a) || *r_a == ty::ReStatic);
self.relation.greater_than(&r_a)
}
}

View File

@ -112,7 +112,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
None
} else {
Some(nll::compute_regions(infcx, def_id, mir))
Some(nll::compute_regions(infcx, def_id, param_env, mir))
};
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };

View File

@ -9,7 +9,7 @@
// except according to those terms.
use rustc::hir;
use rustc::mir::{BasicBlock, BorrowKind, Location, Lvalue, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::{Location, Lvalue, Mir, Rvalue};
use rustc::mir::visit::Visitor;
use rustc::mir::Lvalue::Projection;
use rustc::mir::{LvalueProjection, ProjectionElem};
@ -21,7 +21,6 @@ use rustc::util::common::ErrorReported;
use rustc_data_structures::fx::FxHashSet;
use syntax::codemap::DUMMY_SP;
use super::subtype;
use super::LivenessResults;
use super::ToRegionVid;
use super::region_infer::RegionInferenceContext;
@ -179,29 +178,6 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
self.visit_mir(self.mir);
}
fn add_borrow_constraint(
&mut self,
location: Location,
destination_lv: &Lvalue<'tcx>,
borrow_region: ty::Region<'tcx>,
_borrow_kind: BorrowKind,
_borrowed_lv: &Lvalue<'tcx>,
) {
let tcx = self.infcx.tcx;
let span = self.mir.source_info(location).span;
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
let destination_region = match destination_ty.sty {
ty::TyRef(r, _) => r,
_ => bug!()
};
self.regioncx.add_outlives(span,
borrow_region.to_region_vid(),
destination_region.to_region_vid(),
location.successor_within_block());
}
fn add_reborrow_constraint(
&mut self,
location: Location,
@ -237,35 +213,22 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
}
impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &Statement<'tcx>,
location: Location) {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
debug!("visit_statement(statement={:?}, location={:?})", statement, location);
// Look for a statement like:
// Look for an rvalue like:
//
// D = & L
// & L
//
// where D is the path to which we are assigning, and
// L is the path that is borrowed.
if let StatementKind::Assign(ref destination_lv, ref rv) = statement.kind {
if let Rvalue::Ref(region, bk, ref borrowed_lv) = *rv {
self.add_borrow_constraint(location, destination_lv, region, bk, borrowed_lv);
self.add_reborrow_constraint(location, region, borrowed_lv);
}
let tcx = self.infcx.tcx;
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
let rv_ty = rv.ty(self.mir, tcx);
let span = self.mir.source_info(location).span;
for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
self.regioncx.add_outlives(span, a, b, location.successor_within_block());
}
// 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);
}
self.super_statement(block, statement, location);
self.super_rvalue(rvalue, location);
}
}

View File

@ -50,6 +50,9 @@ pub fn free_regions<'a, 'gcx, 'tcx>(
let mut indices = FxHashMap();
// `'static` is always free.
insert_free_region(&mut indices, infcx.tcx.types.re_static);
// Extract the early regions.
let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
for item_subst in item_substs {

View File

@ -15,14 +15,15 @@ use rustc::ty::{self, RegionKind, RegionVid};
use rustc::util::nodemap::FxHashMap;
use std::collections::BTreeSet;
use transform::MirSource;
use transform::type_check;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
use util as mir_util;
use self::mir_util::PassWhere;
mod constraint_generation;
mod subtype_constraint_generation;
mod free_regions;
mod subtype;
pub(crate) mod region_infer;
use self::region_infer::RegionInferenceContext;
@ -35,6 +36,7 @@ mod renumber;
pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
def_id: DefId,
param_env: ty::ParamEnv<'gcx>,
mir: &mut Mir<'tcx>,
) -> RegionInferenceContext<'tcx> {
// Compute named region information.
@ -43,6 +45,16 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// Replace all regions with fresh inference variables.
renumber::renumber_mir(infcx, free_regions, mir);
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
// 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, free_regions, mir);
subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
// Compute what is live where.
let liveness = &LivenessResults {
regular: liveness::liveness_of_locals(
@ -62,12 +74,10 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
),
};
// Create the region inference context, generate the constraints,
// and then solve them.
let var_origins = infcx.take_region_var_origins();
let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
let param_env = infcx.tcx.param_env(def_id);
// Generate non-subtyping constraints.
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
// Solve the region constraints.
regioncx.solve(infcx, &mir);
// Dump MIR results into a file, if that is enabled. This let us
@ -123,12 +133,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
match pass_where {
// Before the CFG, dump out the values for each region variable.
PassWhere::BeforeCFG => for region in regioncx.regions() {
writeln!(
out,
"| {:?}: {:?}",
region,
regioncx.region_value(region)
)?;
writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?;
},
// Before each basic block, dump out the values

View File

@ -183,6 +183,15 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
// Add `end(X)` into the set for X.
self.definitions[variable].value.add_free_region(variable);
// `'static` outlives all other free regions as well.
if let ty::ReStatic = free_region {
for &other_variable in indices.values() {
self.definitions[variable]
.value
.add_free_region(other_variable);
}
}
// Go through each region Y that outlives X (i.e., where
// Y: X is true). Add `end(X)` into the set for `Y`.
for superregion in free_region_map.regions_that_outlive(&free_region) {

View File

@ -1,98 +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 transform::nll::ToRegionVid;
use rustc::ty::{self, Ty, TyCtxt, RegionVid};
use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
pub fn outlives_pairs<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
a: Ty<'tcx>,
b: Ty<'tcx>)
-> Vec<(RegionVid, RegionVid)>
{
let mut subtype = Subtype::new(tcx);
match subtype.relate(&a, &b) {
Ok(_) => subtype.outlives_pairs,
Err(_) => bug!("Fail to relate a = {:?} and b = {:?}", a, b)
}
}
struct Subtype<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
outlives_pairs: Vec<(RegionVid, RegionVid)>,
ambient_variance: ty::Variance,
}
impl<'a, 'gcx, 'tcx> Subtype<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Subtype<'a, 'gcx, 'tcx> {
Subtype {
tcx,
outlives_pairs: vec![],
ambient_variance: ty::Covariant,
}
}
}
impl<'a, 'gcx, 'tcx> TypeRelation<'a, 'gcx, 'tcx> for Subtype<'a, 'gcx, 'tcx> {
fn tag(&self) -> &'static str { "Subtype" }
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> { self.tcx }
fn a_is_expected(&self) -> bool { true } // irrelevant
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
variance: ty::Variance,
a: &T,
b: &T)
-> RelateResult<'tcx, T>
{
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
let result = self.relate(a, b);
self.ambient_variance = old_ambient_variance;
result
}
fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
relate::super_relate_tys(self, t, t2)
}
fn regions(&mut self, r_a: ty::Region<'tcx>, r_b: ty::Region<'tcx>)
-> RelateResult<'tcx, ty::Region<'tcx>> {
let a = r_a.to_region_vid();
let b = r_b.to_region_vid();
match self.ambient_variance {
ty::Covariant => {
self.outlives_pairs.push((b, a));
},
ty::Invariant => {
self.outlives_pairs.push((a, b));
self.outlives_pairs.push((b, a));
},
ty::Contravariant => {
self.outlives_pairs.push((a, b));
},
ty::Bivariant => {},
}
Ok(r_a)
}
fn binders<T>(&mut self, _a: &ty::Binder<T>, _b: &ty::Binder<T>)
-> RelateResult<'tcx, ty::Binder<T>>
where T: Relate<'tcx>
{
unimplemented!();
}
}

View File

@ -0,0 +1,112 @@
// 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::Mir;
use rustc::infer::region_constraints::Constraint;
use rustc::infer::region_constraints::RegionConstraintData;
use rustc::ty;
use transform::type_check::MirTypeckRegionConstraints;
use transform::type_check::OutlivesSet;
use super::free_regions::FreeRegions;
use super::region_infer::RegionInferenceContext;
/// 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>,
free_regions: &FreeRegions<'tcx>,
mir: &Mir<'tcx>,
constraints: &MirTypeckRegionConstraints<'tcx>,
) {
SubtypeConstraintGenerator {
regioncx,
free_regions,
mir,
}.generate(constraints);
}
struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
regioncx: &'cx mut RegionInferenceContext<'tcx>,
free_regions: &'cx FreeRegions<'tcx>,
mir: &'cx Mir<'tcx>,
}
impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
let MirTypeckRegionConstraints {
liveness_set,
outlives_sets,
} = constraints;
debug!(
"generate(liveness_set={} items, outlives_sets={} items)",
liveness_set.len(),
outlives_sets.len()
);
for (region, location) 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);
}
for OutlivesSet { locations, data } in outlives_sets {
debug!("generate: constraints at: {:#?}", locations);
let RegionConstraintData {
constraints,
verifys,
givens,
} = data;
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 `<=`.
let span = self.mir.source_info(locations.from_location).span;
self.regioncx
.add_outlives(span, b_vid, a_vid, locations.at_location);
}
assert!(verifys.is_empty(), "verifys not yet implemented");
assert!(
givens.is_empty(),
"MIR type-checker does not use givens (thank goodness)"
);
}
}
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 free regions *somewhere*.
if let ty::ReVar(vid) = r {
*vid
} else {
self.free_regions.indices[&r]
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright 2016 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.
// Basic test for free regions in the NLL code. This test ought to
// report an error due to a reborrowing constraint. Right now, we get
// a variety of errors from the older, AST-based machinery (notably
// borrowck), and then we get the NLL error at the end.
// compile-flags:-Znll -Zborrowck-mir
struct Map {
}
impl Map {
fn get(&self) -> Option<&String> { None }
fn set(&mut self, v: String) { }
}
fn ok(map: &mut Map) -> &String {
loop {
match map.get() {
Some(v) => {
return v;
}
None => {
map.set(String::new()); // Just AST errors here
}
}
}
}
fn err(map: &mut Map) -> &String {
loop {
match map.get() {
Some(v) => {
map.set(String::new()); // Both AST and MIR error here
return v;
}
None => {
map.set(String::new()); // Just AST errors here
}
}
}
}
fn main() { }

View File

@ -0,0 +1,47 @@
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
--> $DIR/get_default.rs:33:17
|
28 | match map.get() {
| --- immutable borrow occurs here
...
33 | map.set(String::new()); // Just AST errors here
| ^^^ mutable borrow occurs here
...
37 | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
--> $DIR/get_default.rs:43:17
|
41 | match map.get() {
| --- immutable borrow occurs here
42 | Some(v) => {
43 | map.set(String::new()); // Both AST and MIR error here
| ^^^ mutable borrow occurs here
...
51 | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
--> $DIR/get_default.rs:47:17
|
41 | match map.get() {
| --- immutable borrow occurs here
...
47 | map.set(String::new()); // Just AST errors here
| ^^^ mutable borrow occurs here
...
51 | }
| - immutable borrow ends here
error[E0502]: cannot borrow `(*map)` as mutable because it is also borrowed as immutable (Mir)
--> $DIR/get_default.rs:43:17
|
41 | match map.get() {
| --- immutable borrow occurs here
42 | Some(v) => {
43 | map.set(String::new()); // Both AST and MIR error here
| ^^^ mutable borrow occurs here
error: aborting due to 4 previous errors