Auto merge of #45668 - nikomatsakis:nll-free-region, r=arielb1

extend NLL with preliminary support for free regions on functions

This PR extends https://github.com/rust-lang/rust/pull/45538 with support for free regions. This is pretty preliminary and will no doubt want to change in various ways, particularly as we add support for closures, but it's enough to get the basic idea in place:

- We now create specific regions to represent each named lifetime declared on the function.
- Region values can contain references to these regions (represented for now as a `BTreeSet<RegionIndex>`).
- If we wind up trying to infer that `'a: 'b` must hold, but no such relationship was declared, we report an error.

It also does a number of drive-by refactorings.

r? @arielb1

cc @spastorino
This commit is contained in:
bors 2017-11-06 23:30:57 +00:00
commit 785643a5eb
19 changed files with 661 additions and 223 deletions

View file

@ -262,6 +262,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
errors: &Vec<RegionResolutionError<'tcx>>) { errors: &Vec<RegionResolutionError<'tcx>>) {
debug!("report_region_errors(): {} errors to start", errors.len()); debug!("report_region_errors(): {} errors to start", errors.len());
if self.tcx.sess.opts.debugging_opts.nll {
for error in errors {
match *error {
RegionResolutionError::ConcreteFailure(ref origin, ..) |
RegionResolutionError::GenericBoundFailure(ref origin, ..) => {
self.tcx.sess.span_warn(
origin.span(),
"not reporting region error due to -Znll");
}
RegionResolutionError::SubSupConflict(ref rvo, ..) => {
self.tcx.sess.span_warn(
rvo.span(),
"not reporting region error due to -Znll");
}
}
}
return;
}
// try to pre-process the errors, which will group some of them // try to pre-process the errors, which will group some of them
// together into a `ProcessedErrors` group: // together into a `ProcessedErrors` group:
let errors = self.process_errors(errors); let errors = self.process_errors(errors);

View file

@ -182,6 +182,19 @@ impl<'tcx> FreeRegionMap<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
result result
} }
/// Returns all regions that are known to outlive `r_a`. For
/// example, in a function:
///
/// ```
/// fn foo<'a, 'b: 'a, 'c: 'b>() { .. }
/// ```
///
/// 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));
self.relation.greater_than(&r_a)
}
} }
fn is_free(r: Region) -> bool { fn is_free(r: Region) -> bool {

View file

@ -209,7 +209,7 @@ macro_rules! make_mir_visitor {
fn visit_ty(&mut self, fn visit_ty(&mut self,
ty: & $($mutability)* Ty<'tcx>, ty: & $($mutability)* Ty<'tcx>,
_: Lookup) { _: TyContext) {
self.super_ty(ty); self.super_ty(ty);
} }
@ -256,8 +256,9 @@ macro_rules! make_mir_visitor {
} }
fn visit_local_decl(&mut self, fn visit_local_decl(&mut self,
local: Local,
local_decl: & $($mutability)* LocalDecl<'tcx>) { local_decl: & $($mutability)* LocalDecl<'tcx>) {
self.super_local_decl(local_decl); self.super_local_decl(local, local_decl);
} }
fn visit_local(&mut self, fn visit_local(&mut self,
@ -291,14 +292,14 @@ macro_rules! make_mir_visitor {
self.visit_visibility_scope_data(scope); self.visit_visibility_scope_data(scope);
} }
let lookup = Lookup::Src(SourceInfo { let lookup = TyContext::SourceInfo(SourceInfo {
span: mir.span, span: mir.span,
scope: ARGUMENT_VISIBILITY_SCOPE, scope: ARGUMENT_VISIBILITY_SCOPE,
}); });
self.visit_ty(&$($mutability)* mir.return_ty, lookup); self.visit_ty(&$($mutability)* mir.return_ty, lookup);
for local_decl in &$($mutability)* mir.local_decls { for local in mir.local_decls.indices() {
self.visit_local_decl(local_decl); self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]);
} }
self.visit_span(&$($mutability)* mir.span); self.visit_span(&$($mutability)* mir.span);
@ -359,7 +360,8 @@ macro_rules! make_mir_visitor {
for operand in lvalues { for operand in lvalues {
self.visit_lvalue(& $($mutability)* operand.lval, self.visit_lvalue(& $($mutability)* operand.lval,
LvalueContext::Validate, location); LvalueContext::Validate, location);
self.visit_ty(& $($mutability)* operand.ty, Lookup::Loc(location)); self.visit_ty(& $($mutability)* operand.ty,
TyContext::Location(location));
} }
} }
StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => { StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
@ -421,7 +423,7 @@ macro_rules! make_mir_visitor {
ref values, ref values,
ref targets } => { ref targets } => {
self.visit_operand(discr, source_location); self.visit_operand(discr, source_location);
self.visit_ty(switch_ty, Lookup::Loc(source_location)); self.visit_ty(switch_ty, TyContext::Location(source_location));
for value in &values[..] { for value in &values[..] {
self.visit_const_int(value, source_location); self.visit_const_int(value, source_location);
} }
@ -545,7 +547,7 @@ macro_rules! make_mir_visitor {
ref $($mutability)* operand, ref $($mutability)* operand,
ref $($mutability)* ty) => { ref $($mutability)* ty) => {
self.visit_operand(operand, location); self.visit_operand(operand, location);
self.visit_ty(ty, Lookup::Loc(location)); self.visit_ty(ty, TyContext::Location(location));
} }
Rvalue::BinaryOp(_bin_op, Rvalue::BinaryOp(_bin_op,
@ -567,7 +569,7 @@ macro_rules! make_mir_visitor {
} }
Rvalue::NullaryOp(_op, ref $($mutability)* ty) => { Rvalue::NullaryOp(_op, ref $($mutability)* ty) => {
self.visit_ty(ty, Lookup::Loc(location)); self.visit_ty(ty, TyContext::Location(location));
} }
Rvalue::Aggregate(ref $($mutability)* kind, Rvalue::Aggregate(ref $($mutability)* kind,
@ -575,7 +577,7 @@ macro_rules! make_mir_visitor {
let kind = &$($mutability)* **kind; let kind = &$($mutability)* **kind;
match *kind { match *kind {
AggregateKind::Array(ref $($mutability)* ty) => { AggregateKind::Array(ref $($mutability)* ty) => {
self.visit_ty(ty, Lookup::Loc(location)); self.visit_ty(ty, TyContext::Location(location));
} }
AggregateKind::Tuple => { AggregateKind::Tuple => {
} }
@ -645,7 +647,7 @@ macro_rules! make_mir_visitor {
ref $($mutability)* ty, ref $($mutability)* ty,
} = *static_; } = *static_;
self.visit_def_id(def_id, location); self.visit_def_id(def_id, location);
self.visit_ty(ty, Lookup::Loc(location)); self.visit_ty(ty, TyContext::Location(location));
} }
fn super_projection(&mut self, fn super_projection(&mut self,
@ -675,7 +677,7 @@ macro_rules! make_mir_visitor {
ProjectionElem::Subslice { from: _, to: _ } => { ProjectionElem::Subslice { from: _, to: _ } => {
} }
ProjectionElem::Field(_field, ref $($mutability)* ty) => { ProjectionElem::Field(_field, ref $($mutability)* ty) => {
self.visit_ty(ty, Lookup::Loc(location)); self.visit_ty(ty, TyContext::Location(location));
} }
ProjectionElem::Index(ref $($mutability)* local) => { ProjectionElem::Index(ref $($mutability)* local) => {
self.visit_local(local, LvalueContext::Consume, location); self.visit_local(local, LvalueContext::Consume, location);
@ -690,6 +692,7 @@ macro_rules! make_mir_visitor {
} }
fn super_local_decl(&mut self, fn super_local_decl(&mut self,
local: Local,
local_decl: & $($mutability)* LocalDecl<'tcx>) { local_decl: & $($mutability)* LocalDecl<'tcx>) {
let LocalDecl { let LocalDecl {
mutability: _, mutability: _,
@ -701,7 +704,10 @@ macro_rules! make_mir_visitor {
is_user_variable: _, is_user_variable: _,
} = *local_decl; } = *local_decl;
self.visit_ty(ty, Lookup::Src(*source_info)); self.visit_ty(ty, TyContext::LocalDecl {
local,
source_info: *source_info,
});
self.visit_source_info(source_info); self.visit_source_info(source_info);
self.visit_visibility_scope(lexical_scope); self.visit_visibility_scope(lexical_scope);
} }
@ -725,7 +731,7 @@ macro_rules! make_mir_visitor {
} = *constant; } = *constant;
self.visit_span(span); self.visit_span(span);
self.visit_ty(ty, Lookup::Loc(location)); self.visit_ty(ty, TyContext::Location(location));
self.visit_literal(literal, location); self.visit_literal(literal, location);
} }
@ -803,10 +809,21 @@ macro_rules! make_mir_visitor {
make_mir_visitor!(Visitor,); make_mir_visitor!(Visitor,);
make_mir_visitor!(MutVisitor,mut); make_mir_visitor!(MutVisitor,mut);
/// Extra information passed to `visit_ty` and friends to give context
/// about where the type etc appears.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Lookup { pub enum TyContext {
Loc(Location), LocalDecl {
Src(SourceInfo), /// The index of the local variable we are visiting.
local: Local,
/// The source location where this local variable was declared.
source_info: SourceInfo,
},
Location(Location),
SourceInfo(SourceInfo),
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]

View file

@ -145,7 +145,7 @@ pub struct BitMatrix {
} }
impl BitMatrix { impl BitMatrix {
// Create a new `rows x columns` matrix, initially empty. /// Create a new `rows x columns` matrix, initially empty.
pub fn new(rows: usize, columns: usize) -> BitMatrix { pub fn new(rows: usize, columns: usize) -> BitMatrix {
// For every element, we need one bit for every other // For every element, we need one bit for every other
// element. Round up to an even number of u64s. // element. Round up to an even number of u64s.
@ -163,9 +163,13 @@ impl BitMatrix {
(start, start + u64s_per_row) (start, start + u64s_per_row)
} }
pub fn add(&mut self, source: usize, target: usize) -> bool { /// Sets the cell at `(row, column)` to true. Put another way, add
let (start, _) = self.range(source); /// `column` to the bitset for `row`.
let (word, mask) = word_mask(target); ///
/// Returns true if this changed the matrix, and false otherwies.
pub fn add(&mut self, row: usize, column: usize) -> bool {
let (start, _) = self.range(row);
let (word, mask) = word_mask(column);
let vector = &mut self.vector[..]; let vector = &mut self.vector[..];
let v1 = vector[start + word]; let v1 = vector[start + word];
let v2 = v1 | mask; let v2 = v1 | mask;
@ -173,19 +177,19 @@ impl BitMatrix {
v1 != v2 v1 != v2
} }
/// Do the bits from `source` contain `target`? /// Do the bits from `row` contain `column`? Put another way, is
/// /// the matrix cell at `(row, column)` true? Put yet another way,
/// Put another way, if the matrix represents (transitive) /// if the matrix represents (transitive) reachability, can
/// reachability, can `source` reach `target`? /// `row` reach `column`?
pub fn contains(&self, source: usize, target: usize) -> bool { pub fn contains(&self, row: usize, column: usize) -> bool {
let (start, _) = self.range(source); let (start, _) = self.range(row);
let (word, mask) = word_mask(target); let (word, mask) = word_mask(column);
(self.vector[start + word] & mask) != 0 (self.vector[start + word] & mask) != 0
} }
/// Returns those indices that are reachable from both `a` and /// Returns those indices that are true in rows `a` and `b`. This
/// `b`. This is an O(n) operation where `n` is the number of /// is an O(n) operation where `n` is the number of elements
/// elements (somewhat independent from the actual size of the /// (somewhat independent from the actual size of the
/// intersection, in particular). /// intersection, in particular).
pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> { pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> {
let (a_start, a_end) = self.range(a); let (a_start, a_end) = self.range(a);
@ -206,7 +210,7 @@ impl BitMatrix {
result result
} }
/// Add the bits from `read` to the bits from `write`, /// Add the bits from row `read` to the bits from row `write`,
/// return true if anything changed. /// return true if anything changed.
/// ///
/// This is used when computing transitive reachability because if /// This is used when computing transitive reachability because if
@ -227,6 +231,8 @@ impl BitMatrix {
changed changed
} }
/// Iterates through all the columns set to true in a given row of
/// the matrix.
pub fn iter<'a>(&'a self, row: usize) -> BitVectorIter<'a> { pub fn iter<'a>(&'a self, row: usize) -> BitVectorIter<'a> {
let (start, end) = self.range(row); let (start, end) = self.range(row);
BitVectorIter { BitVectorIter {

View file

@ -134,12 +134,12 @@ impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
} }
} }
/// Returns a vector of all things less than `a`. /// Returns a vector of all things greater than `a`.
/// ///
/// Really this probably ought to be `impl Iterator<Item=&T>`, but /// Really this probably ought to be `impl Iterator<Item=&T>`, but
/// I'm too lazy to make that work, and -- given the caching /// I'm too lazy to make that work, and -- given the caching
/// strategy -- it'd be a touch tricky anyhow. /// strategy -- it'd be a touch tricky anyhow.
pub fn less_than(&self, a: &T) -> Vec<&T> { pub fn greater_than(&self, a: &T) -> Vec<&T> {
match self.index(a) { match self.index(a) {
Some(a) => self.with_closure(|closure| { Some(a) => self.with_closure(|closure| {
closure.iter(a.0).map(|i| &self.elements[i]).collect() closure.iter(a.0).map(|i| &self.elements[i]).collect()

View file

@ -17,7 +17,7 @@ use rustc::hir::def_id::DefId;
use rustc::middle::region; use rustc::middle::region;
use rustc::mir::*; use rustc::mir::*;
use rustc::mir::transform::MirSource; use rustc::mir::transform::MirSource;
use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::util::nodemap::NodeMap; use rustc::util::nodemap::NodeMap;
@ -165,7 +165,7 @@ struct GlobalizeMir<'a, 'gcx: 'a> {
} }
impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) {
if let Some(lifted) = self.tcx.lift(ty) { if let Some(lifted) = self.tcx.lift(ty) {
*ty = lifted; *ty = lifted;
} else { } else {

View file

@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
location_map: FxHashMap<Location, BorrowIndex>, location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>, region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>, region_span_map: FxHashMap<RegionKind, Span>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>, nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
} }
// temporarily allow some dead fields: `kind` and `region` will be // temporarily allow some dead fields: `kind` and `region` will be
@ -69,7 +69,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>) nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
-> Self { -> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(), let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(), location_map: FxHashMap(),
@ -139,11 +139,21 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
location: Location) { location: Location) {
if let Some(regioncx) = self.nonlexical_regioncx { if let Some(regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
let borrow_region = regioncx.region_value(borrow_data.region.to_region_index()); let borrow_region = borrow_data.region.to_region_index();
if !borrow_region.may_contain(location) && location != borrow_data.location { if !regioncx.region_contains_point(borrow_region, location) {
debug!("kill_loans_out_of_scope_at_location: kill{:?} \ // The region checker really considers the borrow
location={:?} borrow_data={:?}", borrow_index, location, borrow_data); // to start at the point **after** the location of
sets.kill(&borrow_index); // the borrow, but the borrow checker puts the gen
// directly **on** the location of the
// borrow. This results in a gen/kill both being
// generated for same point if we are not
// careful. Probably we should change the point of
// the gen, but for now we hackily account for the
// mismatch here by not generating a kill for the
// location on the borrow itself.
if location != borrow_data.location {
sets.kill(&borrow_index);
}
} }
} }
} }

View file

@ -24,7 +24,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc::middle::region; use rustc::middle::region;
use rustc::mir::transform::{MirPass, MirSource}; use rustc::mir::transform::{MirPass, MirSource};
use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind}; use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, Visitor, Lookup}; use rustc::mir::visit::{MutVisitor, Visitor, TyContext};
use rustc::ty::{Ty, RegionKind, TyCtxt}; use rustc::ty::{Ty, RegionKind, TyCtxt};
pub struct CleanEndRegions; pub struct CleanEndRegions;
@ -67,7 +67,7 @@ impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
self.super_rvalue(rvalue, location); self.super_rvalue(rvalue, location);
} }
fn visit_ty(&mut self, ty: &Ty<'tcx>, _: Lookup) { fn visit_ty(&mut self, ty: &Ty<'tcx>, _: TyContext) {
// Gather regions that occur in types // Gather regions that occur in types
for re in ty.walk().flat_map(|t| t.regions()) { for re in ty.walk().flat_map(|t| t.regions()) {
match *re { match *re {

View file

@ -17,7 +17,7 @@
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::mir::*; use rustc::mir::*;
use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::mir::transform::{MirPass, MirSource}; use rustc::mir::transform::{MirPass, MirSource};
struct EraseRegionsVisitor<'a, 'tcx: 'a> { struct EraseRegionsVisitor<'a, 'tcx: 'a> {
@ -35,7 +35,7 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> {
} }
impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) {
if !self.in_validation_statement { if !self.in_validation_statement {
*ty = self.tcx.erase_regions(ty); *ty = self.tcx.erase_regions(ty);
} }

View file

@ -29,7 +29,7 @@ use super::region_infer::RegionInferenceContext;
pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext, regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>, mir: &Mir<'tcx>,
mir_source: MirSource, mir_source: MirSource,
liveness: &LivenessResults, liveness: &LivenessResults,
@ -45,7 +45,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> { struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cx mut RegionInferenceContext, regioncx: &'cx mut RegionInferenceContext<'tcx>,
mir: &'cx Mir<'tcx>, mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults, liveness: &'cx LivenessResults,
mir_source: MirSource, mir_source: MirSource,
@ -191,6 +191,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
_borrowed_lv: &Lvalue<'tcx>, _borrowed_lv: &Lvalue<'tcx>,
) { ) {
let tcx = self.infcx.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_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
let destination_region = match destination_ty.sty { let destination_region = match destination_ty.sty {
@ -198,7 +199,8 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
_ => bug!() _ => bug!()
}; };
self.regioncx.add_outlives(borrow_region.to_region_index(), self.regioncx.add_outlives(span,
borrow_region.to_region_index(),
destination_region.to_region_index(), destination_region.to_region_index(),
location.successor_within_block()); location.successor_within_block());
} }
@ -226,7 +228,9 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
}, },
} }
self.regioncx.add_outlives(base_region.to_region_index(), let span = self.mir.source_info(location).span;
self.regioncx.add_outlives(span,
base_region.to_region_index(),
borrow_region.to_region_index(), borrow_region.to_region_index(),
location.successor_within_block()); location.successor_within_block());
} }
@ -259,8 +263,9 @@ impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx); let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
let rv_ty = rv.ty(self.mir, 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) { for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
self.regioncx.add_outlives(a, b, location.successor_within_block()); self.regioncx.add_outlives(span, a, b, location.successor_within_block());
} }
} }

View file

@ -0,0 +1,88 @@
// 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.
//! Code to extract the free regions declared on a function and the
//! relationships between them. For example:
//!
//! ```
//! fn foo<'a, 'b, 'c: 'b>() { }
//! ```
//!
//! here we would be returning a map assigning each of `{'a, 'b, 'c}`
//! to an index, as well as the `FreeRegionMap` which can compute
//! relationships between them.
//!
//! The code in this file doesn't *do anything* with those results; it
//! just returns them for other code to use.
use rustc::infer::InferCtxt;
use rustc::middle::free_region::FreeRegionMap;
use rustc::mir::transform::MirSource;
use rustc::ty;
use rustc::ty::subst::Substs;
use rustc::util::nodemap::FxHashMap;
#[derive(Debug)]
pub struct FreeRegions<'tcx> {
/// Given a free region defined on this function (either early- or
/// late-bound), this maps it to its internal region index. The
/// corresponding variable will be "capped" so that it cannot
/// grow.
pub indices: FxHashMap<ty::Region<'tcx>, usize>,
/// The map from the typeck tables telling us how to relate free regions.
pub free_region_map: &'tcx FreeRegionMap<'tcx>,
}
pub fn free_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource,
) -> FreeRegions<'tcx> {
debug!("free_regions(source={:?})", source);
let item_id = source.item_id();
let item_def_id = infcx.tcx.hir.local_def_id(item_id);
let mut indices = FxHashMap();
// Extract the early regions.
let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
for item_subst in item_substs {
if let Some(region) = item_subst.as_region() {
insert_free_region(&mut indices, region);
}
}
// Extract the late-bound regions. Use the liberated fn sigs,
// where the late-bound regions will have been converted into free
// regions, and add them to the map.
let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
let tables = infcx.tcx.typeck_tables_of(item_def_id);
let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
infcx
.tcx
.for_each_free_region(&fn_sig.inputs_and_output, |region| {
if let ty::ReFree(_) = *region {
insert_free_region(&mut indices, region);
}
});
debug!("free_regions: indices={:#?}", indices);
FreeRegions { indices, free_region_map: &tables.free_region_map }
}
fn insert_free_region<'tcx>(
free_regions: &mut FxHashMap<ty::Region<'tcx>, usize>,
region: ty::Region<'tcx>,
) {
let len = free_regions.len();
free_regions.entry(region).or_insert(len);
}

View file

@ -9,19 +9,19 @@
// except according to those terms. // except according to those terms.
use rustc::ty::{self, RegionKind}; use rustc::ty::{self, RegionKind};
use rustc::mir::{Location, Mir}; use rustc::mir::Mir;
use rustc::mir::transform::MirSource; use rustc::mir::transform::MirSource;
use rustc::infer::InferCtxt; use rustc::infer::InferCtxt;
use rustc::util::nodemap::FxHashMap; use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet}; use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
use util as mir_util; use util as mir_util;
use self::mir_util::PassWhere; use self::mir_util::PassWhere;
mod constraint_generation; mod constraint_generation;
mod free_regions;
mod subtype; mod subtype;
pub(crate) mod region_infer; pub(crate) mod region_infer;
@ -36,9 +36,12 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource, source: MirSource,
mir: &mut Mir<'tcx>, mir: &mut Mir<'tcx>,
) -> RegionInferenceContext { ) -> RegionInferenceContext<'tcx> {
// Compute named region information.
let free_regions = &free_regions::free_regions(infcx, source);
// Replace all regions with fresh inference variables. // Replace all regions with fresh inference variables.
let num_region_variables = renumber::renumber_mir(infcx, mir); let num_region_variables = renumber::renumber_mir(infcx, free_regions, mir);
// Compute what is live where. // Compute what is live where.
let liveness = &LivenessResults { let liveness = &LivenessResults {
@ -61,11 +64,9 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// Create the region inference context, generate the constraints, // Create the region inference context, generate the constraints,
// and then solve them. // and then solve them.
let mut regioncx = RegionInferenceContext::new(num_region_variables); let mut regioncx = RegionInferenceContext::new(free_regions, num_region_variables, mir);
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness);
let errors = regioncx.solve(infcx, &mir); regioncx.solve(infcx, &mir);
assert!(errors.is_empty(), "FIXME: report region inference failures");
// Dump MIR results into a file, if that is enabled. This let us // Dump MIR results into a file, if that is enabled. This let us
// write unit-tests. // write unit-tests.
@ -149,27 +150,6 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
}); });
} }
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Region {
points: BTreeSet<Location>,
}
impl fmt::Debug for Region {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "{:?}", self.points)
}
}
impl Region {
pub fn add_point(&mut self, point: Location) -> bool {
self.points.insert(point)
}
pub fn may_contain(&self, point: Location) -> bool {
self.points.contains(&point)
}
}
newtype_index!(RegionIndex { newtype_index!(RegionIndex {
DEBUG_FORMAT = "'_#{}r", DEBUG_FORMAT = "'_#{}r",
}); });

View file

@ -8,161 +8,328 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use super::{Region, RegionIndex}; use super::RegionIndex;
use std::mem; use super::free_regions::FreeRegions;
use rustc::infer::InferCtxt; use rustc::infer::InferCtxt;
use rustc::mir::{Location, Mir}; use rustc::mir::{Location, Mir};
use rustc_data_structures::indexed_vec::IndexVec; use rustc::ty;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use std::collections::BTreeSet;
use std::fmt;
use syntax_pos::Span;
pub struct RegionInferenceContext { pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region /// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionIndex`). The /// variables are identified by their index (`RegionIndex`). The
/// definition contains information about where the region came /// definition contains information about where the region came
/// from as well as its final inferred value. /// from as well as its final inferred value.
definitions: IndexVec<RegionIndex, RegionDefinition>, definitions: IndexVec<RegionIndex, RegionDefinition<'tcx>>,
/// The indices of all "free regions" in scope. These are the
/// lifetime parameters (anonymous and named) declared in the
/// function signature:
///
/// fn foo<'a, 'b>(x: &Foo<'a, 'b>)
/// ^^ ^^ ^
///
/// These indices will be from 0..N, as it happens, but we collect
/// them into a vector for convenience.
free_regions: Vec<RegionIndex>,
/// The constraints we have accumulated and used during solving. /// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>, constraints: Vec<Constraint>,
/// List of errors we have accumulated as we add constraints.
/// After solving is done, this is replaced with an empty vector.
errors: Vec<InferenceError>,
}
pub struct InferenceError {
pub constraint_point: Location,
pub name: (), // FIXME(nashenas88) RegionName
} }
#[derive(Default)] #[derive(Default)]
struct RegionDefinition { struct RegionDefinition<'tcx> {
name: (), // FIXME(nashenas88) RegionName /// If this is a free-region, then this is `Some(X)` where `X` is
/// the name of the region.
name: Option<ty::Region<'tcx>>,
/// If true, this is a constant region which cannot grow larger.
/// This is used for named regions as well as `'static`.
constant: bool,
/// The current value of this inference variable. This starts out
/// empty, but grows as we add constraints. The final value is
/// determined when `solve()` is executed.
value: Region, value: Region,
capped: bool, }
/// The value of an individual region variable. Region variables
/// consist of a set of points in the CFG as well as a set of "free
/// regions", which are sometimes written as `end(R)`. These
/// correspond to the named lifetimes and refer to portions of the
/// caller's control-flow graph -- specifically some portion that can
/// be reached after we return.
#[derive(Clone, Default, PartialEq, Eq)]
struct Region {
points: BTreeSet<Location>,
free_regions: BTreeSet<RegionIndex>,
}
impl fmt::Debug for Region {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
formatter
.debug_set()
.entries(&self.points)
.entries(&self.free_regions)
.finish()
}
}
impl Region {
fn add_point(&mut self, point: Location) -> bool {
self.points.insert(point)
}
fn add_free_region(&mut self, region: RegionIndex) -> bool {
self.free_regions.insert(region)
}
fn contains_point(&self, point: Location) -> bool {
self.points.contains(&point)
}
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Constraint { pub struct Constraint {
sub: RegionIndex, /// Where did this constraint arise?
span: Span,
/// The region SUP must outlive SUB...
sup: RegionIndex, sup: RegionIndex,
/// Region that must be outlived.
sub: RegionIndex,
/// At this location.
point: Location, point: Location,
} }
impl RegionInferenceContext { impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
pub fn new(num_region_variables: usize) -> Self { /// Creates a new region inference context with a total of
Self { /// `num_region_variables` valid inference variables; the first N
/// of those will be constant regions representing the free
/// regions defined in `free_regions`.
pub fn new(
free_regions: &FreeRegions<'tcx>,
num_region_variables: usize,
mir: &Mir<'tcx>,
) -> Self {
let mut result = Self {
definitions: (0..num_region_variables) definitions: (0..num_region_variables)
.map(|_| RegionDefinition::default()) .map(|_| RegionDefinition::default())
.collect(), .collect(),
constraints: Vec::new(), constraints: Vec::new(),
errors: Vec::new(), free_regions: Vec::new(),
} };
result.init_free_regions(free_regions, mir);
result
} }
/// Initializes the region variables for each free region
/// (lifetime parameter). The first N variables always correspond
/// to the free regions appearing in the function signature (both
/// named and anonymous) and where clauses. This function iterates
/// over those regions and initializes them with minimum values.
///
/// For example:
///
/// fn foo<'a, 'b>(..) where 'a: 'b
///
/// would initialize two variables like so:
///
/// R0 = { CFG, R0 } // 'a
/// R1 = { CFG, R0, R1 } // 'b
///
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
/// and (b) any free regions that it outlives, which in this case
/// is just itself. R1 (`'b`) in contrast also outlives `'a` and
/// hence contains R0 and R1.
fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) {
let &FreeRegions {
ref indices,
ref free_region_map,
} = free_regions;
// For each free region X:
for (free_region, index) in indices {
let variable = RegionIndex::new(*index);
self.free_regions.push(variable);
// Initialize the name and a few other details.
self.definitions[variable].name = Some(free_region);
self.definitions[variable].constant = true;
// Add all nodes in the CFG to `definition.value`.
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
let definition = &mut self.definitions[variable];
for statement_index in 0..block_data.statements.len() + 1 {
let location = Location {
block,
statement_index,
};
definition.value.add_point(location);
}
}
// Add `end(X)` into the set for X.
self.definitions[variable].value.add_free_region(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) {
let superregion_index = RegionIndex::new(indices[superregion]);
self.definitions[superregion_index]
.value
.add_free_region(variable);
}
debug!(
"init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
free_region,
variable,
self.definitions[variable].value
);
}
}
/// Returns an iterator over all the region indices. /// Returns an iterator over all the region indices.
pub fn regions(&self) -> impl Iterator<Item = RegionIndex> { pub fn regions(&self) -> impl Iterator<Item = RegionIndex> {
self.definitions.indices() self.definitions.indices()
} }
/// Returns the inferred value for the region `r`. /// Returns true if the region `r` contains the point `p`.
/// ///
/// Until `solve()` executes, this value is not particularly meaningful. /// Until `solve()` executes, this value is not particularly meaningful.
pub fn region_value(&self, r: RegionIndex) -> &Region { pub fn region_contains_point(&self, r: RegionIndex, p: Location) -> bool {
self.definitions[r].value.contains_point(p)
}
/// Returns access to the value of `r` for debugging purposes.
pub(super) fn region_value(&self, r: RegionIndex) -> &fmt::Debug {
&self.definitions[r].value &self.definitions[r].value
} }
/// Flags a region as being "capped" -- this means that if its /// Indicates that the region variable `v` is live at the point `point`.
/// value is required to grow as a result of some constraint
/// (e.g., `add_live_point` or `add_outlives`), that indicates an
/// error. This is used for the regions representing named
/// lifetime parameters on a function: they get initialized to
/// their complete value, and then "capped" so that they can no
/// longer grow.
#[allow(dead_code)]
pub(super) fn cap_var(&mut self, v: RegionIndex) {
self.definitions[v].capped = true;
}
pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) { pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
debug!("add_live_point({:?}, {:?})", v, point); debug!("add_live_point({:?}, {:?})", v, point);
let definition = &mut self.definitions[v]; let definition = &mut self.definitions[v];
if definition.value.add_point(point) { if !definition.constant {
if definition.capped { definition.value.add_point(point);
self.errors.push(InferenceError { } else {
constraint_point: point, // Constants are used for free regions, which already
name: definition.name, // contain all the points in the control-flow graph.
}); assert!(definition.value.contains_point(point));
}
} }
} }
pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
pub(super) fn add_outlives(
&mut self,
span: Span,
sup: RegionIndex,
sub: RegionIndex,
point: Location,
) {
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
self.constraints.push(Constraint { sup, sub, point }); self.constraints.push(Constraint {
span,
sup,
sub,
point,
});
} }
/// Perform region inference. /// Perform region inference.
pub(super) fn solve<'a, 'gcx, 'tcx>( pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>) {
let errors = self.propagate_constraints(mir);
// worst error msg ever
for (fr1, span, fr2) in errors {
infcx.tcx.sess.span_err(
span,
&format!(
"free region `{}` does not outlive `{}`",
self.definitions[fr1].name.unwrap(),
self.definitions[fr2].name.unwrap()
),
);
}
}
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
fn propagate_constraints(
&mut self, &mut self,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>,
mir: &'a Mir<'tcx>, ) -> Vec<(RegionIndex, Span, RegionIndex)> {
) -> Vec<InferenceError>
where
'gcx: 'tcx + 'a,
'tcx: 'a,
{
let mut changed = true; let mut changed = true;
let mut dfs = Dfs::new(infcx, mir); let mut dfs = Dfs::new(mir);
let mut error_regions = FxHashSet();
let mut errors = vec![];
while changed { while changed {
changed = false; changed = false;
for constraint in &self.constraints { for constraint in &self.constraints {
debug!("constraint: {:?}", constraint);
let sub = &self.definitions[constraint.sub].value.clone(); let sub = &self.definitions[constraint.sub].value.clone();
let sup_def = &mut self.definitions[constraint.sup]; let sup_def = &mut self.definitions[constraint.sup];
debug!("constraint: {:?}", constraint);
debug!(" sub (before): {:?}", sub); debug!(" sub (before): {:?}", sub);
debug!(" sup (before): {:?}", sup_def.value); debug!(" sup (before): {:?}", sup_def.value);
if dfs.copy(sub, &mut sup_def.value, constraint.point) { if !sup_def.constant {
changed = true; // If this is not a constant, then grow the value as needed to
if sup_def.capped { // accommodate the outlives constraint.
// This is kind of a hack, but when we add a
// constraint, the "point" is always the point
// AFTER the action that induced the
// constraint. So report the error on the
// action BEFORE that.
assert!(constraint.point.statement_index > 0);
let p = Location {
block: constraint.point.block,
statement_index: constraint.point.statement_index - 1,
};
self.errors.push(InferenceError { if dfs.copy(sub, &mut sup_def.value, constraint.point) {
constraint_point: p, changed = true;
name: sup_def.name, }
});
debug!(" sup (after) : {:?}", sup_def.value);
debug!(" changed : {:?}", changed);
} else {
// If this is a constant, check whether it *would
// have* to grow in order for the constraint to be
// satisfied. If so, create an error.
let mut sup_value = sup_def.value.clone();
if dfs.copy(sub, &mut sup_value, constraint.point) {
// Constant values start out with the entire
// CFG, so it must be some new free region
// that was added. Find one.
let &new_region = sup_value
.free_regions
.difference(&sup_def.value.free_regions)
.next()
.unwrap();
debug!(" new_region : {:?}", new_region);
if error_regions.insert(constraint.sup) {
errors.push((constraint.sup, constraint.span, new_region));
}
} }
} }
debug!(" sup (after) : {:?}", sup_def.value);
debug!(" changed : {:?}", changed);
} }
debug!("\n"); debug!("\n");
} }
errors
mem::replace(&mut self.errors, Vec::new())
} }
} }
struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> { struct Dfs<'a, 'tcx: 'a> {
#[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
} }
impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> { impl<'a, 'tcx> Dfs<'a, 'tcx> {
fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { fn new(mir: &'a Mir<'tcx>) -> Self {
Self { infcx, mir } Self { mir }
} }
fn copy( fn copy(
@ -180,7 +347,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
while let Some(p) = stack.pop() { while let Some(p) = stack.pop() {
debug!(" dfs: p={:?}", p); debug!(" dfs: p={:?}", p);
if !from_region.may_contain(p) { if !from_region.contains_point(p) {
debug!(" not in from-region"); debug!(" not in from-region");
continue; continue;
} }
@ -215,19 +382,14 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
}; };
if successor_points.is_empty() { if successor_points.is_empty() {
// FIXME handle free regions
// If we reach the END point in the graph, then copy // If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_region` // over any skolemized end points in the `from_region`
// and make sure they are included in the `to_region`. // and make sure they are included in the `to_region`.
// for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
// // FIXME(nashenas88) figure out skolemized_end points debug!(" dfs: free_regions={:?}", from_region.free_regions);
// let block = self.env.graph.skolemized_end(region_decl.name); for &fr in &from_region.free_regions {
// let skolemized_end_point = Location { changed |= to_region.free_regions.insert(fr);
// block, }
// statement_index: 0,
// };
// changed |= to_region.add_point(skolemized_end_point);
// }
} else { } else {
stack.extend(successor_points); stack.extend(successor_points);
} }

View file

@ -8,81 +8,131 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use rustc::ty::TypeFoldable; use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::subst::{Kind, Substs}; use rustc::ty::subst::{Kind, Substs};
use rustc::ty::{Ty, ClosureSubsts, RegionVid, RegionKind}; use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable};
use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{self as rustc_infer, InferCtxt}; use rustc::infer::{self as rustc_infer, InferCtxt};
use syntax_pos::DUMMY_SP; use syntax_pos::DUMMY_SP;
use std::collections::HashMap; use std::collections::HashMap;
use super::free_regions::FreeRegions;
/// Replaces all free regions appearing in the MIR with fresh /// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created. /// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, pub fn renumber_mir<'a, 'gcx, 'tcx>(
mir: &mut Mir<'tcx>) infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-> usize free_regions: &FreeRegions<'tcx>,
{ mir: &mut Mir<'tcx>,
let mut visitor = NLLVisitor::new(infcx); ) -> usize {
// Create inference variables for each of the free regions
// declared on the function signature.
let free_region_inference_vars = (0..free_regions.indices.len())
.map(|_| {
infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
})
.collect();
let mut visitor = NLLVisitor {
infcx,
lookup_map: HashMap::new(),
num_region_variables: free_regions.indices.len(),
free_regions,
free_region_inference_vars,
arg_count: mir.arg_count,
};
visitor.visit_mir(mir); visitor.visit_mir(mir);
visitor.num_region_variables visitor.num_region_variables
} }
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
lookup_map: HashMap<RegionVid, Lookup>, lookup_map: HashMap<RegionVid, TyContext>,
num_region_variables: usize, num_region_variables: usize,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
free_regions: &'a FreeRegions<'tcx>,
free_region_inference_vars: Vec<ty::Region<'tcx>>,
arg_count: usize,
} }
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { /// Replaces all regions appearing in `value` with fresh inference
NLLVisitor { /// variables. This is what we do for almost the entire MIR, with
infcx, /// the exception of the declared types of our arguments.
lookup_map: HashMap::new(), fn renumber_regions<T>(&mut self, value: &T) -> T
num_region_variables: 0 where
} T: TypeFoldable<'tcx>,
{
self.infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
self.num_region_variables += 1;
self.infcx
.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
})
} }
fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> { /// Renumbers the regions appearing in `value`, but those regions
self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { /// are expected to be free regions from the function signature.
self.num_region_variables += 1; fn renumber_free_regions<T>(&mut self, value: &T) -> T
self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) where
}) T: TypeFoldable<'tcx>,
{
self.infcx
.tcx
.fold_regions(value, &mut false, |region, _depth| {
let index = self.free_regions.indices[&region];
self.free_region_inference_vars[index]
})
} }
fn store_region(&mut self, region: &RegionKind, lookup: Lookup) { fn store_region(&mut self, region: &RegionKind, lookup: TyContext) {
if let RegionKind::ReVar(rid) = *region { if let RegionKind::ReVar(rid) = *region {
self.lookup_map.entry(rid).or_insert(lookup); self.lookup_map.entry(rid).or_insert(lookup);
} }
} }
fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) { fn store_ty_regions(&mut self, ty: &Ty<'tcx>, ty_context: TyContext) {
for region in ty.regions() { for region in ty.regions() {
self.store_region(region, lookup); self.store_region(region, ty_context);
} }
} }
fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) { fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) {
if let Some(ty) = kind.as_type() { if let Some(ty) = kind.as_type() {
self.store_ty_regions(&ty, lookup); self.store_ty_regions(&ty, ty_context);
} else if let Some(region) = kind.as_region() { } else if let Some(region) = kind.as_region() {
self.store_region(region, lookup); self.store_region(region, ty_context);
} }
} }
fn is_argument_or_return_slot(&self, local: Local) -> bool {
// The first argument is return slot, next N are arguments.
local.index() <= self.arg_count
}
} }
impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
let is_arg = match ty_context {
TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
_ => false,
};
let old_ty = *ty; let old_ty = *ty;
*ty = self.renumber_regions(&old_ty); *ty = if is_arg {
self.store_ty_regions(ty, lookup); self.renumber_free_regions(&old_ty)
} else {
self.renumber_regions(&old_ty)
};
self.store_ty_regions(ty, ty_context);
} }
fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
*substs = self.renumber_regions(&{*substs}); *substs = self.renumber_regions(&{ *substs });
let lookup = Lookup::Loc(location); let ty_context = TyContext::Location(location);
for kind in *substs { for kind in *substs {
self.store_kind_regions(kind, lookup); self.store_kind_regions(kind, ty_context);
} }
} }
@ -91,8 +141,8 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
Rvalue::Ref(ref mut r, _, _) => { Rvalue::Ref(ref mut r, _, _) => {
let old_r = *r; let old_r = *r;
*r = self.renumber_regions(&old_r); *r = self.renumber_regions(&old_r);
let lookup = Lookup::Loc(location); let ty_context = TyContext::Location(location);
self.store_region(r, lookup); self.store_region(r, ty_context);
} }
Rvalue::Use(..) | Rvalue::Use(..) |
Rvalue::Repeat(..) | Rvalue::Repeat(..) |
@ -110,20 +160,20 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
self.super_rvalue(rvalue, location); self.super_rvalue(rvalue, location);
} }
fn visit_closure_substs(&mut self, fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
substs: &mut ClosureSubsts<'tcx>,
location: Location) {
*substs = self.renumber_regions(substs); *substs = self.renumber_regions(substs);
let lookup = Lookup::Loc(location); let ty_context = TyContext::Location(location);
for kind in substs.substs { for kind in substs.substs {
self.store_kind_regions(kind, lookup); self.store_kind_regions(kind, ty_context);
} }
} }
fn visit_statement(&mut self, fn visit_statement(
block: BasicBlock, &mut self,
statement: &mut Statement<'tcx>, block: BasicBlock,
location: Location) { statement: &mut Statement<'tcx>,
location: Location,
) {
if let StatementKind::EndRegion(_) = statement.kind { if let StatementKind::EndRegion(_) = statement.kind {
statement.kind = StatementKind::Nop; statement.kind = StatementKind::Nop;
} }

View file

@ -92,8 +92,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
self.sanitize_type(rvalue, rval_ty); self.sanitize_type(rvalue, rval_ty);
} }
fn visit_local_decl(&mut self, local_decl: &LocalDecl<'tcx>) { fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
self.super_local_decl(local_decl); self.super_local_decl(local, local_decl);
self.sanitize_type(local_decl, local_decl.ty); self.sanitize_type(local_decl, local_decl.ty);
} }

View file

@ -14,7 +14,7 @@
use rustc_const_math::{ConstUsize}; use rustc_const_math::{ConstUsize};
use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData}; use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData};
use rustc::mir::{Constant, Literal, Location, LocalDecl}; use rustc::mir::{Constant, Literal, Location, Local, LocalDecl};
use rustc::mir::{Lvalue, LvalueElem, LvalueProjection}; use rustc::mir::{Lvalue, LvalueElem, LvalueProjection};
use rustc::mir::{Mir, Operand, ProjectionElem}; use rustc::mir::{Mir, Operand, ProjectionElem};
use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind}; use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind};
@ -270,9 +270,10 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
} }
fn visit_local_decl(&mut self, fn visit_local_decl(&mut self,
local: Local,
local_decl: &LocalDecl<'tcx>) { local_decl: &LocalDecl<'tcx>) {
self.record("LocalDecl", local_decl); self.record("LocalDecl", local_decl);
self.super_local_decl(local_decl); self.super_local_decl(local, local_decl);
} }
fn visit_visibility_scope(&mut self, fn visit_visibility_scope(&mut self,

View file

@ -0,0 +1,34 @@
// Copyright 2012-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 named lifetime translation. Check that we
// instantiate the types that appear in function arguments with
// suitable variables and that we setup the outlives relationship
// between R0 and R1 properly.
// compile-flags:-Znll -Zverbose
// ^^^^^^^^^ force compiler to dump more region information
// ignore-tidy-linelength
#![allow(warnings)]
fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { true }
fn main() {
}
// END RUST SOURCE
// START rustc.node4.nll.0.mir
// | '_#0r: {bb0[0], bb0[1], '_#0r}
// | '_#1r: {bb0[0], bb0[1], '_#0r, '_#1r}
// | '_#2r: {bb0[0], bb0[1], '_#2r}
// ...
// fn use_x(_1: &'_#0r mut i32, _2: &'_#1r u32, _3: &'_#0r u32, _4: &'_#2r u32) -> bool {
// END rustc.node4.nll.0.mir

View file

@ -0,0 +1,22 @@
// 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
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
&*x
}
fn main() { }

View file

@ -0,0 +1,29 @@
warning: not reporting region error due to -Znll
--> $DIR/named-region-basic.rs:19:5
|
19 | &*x
| ^^^
error[E0597]: `*x` does not live long enough
--> $DIR/named-region-basic.rs:19:6
|
19 | &*x
| ^^ does not live long enough
|
= note: borrowed value must be valid for the static lifetime...
note: ...but borrowed value is only valid for the lifetime 'a as defined on the function body at 18:1
--> $DIR/named-region-basic.rs:18:1
|
18 | / fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
19 | | &*x
20 | | }
| |_^
error: free region `'a` does not outlive `'b`
--> $DIR/named-region-basic.rs:19:5
|
19 | &*x
| ^^^
error: aborting due to 2 previous errors