errors: &Vec<RegionResolutionError<'tcx>>) {
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
// together into a `ProcessedErrors` group:
let errors = self.process_errors(errors);
debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, 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 visit_ty(&mut self,
ty: & $($mutability)* Ty<'tcx>,
- _: Lookup) {
+ _: TyContext) {
self.super_ty(ty);
}
}
fn visit_local_decl(&mut self,
+ local: Local,
local_decl: & $($mutability)* LocalDecl<'tcx>) {
- self.super_local_decl(local_decl);
+ self.super_local_decl(local, local_decl);
}
fn visit_local(&mut self,
self.visit_visibility_scope_data(scope);
}
- let lookup = Lookup::Src(SourceInfo {
+ let lookup = TyContext::SourceInfo(SourceInfo {
span: mir.span,
scope: ARGUMENT_VISIBILITY_SCOPE,
});
self.visit_ty(&$($mutability)* mir.return_ty, lookup);
- for local_decl in &$($mutability)* mir.local_decls {
- self.visit_local_decl(local_decl);
+ for local in mir.local_decls.indices() {
+ self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]);
}
self.visit_span(&$($mutability)* mir.span);
for operand in lvalues {
self.visit_lvalue(& $($mutability)* operand.lval,
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, .. } => {
ref values,
ref targets } => {
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[..] {
self.visit_const_int(value, source_location);
}
ref $($mutability)* operand,
ref $($mutability)* ty) => {
self.visit_operand(operand, location);
- self.visit_ty(ty, Lookup::Loc(location));
+ self.visit_ty(ty, TyContext::Location(location));
}
Rvalue::BinaryOp(_bin_op,
}
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,
let kind = &$($mutability)* **kind;
match *kind {
AggregateKind::Array(ref $($mutability)* ty) => {
- self.visit_ty(ty, Lookup::Loc(location));
+ self.visit_ty(ty, TyContext::Location(location));
}
AggregateKind::Tuple => {
}
ref $($mutability)* ty,
} = *static_;
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,
ProjectionElem::Subslice { from: _, to: _ } => {
}
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) => {
self.visit_local(local, LvalueContext::Consume, location);
}
fn super_local_decl(&mut self,
+ local: Local,
local_decl: & $($mutability)* LocalDecl<'tcx>) {
let LocalDecl {
mutability: _,
is_user_variable: _,
} = *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_visibility_scope(lexical_scope);
}
} = *constant;
self.visit_span(span);
- self.visit_ty(ty, Lookup::Loc(location));
+ self.visit_ty(ty, TyContext::Location(location));
self.visit_literal(literal, location);
}
make_mir_visitor!(Visitor,);
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)]
-pub enum Lookup {
- Loc(Location),
- Src(SourceInfo),
+pub enum TyContext {
+ LocalDecl {
+ /// 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)]
}
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 {
// For every element, we need one bit for every other
// element. Round up to an even number of u64s.
(start, start + u64s_per_row)
}
- pub fn add(&mut self, source: usize, target: usize) -> bool {
- let (start, _) = self.range(source);
- let (word, mask) = word_mask(target);
+ /// Sets the cell at `(row, column)` to true. Put another way, add
+ /// `column` to the bitset for `row`.
+ ///
+ /// 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 v1 = vector[start + word];
let v2 = v1 | mask;
v1 != v2
}
- /// Do the bits from `source` contain `target`?
- ///
- /// Put another way, if the matrix represents (transitive)
- /// reachability, can `source` reach `target`?
- pub fn contains(&self, source: usize, target: usize) -> bool {
- let (start, _) = self.range(source);
- let (word, mask) = word_mask(target);
+ /// Do the bits from `row` contain `column`? Put another way, is
+ /// the matrix cell at `(row, column)` true? Put yet another way,
+ /// if the matrix represents (transitive) reachability, can
+ /// `row` reach `column`?
+ pub fn contains(&self, row: usize, column: usize) -> bool {
+ let (start, _) = self.range(row);
+ let (word, mask) = word_mask(column);
(self.vector[start + word] & mask) != 0
}
- /// Returns those indices that are reachable from both `a` and
- /// `b`. This is an O(n) operation where `n` is the number of
- /// elements (somewhat independent from the actual size of the
+ /// Returns those indices that are true in rows `a` and `b`. This
+ /// is an O(n) operation where `n` is the number of elements
+ /// (somewhat independent from the actual size of the
/// intersection, in particular).
pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> {
let (a_start, a_end) = self.range(a);
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.
///
/// This is used when computing transitive reachability because if
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> {
let (start, end) = self.range(row);
BitVectorIter {
}
}
- /// 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
/// I'm too lazy to make that work, and -- given the caching
/// 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) {
Some(a) => self.with_closure(|closure| {
closure.iter(a.0).map(|i| &self.elements[i]).collect()
use rustc::middle::region;
use rustc::mir::*;
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::subst::Substs;
use rustc::util::nodemap::NodeMap;
}
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) {
*ty = lifted;
} else {
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
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
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
- nonlexical_regioncx: Option<&'a RegionInferenceContext>)
+ nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
-> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(),
location: Location) {
if let Some(regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
- let borrow_region = regioncx.region_value(borrow_data.region.to_region_index());
- if !borrow_region.may_contain(location) && location != borrow_data.location {
- debug!("kill_loans_out_of_scope_at_location: kill{:?} \
- location={:?} borrow_data={:?}", borrow_index, location, borrow_data);
- sets.kill(&borrow_index);
+ let borrow_region = borrow_data.region.to_region_index();
+ if !regioncx.region_contains_point(borrow_region, location) {
+ // The region checker really considers the borrow
+ // to start at the point **after** the location of
+ // 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);
+ }
}
}
}
use rustc::middle::region;
use rustc::mir::transform::{MirPass, MirSource};
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};
pub struct CleanEndRegions;
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
for re in ty.walk().flat_map(|t| t.regions()) {
match *re {
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::mir::*;
-use rustc::mir::visit::{MutVisitor, Lookup};
+use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::mir::transform::{MirPass, MirSource};
struct EraseRegionsVisitor<'a, 'tcx: 'a> {
}
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 {
*ty = self.tcx.erase_regions(ty);
}
pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
- regioncx: &mut RegionInferenceContext,
+ regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>,
mir_source: MirSource,
liveness: &LivenessResults,
struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
- regioncx: &'cx mut RegionInferenceContext,
+ regioncx: &'cx mut RegionInferenceContext<'tcx>,
mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults,
mir_source: MirSource,
_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 {
_ => 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(),
location.successor_within_block());
}
},
}
- 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(),
location.successor_within_block());
}
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(a, b, location.successor_within_block());
+ self.regioncx.add_outlives(span, a, b, location.successor_within_block());
}
}
--- /dev/null
+// 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);
+}
// except according to those terms.
use rustc::ty::{self, RegionKind};
-use rustc::mir::{Location, Mir};
+use rustc::mir::Mir;
use rustc::mir::transform::MirSource;
use rustc::infer::InferCtxt;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use std::collections::BTreeSet;
-use std::fmt;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
use util as mir_util;
use self::mir_util::PassWhere;
mod constraint_generation;
+mod free_regions;
mod subtype;
pub(crate) mod region_infer;
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource,
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.
- 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.
let liveness = &LivenessResults {
// Create the region inference context, generate the constraints,
// 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);
- let errors = regioncx.solve(infcx, &mir);
-
- assert!(errors.is_empty(), "FIXME: report region inference failures");
+ regioncx.solve(infcx, &mir);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests.
});
}
-#[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 {
DEBUG_FORMAT = "'_#{}r",
});
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use super::{Region, RegionIndex};
-use std::mem;
+use super::RegionIndex;
+use super::free_regions::FreeRegions;
use rustc::infer::InferCtxt;
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 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
/// variables are identified by their index (`RegionIndex`). The
/// definition contains information about where the region came
/// 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.
constraints: Vec<Constraint>,
+}
+
+#[derive(Default)]
+struct RegionDefinition<'tcx> {
+ /// 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,
+}
- /// List of errors we have accumulated as we add constraints.
- /// After solving is done, this is replaced with an empty vector.
- errors: Vec<InferenceError>,
+/// 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>,
}
-pub struct InferenceError {
- pub constraint_point: Location,
- pub name: (), // FIXME(nashenas88) RegionName
+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()
+ }
}
-#[derive(Default)]
-struct RegionDefinition {
- name: (), // FIXME(nashenas88) RegionName
- value: Region,
- capped: bool,
+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)]
pub struct Constraint {
- sub: RegionIndex,
+ /// Where did this constraint arise?
+ span: Span,
+
+ /// The region SUP must outlive SUB...
sup: RegionIndex,
+
+ /// Region that must be outlived.
+ sub: RegionIndex,
+
+ /// At this location.
point: Location,
}
-impl RegionInferenceContext {
- pub fn new(num_region_variables: usize) -> Self {
- Self {
+impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
+ /// Creates a new region inference context with a total of
+ /// `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)
.map(|_| RegionDefinition::default())
.collect(),
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.
pub fn regions(&self) -> impl Iterator<Item = RegionIndex> {
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.
- pub fn region_value(&self, r: RegionIndex) -> &Region {
- &self.definitions[r].value
+ pub fn region_contains_point(&self, r: RegionIndex, p: Location) -> bool {
+ self.definitions[r].value.contains_point(p)
}
- /// Flags a region as being "capped" -- this means that if its
- /// 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;
+ /// Returns access to the value of `r` for debugging purposes.
+ pub(super) fn region_value(&self, r: RegionIndex) -> &fmt::Debug {
+ &self.definitions[r].value
}
+ /// Indicates that the region variable `v` is live at the point `point`.
pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
debug!("add_live_point({:?}, {:?})", v, point);
let definition = &mut self.definitions[v];
- if definition.value.add_point(point) {
- if definition.capped {
- self.errors.push(InferenceError {
- constraint_point: point,
- name: definition.name,
- });
- }
+ if !definition.constant {
+ definition.value.add_point(point);
+ } else {
+ // Constants are used for free regions, which already
+ // 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);
- self.constraints.push(Constraint { sup, sub, point });
+ self.constraints.push(Constraint {
+ span,
+ sup,
+ sub,
+ point,
+ });
}
/// 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,
- infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
- mir: &'a Mir<'tcx>,
- ) -> Vec<InferenceError>
- where
- 'gcx: 'tcx + 'a,
- 'tcx: 'a,
- {
+ mir: &Mir<'tcx>,
+ ) -> Vec<(RegionIndex, Span, RegionIndex)> {
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 {
changed = false;
for constraint in &self.constraints {
+ debug!("constraint: {:?}", constraint);
let sub = &self.definitions[constraint.sub].value.clone();
let sup_def = &mut self.definitions[constraint.sup];
- debug!("constraint: {:?}", constraint);
+
debug!(" sub (before): {:?}", sub);
debug!(" sup (before): {:?}", sup_def.value);
- if dfs.copy(sub, &mut sup_def.value, constraint.point) {
- changed = true;
- if sup_def.capped {
- // 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 {
- constraint_point: p,
- name: sup_def.name,
- });
+ if !sup_def.constant {
+ // If this is not a constant, then grow the value as needed to
+ // accommodate the outlives constraint.
+
+ if dfs.copy(sub, &mut sup_def.value, constraint.point) {
+ changed = true;
}
- }
- debug!(" sup (after) : {:?}", sup_def.value);
- debug!(" changed : {:?}", changed);
+ 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!("\n");
}
-
- mem::replace(&mut self.errors, Vec::new())
+ errors
}
}
-struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
- #[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+struct Dfs<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
}
-impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
- fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
- Self { infcx, mir }
+impl<'a, 'tcx> Dfs<'a, 'tcx> {
+ fn new(mir: &'a Mir<'tcx>) -> Self {
+ Self { mir }
}
fn copy(
while let Some(p) = stack.pop() {
debug!(" dfs: p={:?}", p);
- if !from_region.may_contain(p) {
+ if !from_region.contains_point(p) {
debug!(" not in from-region");
continue;
}
};
if successor_points.is_empty() {
- // FIXME handle free regions
// If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_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
- // let block = self.env.graph.skolemized_end(region_decl.name);
- // let skolemized_end_point = Location {
- // block,
- // statement_index: 0,
- // };
- // changed |= to_region.add_point(skolemized_end_point);
- // }
+
+ debug!(" dfs: free_regions={:?}", from_region.free_regions);
+ for &fr in &from_region.free_regions {
+ changed |= to_region.free_regions.insert(fr);
+ }
} else {
stack.extend(successor_points);
}
// option. This file may not be copied, modified, or distributed
// 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::{Ty, ClosureSubsts, RegionVid, RegionKind};
-use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
-use rustc::mir::visit::{MutVisitor, Lookup};
+use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable};
+use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
+use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{self as rustc_infer, InferCtxt};
use syntax_pos::DUMMY_SP;
use std::collections::HashMap;
+use super::free_regions::FreeRegions;
+
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
-pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
- mir: &mut Mir<'tcx>)
- -> usize
-{
- let mut visitor = NLLVisitor::new(infcx);
+pub fn renumber_mir<'a, 'gcx, 'tcx>(
+ infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+ free_regions: &FreeRegions<'tcx>,
+ mir: &mut Mir<'tcx>,
+) -> 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.num_region_variables
}
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
- lookup_map: HashMap<RegionVid, Lookup>,
+ lookup_map: HashMap<RegionVid, TyContext>,
num_region_variables: usize,
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> {
- pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
- NLLVisitor {
- infcx,
- lookup_map: HashMap::new(),
- num_region_variables: 0
- }
+ /// Replaces all regions appearing in `value` with fresh inference
+ /// variables. This is what we do for almost the entire MIR, with
+ /// the exception of the declared types of our arguments.
+ fn renumber_regions<T>(&mut self, value: &T) -> T
+ 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> {
- 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))
- })
+ /// Renumbers the regions appearing in `value`, but those regions
+ /// are expected to be free regions from the function signature.
+ fn renumber_free_regions<T>(&mut self, value: &T) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ self.infcx
+ .tcx
+ .fold_regions(value, &mut false, |region, _depth| {
+ let index = self.free_regions.indices[®ion];
+ 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 {
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() {
- 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() {
- self.store_ty_regions(&ty, lookup);
+ self.store_ty_regions(&ty, ty_context);
} 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> {
- 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;
- *ty = self.renumber_regions(&old_ty);
- self.store_ty_regions(ty, lookup);
+ *ty = if is_arg {
+ 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) {
- *substs = self.renumber_regions(&{*substs});
- let lookup = Lookup::Loc(location);
+ *substs = self.renumber_regions(&{ *substs });
+ let ty_context = TyContext::Location(location);
for kind in *substs {
- self.store_kind_regions(kind, lookup);
+ self.store_kind_regions(kind, ty_context);
}
}
Rvalue::Ref(ref mut r, _, _) => {
let old_r = *r;
*r = self.renumber_regions(&old_r);
- let lookup = Lookup::Loc(location);
- self.store_region(r, lookup);
+ let ty_context = TyContext::Location(location);
+ self.store_region(r, ty_context);
}
Rvalue::Use(..) |
Rvalue::Repeat(..) |
self.super_rvalue(rvalue, location);
}
- fn visit_closure_substs(&mut self,
- substs: &mut ClosureSubsts<'tcx>,
- location: Location) {
+ fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
*substs = self.renumber_regions(substs);
- let lookup = Lookup::Loc(location);
+ let ty_context = TyContext::Location(location);
for kind in substs.substs {
- self.store_kind_regions(kind, lookup);
+ self.store_kind_regions(kind, ty_context);
}
}
- fn visit_statement(&mut self,
- block: BasicBlock,
- statement: &mut Statement<'tcx>,
- location: Location) {
+ fn visit_statement(
+ &mut self,
+ block: BasicBlock,
+ statement: &mut Statement<'tcx>,
+ location: Location,
+ ) {
if let StatementKind::EndRegion(_) = statement.kind {
statement.kind = StatementKind::Nop;
}
self.sanitize_type(rvalue, rval_ty);
}
- fn visit_local_decl(&mut self, local_decl: &LocalDecl<'tcx>) {
- self.super_local_decl(local_decl);
+ fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
+ self.super_local_decl(local, local_decl);
self.sanitize_type(local_decl, local_decl.ty);
}
use rustc_const_math::{ConstUsize};
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::{Mir, Operand, ProjectionElem};
use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind};
}
fn visit_local_decl(&mut self,
+ local: Local,
local_decl: &LocalDecl<'tcx>) {
self.record("LocalDecl", local_decl);
- self.super_local_decl(local_decl);
+ self.super_local_decl(local, local_decl);
}
fn visit_visibility_scope(&mut self,
--- /dev/null
+// 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
--- /dev/null
+// 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() { }
--- /dev/null
+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
+