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>>) {
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);

View file

@ -182,6 +182,19 @@ impl<'tcx> FreeRegionMap<'tcx> {
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 {

View file

@ -209,7 +209,7 @@ macro_rules! make_mir_visitor {
fn visit_ty(&mut self,
ty: & $($mutability)* Ty<'tcx>,
_: Lookup) {
_: TyContext) {
self.super_ty(ty);
}
@ -256,8 +256,9 @@ macro_rules! make_mir_visitor {
}
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,
@ -291,14 +292,14 @@ macro_rules! make_mir_visitor {
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);
@ -359,7 +360,8 @@ macro_rules! make_mir_visitor {
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, .. } => {
@ -421,7 +423,7 @@ macro_rules! make_mir_visitor {
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);
}
@ -545,7 +547,7 @@ macro_rules! make_mir_visitor {
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,
@ -567,7 +569,7 @@ macro_rules! make_mir_visitor {
}
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,
@ -575,7 +577,7 @@ macro_rules! make_mir_visitor {
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 => {
}
@ -645,7 +647,7 @@ macro_rules! make_mir_visitor {
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,
@ -675,7 +677,7 @@ macro_rules! make_mir_visitor {
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);
@ -690,6 +692,7 @@ macro_rules! make_mir_visitor {
}
fn super_local_decl(&mut self,
local: Local,
local_decl: & $($mutability)* LocalDecl<'tcx>) {
let LocalDecl {
mutability: _,
@ -701,7 +704,10 @@ macro_rules! make_mir_visitor {
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);
}
@ -725,7 +731,7 @@ macro_rules! make_mir_visitor {
} = *constant;
self.visit_span(span);
self.visit_ty(ty, Lookup::Loc(location));
self.visit_ty(ty, TyContext::Location(location));
self.visit_literal(literal, location);
}
@ -803,10 +809,21 @@ macro_rules! make_mir_visitor {
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)]

View file

@ -145,7 +145,7 @@ pub struct 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 {
// For every element, we need one bit for every other
// element. Round up to an even number of u64s.
@ -163,9 +163,13 @@ impl BitMatrix {
(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;
@ -173,19 +177,19 @@ impl BitMatrix {
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);
@ -206,7 +210,7 @@ impl BitMatrix {
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
@ -227,6 +231,8 @@ impl BitMatrix {
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 {

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
/// 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()

View file

@ -17,7 +17,7 @@ use rustc::hir::def_id::DefId;
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;
@ -165,7 +165,7 @@ struct GlobalizeMir<'a, 'gcx: 'a> {
}
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 {

View file

@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
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
@ -69,7 +69,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
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(),
@ -139,11 +139,21 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
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);
}
}
}
}

View file

@ -24,7 +24,7 @@ use rustc_data_structures::fx::FxHashSet;
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;
@ -67,7 +67,7 @@ impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
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 {

View file

@ -17,7 +17,7 @@
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> {
@ -35,7 +35,7 @@ impl<'a, 'tcx> 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 {
*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>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext,
regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>,
mir_source: MirSource,
liveness: &LivenessResults,
@ -45,7 +45,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
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,
@ -191,6 +191,7 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
_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 {
@ -198,7 +199,8 @@ impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
_ => 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());
}
@ -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(),
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 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());
}
}

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.
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;
@ -36,9 +36,12 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
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 {
@ -61,11 +64,9 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// 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.
@ -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 {
DEBUG_FORMAT = "'_#{}r",
});

View file

@ -8,161 +8,328 @@
// 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>,
/// 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)]
struct RegionDefinition {
name: (), // FIXME(nashenas88) RegionName
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,
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)]
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 {
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
}
/// 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;
}
/// 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,
};
if !sup_def.constant {
// If this is not a constant, then grow the value as needed to
// accommodate the outlives constraint.
self.errors.push(InferenceError {
constraint_point: p,
name: sup_def.name,
});
if dfs.copy(sub, &mut sup_def.value, constraint.point) {
changed = true;
}
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");
}
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(
@ -180,7 +347,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
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;
}
@ -215,19 +382,14 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
};
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);
}

View file

@ -8,81 +8,131 @@
// 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[&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 {
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);
}
}
@ -91,8 +141,8 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
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(..) |
@ -110,20 +160,20 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
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;
}

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);
}
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);
}

View file

@ -14,7 +14,7 @@
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};
@ -270,9 +270,10 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
}
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,

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