diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 895894a0bb2fc..5d0a6ae98d7a6 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -262,6 +262,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { errors: &Vec>) { 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); diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 49a241b86e015..3bcdc4f7e2c63 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -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 { diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 63652980f9b4b..2d9d3643ff69d 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -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); } @@ -538,7 +540,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, @@ -560,7 +562,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, @@ -568,7 +570,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 => { } @@ -638,7 +640,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, @@ -668,7 +670,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); @@ -683,6 +685,7 @@ macro_rules! make_mir_visitor { } fn super_local_decl(&mut self, + local: Local, local_decl: & $($mutability)* LocalDecl<'tcx>) { let LocalDecl { mutability: _, @@ -694,7 +697,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); } @@ -718,7 +724,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); } @@ -796,10 +802,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)] diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index e8f9a6720872d..94edaa746f915 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -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 { 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 { diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs index 7cb386b019798..933e08811ce5d 100644 --- a/src/librustc_data_structures/transitive_relation.rs +++ b/src/librustc_data_structures/transitive_relation.rs @@ -134,12 +134,12 @@ impl TransitiveRelation { } } - /// 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`, 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() diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 77496c7b8f218..2073d49530061 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -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 { diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index 17aa8c054181e..e8bf543b70b6a 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { location_map: FxHashMap, region_map: FxHashMap, FxHashSet>, region_span_map: FxHashMap, - 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); + } } } } diff --git a/src/librustc_mir/transform/clean_end_regions.rs b/src/librustc_mir/transform/clean_end_regions.rs index a6750f400ba93..d356d3b5a8508 100644 --- a/src/librustc_mir/transform/clean_end_regions.rs +++ b/src/librustc_mir/transform/clean_end_regions.rs @@ -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 { diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index dc18cdd8f0dd6..08ca20e50eb7f 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -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); } diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index a7570c610d8d3..a2f9bbb174eb7 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -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()); } } diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs new file mode 100644 index 0000000000000..006a2f9047ad9 --- /dev/null +++ b/src/librustc_mir/transform/nll/free_regions.rs @@ -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 or the MIT license +// , 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, 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, usize>, + region: ty::Region<'tcx>, +) { + let len = free_regions.len(); + free_regions.entry(region).or_insert(len); +} diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index d4938dc40bf25..e24def2292ec6 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -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, -} - -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", }); diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index c23d73e784ae9..553d5ad4a320a 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -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, + definitions: IndexVec>, + + /// 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, /// The constraints we have accumulated and used during solving. constraints: Vec, +} + +#[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>, + + /// 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, +/// 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, + free_regions: BTreeSet, } -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 { 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 - 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( @@ -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); } diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs index 589179c206685..a3ff7a041ca07 100644 --- a/src/librustc_mir/transform/nll/renumber.rs +++ b/src/librustc_mir/transform/nll/renumber.rs @@ -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, + lookup_map: HashMap, num_region_variables: usize, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + free_regions: &'a FreeRegions<'tcx>, + free_region_inference_vars: Vec>, + 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(&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(&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(&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); } } @@ -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; } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index ab5998a34805b..d238b145d424c 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -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); } diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 1fa49614580a3..3f75c6385223e 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -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}; @@ -269,9 +269,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, diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs new file mode 100644 index 0000000000000..c4f3a6fea5353 --- /dev/null +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/ui/nll/named-region-basic.rs b/src/test/ui/nll/named-region-basic.rs new file mode 100644 index 0000000000000..539c2017ea6c0 --- /dev/null +++ b/src/test/ui/nll/named-region-basic.rs @@ -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 or the MIT license +// , 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() { } diff --git a/src/test/ui/nll/named-region-basic.stderr b/src/test/ui/nll/named-region-basic.stderr new file mode 100644 index 0000000000000..42b2aea01f0df --- /dev/null +++ b/src/test/ui/nll/named-region-basic.stderr @@ -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 +