Auto merge of #50593 - nikomatsakis:nll-no-location, r=nikomatsakis
stop considering location when computing outlives relationships This doesn't (yet?) use SEME regions, but it does ignore the location for outlives constraints. This makes (I believe) NLL significantly faster -- but we should do some benchmarks. It regresses the "get-default" family of use cases for NLL, which is a shame, but keeps the other benefits, and thus represents a decent step forward. r? @pnkfelix
This commit is contained in:
commit
612ca14b81
|
@ -8,11 +8,11 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::marker::PhantomData;
|
||||
use std::iter::FromIterator;
|
||||
use indexed_vec::{Idx, IndexVec};
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::BTreeMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
type Word = u128;
|
||||
const WORD_BITS: usize = 128;
|
||||
|
@ -317,14 +317,25 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
|
|||
if read != write {
|
||||
let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write);
|
||||
|
||||
for read_val in bit_set_read.iter() {
|
||||
changed = changed | bit_set_write.insert(read_val);
|
||||
for read_chunk in bit_set_read.chunks() {
|
||||
changed = changed | bit_set_write.insert_chunk(read_chunk).any();
|
||||
}
|
||||
}
|
||||
|
||||
changed
|
||||
}
|
||||
|
||||
/// True if `sub` is a subset of `sup`
|
||||
pub fn is_subset(&self, sub: R, sup: R) -> bool {
|
||||
sub == sup || {
|
||||
let bit_set_sub = &self.vector[sub];
|
||||
let bit_set_sup = &self.vector[sup];
|
||||
bit_set_sub
|
||||
.chunks()
|
||||
.all(|read_chunk| read_chunk.bits_eq(bit_set_sup.contains_chunk(read_chunk)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates through all the columns set to true in a given row of
|
||||
/// the matrix.
|
||||
pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
|
||||
|
@ -346,6 +357,7 @@ pub struct SparseChunk<I> {
|
|||
}
|
||||
|
||||
impl<I: Idx> SparseChunk<I> {
|
||||
#[inline]
|
||||
pub fn one(index: I) -> Self {
|
||||
let index = index.index();
|
||||
let key_usize = index / 128;
|
||||
|
@ -358,10 +370,16 @@ impl<I: Idx> SparseChunk<I> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn any(&self) -> bool {
|
||||
self.bits != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bits_eq(&self, other: SparseChunk<I>) -> bool {
|
||||
self.bits == other.bits
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = I> {
|
||||
let base = self.key as usize * 128;
|
||||
let mut bits = self.bits;
|
||||
|
@ -394,6 +412,10 @@ impl<I: Idx> SparseBitSet<I> {
|
|||
self.chunk_bits.len() * 128
|
||||
}
|
||||
|
||||
/// Returns a chunk containing only those bits that are already
|
||||
/// present. You can test therefore if `self` contains all the
|
||||
/// bits in chunk already by doing `chunk ==
|
||||
/// self.contains_chunk(chunk)`.
|
||||
pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> {
|
||||
SparseChunk {
|
||||
bits: self.chunk_bits
|
||||
|
@ -403,6 +425,11 @@ impl<I: Idx> SparseBitSet<I> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Modifies `self` to contain all the bits from `chunk` (in
|
||||
/// addition to any pre-existing bits); returns a new chunk that
|
||||
/// contains only those bits that were newly added. You can test
|
||||
/// if anything was inserted by invoking `any()` on the returned
|
||||
/// value.
|
||||
pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
|
||||
if chunk.bits == 0 {
|
||||
return chunk;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
//! This query borrow-checks the MIR to (further) ensure it is not broken.
|
||||
|
||||
use borrow_check::nll::region_infer::{RegionCausalInfo, RegionInferenceContext};
|
||||
use borrow_check::nll::region_infer::RegionInferenceContext;
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
|
@ -248,7 +248,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||
nonlexical_regioncx: regioncx,
|
||||
used_mut: FxHashSet(),
|
||||
used_mut_upvars: SmallVec::new(),
|
||||
nonlexical_cause_info: None,
|
||||
borrow_set,
|
||||
dominators,
|
||||
};
|
||||
|
@ -367,7 +366,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
|||
/// contains the results from region inference and lets us e.g.
|
||||
/// find out which CFG points are contained in each borrow region.
|
||||
nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
|
||||
nonlexical_cause_info: Option<RegionCausalInfo>,
|
||||
|
||||
/// The set of borrows extracted from the MIR
|
||||
borrow_set: Rc<BorrowSet<'tcx>>,
|
||||
|
|
|
@ -32,13 +32,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||
let regioncx = &&self.nonlexical_regioncx;
|
||||
let mir = self.mir;
|
||||
|
||||
if self.nonlexical_cause_info.is_none() {
|
||||
self.nonlexical_cause_info = Some(regioncx.compute_causal_info(mir));
|
||||
}
|
||||
|
||||
let cause_info = self.nonlexical_cause_info.as_ref().unwrap();
|
||||
if let Some(cause) = cause_info.why_region_contains_point(borrow.region, context.loc) {
|
||||
match *cause.root_cause() {
|
||||
let borrow_region_vid = regioncx.to_region_vid(borrow.region);
|
||||
if let Some(cause) = regioncx.why_region_contains_point(borrow_region_vid, context.loc) {
|
||||
match cause {
|
||||
Cause::LiveVar(local, location) => {
|
||||
match find_regular_use(mir, regioncx, borrow, location, local) {
|
||||
Some(p) => {
|
||||
|
|
|
@ -1,265 +0,0 @@
|
|||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Module defining the `dfs` method on `RegionInferenceContext`, along with
|
||||
//! its associated helper traits.
|
||||
|
||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||
use borrow_check::nll::region_infer::RegionInferenceContext;
|
||||
use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements,
|
||||
RegionValues};
|
||||
use syntax::codemap::Span;
|
||||
use rustc::mir::{Location, Mir};
|
||||
use rustc::ty::RegionVid;
|
||||
use rustc_data_structures::bitvec::BitVector;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
pub(super) struct DfsStorage {
|
||||
stack: Vec<Location>,
|
||||
visited: BitVector,
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Creates dfs storage for use by dfs; this should be shared
|
||||
/// across as many calls to dfs as possible to amortize allocation
|
||||
/// costs.
|
||||
pub(super) fn new_dfs_storage(&self) -> DfsStorage {
|
||||
let num_elements = self.elements.num_elements();
|
||||
DfsStorage {
|
||||
stack: vec![],
|
||||
visited: BitVector::new(num_elements),
|
||||
}
|
||||
}
|
||||
|
||||
/// Function used to satisfy or test a `R1: R2 @ P`
|
||||
/// constraint. The core idea is that it performs a DFS starting
|
||||
/// from `P`. The precise actions *during* that DFS depend on the
|
||||
/// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more
|
||||
/// details.
|
||||
///
|
||||
/// Returns:
|
||||
///
|
||||
/// - `Ok(true)` if the walk was completed and something changed
|
||||
/// along the way;
|
||||
/// - `Ok(false)` if the walk was completed with no changes;
|
||||
/// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the
|
||||
/// value that `op` returned.
|
||||
#[inline(never)] // ensure dfs is identifiable in profiles
|
||||
pub(super) fn dfs<C>(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
dfs: &mut DfsStorage,
|
||||
mut op: C,
|
||||
) -> Result<bool, C::Early>
|
||||
where
|
||||
C: DfsOp,
|
||||
{
|
||||
let mut changed = false;
|
||||
|
||||
dfs.visited.clear();
|
||||
dfs.stack.push(op.start_point());
|
||||
while let Some(p) = dfs.stack.pop() {
|
||||
let point_index = self.elements.index(p);
|
||||
|
||||
if !op.source_region_contains(point_index) {
|
||||
debug!(" not in from-region");
|
||||
continue;
|
||||
}
|
||||
|
||||
if !dfs.visited.insert(point_index.index()) {
|
||||
debug!(" already visited");
|
||||
continue;
|
||||
}
|
||||
|
||||
let new = op.add_to_target_region(point_index)?;
|
||||
changed |= new;
|
||||
|
||||
let block_data = &mir[p.block];
|
||||
|
||||
let start_stack_len = dfs.stack.len();
|
||||
|
||||
if p.statement_index < block_data.statements.len() {
|
||||
dfs.stack.push(Location {
|
||||
statement_index: p.statement_index + 1,
|
||||
..p
|
||||
});
|
||||
} else {
|
||||
dfs.stack.extend(
|
||||
block_data
|
||||
.terminator()
|
||||
.successors()
|
||||
.map(|&basic_block| Location {
|
||||
statement_index: 0,
|
||||
block: basic_block,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if dfs.stack.len() == start_stack_len {
|
||||
// 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`.
|
||||
changed |= op.add_universal_regions_outlived_by_source_to_target()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(changed)
|
||||
}
|
||||
}
|
||||
|
||||
/// Customizes the operation of the `dfs` function. This function is
|
||||
/// used during inference to satisfy a `R1: R2 @ P` constraint.
|
||||
pub(super) trait DfsOp {
|
||||
/// If this op stops the walk early, what type does it propagate?
|
||||
type Early;
|
||||
|
||||
/// Returns the point from which to start the DFS.
|
||||
fn start_point(&self) -> Location;
|
||||
|
||||
/// Returns true if the source region contains the given point.
|
||||
fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool;
|
||||
|
||||
/// Adds the given point to the target region, returning true if
|
||||
/// something has changed. Returns `Err` if we should abort the
|
||||
/// walk early.
|
||||
fn add_to_target_region(
|
||||
&mut self,
|
||||
point_index: RegionElementIndex,
|
||||
) -> Result<bool, Self::Early>;
|
||||
|
||||
/// Adds all universal regions in the source region to the target region, returning
|
||||
/// true if something has changed.
|
||||
fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, Self::Early>;
|
||||
}
|
||||
|
||||
/// Used during inference to enforce a `R1: R2 @ P` constraint. For
|
||||
/// each point Q we reach along the DFS, we check if Q is in R2 (the
|
||||
/// "source region"). If not, we stop the walk. Otherwise, we add Q to
|
||||
/// R1 (the "target region") and continue to Q's successors. If we
|
||||
/// reach the end of the graph, then we add any universal regions from
|
||||
/// R2 into R1.
|
||||
pub(super) struct CopyFromSourceToTarget<'v> {
|
||||
pub source_region: RegionVid,
|
||||
pub target_region: RegionVid,
|
||||
pub inferred_values: &'v mut RegionValues,
|
||||
pub constraint_point: Location,
|
||||
pub constraint_span: Span,
|
||||
}
|
||||
|
||||
impl<'v> DfsOp for CopyFromSourceToTarget<'v> {
|
||||
/// We never stop the walk early.
|
||||
type Early = !;
|
||||
|
||||
fn start_point(&self) -> Location {
|
||||
self.constraint_point
|
||||
}
|
||||
|
||||
fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
|
||||
self.inferred_values
|
||||
.contains(self.source_region, point_index)
|
||||
}
|
||||
|
||||
fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result<bool, !> {
|
||||
Ok(self.inferred_values.add_due_to_outlives(
|
||||
self.source_region,
|
||||
self.target_region,
|
||||
point_index,
|
||||
self.constraint_point,
|
||||
self.constraint_span,
|
||||
))
|
||||
}
|
||||
|
||||
fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, !> {
|
||||
Ok(self.inferred_values.add_universal_regions_outlived_by(
|
||||
self.source_region,
|
||||
self.target_region,
|
||||
self.constraint_point,
|
||||
self.constraint_span,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Used after inference to *test* a `R1: R2 @ P` constraint. For
|
||||
/// each point Q we reach along the DFS, we check if Q in R2 is also
|
||||
/// contained in R1. If not, we abort the walk early with an `Err`
|
||||
/// condition. Similarly, if we reach the end of the graph and find
|
||||
/// that R1 contains some universal region that R2 does not contain,
|
||||
/// we abort the walk early.
|
||||
pub(super) struct TestTargetOutlivesSource<'v, 'tcx: 'v> {
|
||||
pub source_region: RegionVid,
|
||||
pub target_region: RegionVid,
|
||||
pub elements: &'v RegionValueElements,
|
||||
pub universal_regions: &'v UniversalRegions<'tcx>,
|
||||
pub inferred_values: &'v RegionValues,
|
||||
pub constraint_point: Location,
|
||||
}
|
||||
|
||||
impl<'v, 'tcx> DfsOp for TestTargetOutlivesSource<'v, 'tcx> {
|
||||
/// The element that was not found within R2.
|
||||
type Early = RegionElementIndex;
|
||||
|
||||
fn start_point(&self) -> Location {
|
||||
self.constraint_point
|
||||
}
|
||||
|
||||
fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
|
||||
self.inferred_values
|
||||
.contains(self.source_region, point_index)
|
||||
}
|
||||
|
||||
fn add_to_target_region(
|
||||
&mut self,
|
||||
point_index: RegionElementIndex,
|
||||
) -> Result<bool, RegionElementIndex> {
|
||||
if !self.inferred_values
|
||||
.contains(self.target_region, point_index)
|
||||
{
|
||||
return Err(point_index);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn add_universal_regions_outlived_by_source_to_target(
|
||||
&mut self,
|
||||
) -> Result<bool, RegionElementIndex> {
|
||||
// For all `ur_in_source` in `source_region`.
|
||||
for ur_in_source in self.inferred_values
|
||||
.universal_regions_outlived_by(self.source_region)
|
||||
{
|
||||
// Check that `target_region` outlives `ur_in_source`.
|
||||
|
||||
// If `ur_in_source` is a member of `target_region`, OK.
|
||||
//
|
||||
// (This is implied by the loop below, actually, just an
|
||||
// irresistible micro-opt. Mm. Premature optimization. So
|
||||
// tasty.)
|
||||
if self.inferred_values
|
||||
.contains(self.target_region, ur_in_source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there is some other element X such that `target_region: X` and
|
||||
// `X: ur_in_source`, OK.
|
||||
if self.inferred_values
|
||||
.universal_regions_outlived_by(self.target_region)
|
||||
.any(|ur_in_target| self.universal_regions.outlives(ur_in_target, ur_in_source))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, not known to be true.
|
||||
return Err(self.elements.index(ur_in_source));
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
}
|
|
@ -9,16 +9,19 @@
|
|||
// except according to those terms.
|
||||
|
||||
use super::universal_regions::UniversalRegions;
|
||||
use borrow_check::nll::region_infer::values::ToElementIndex;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
use rustc::infer::region_constraints::{GenericKind, VarInfos};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::infer::NLLRegionVariableOrigin;
|
||||
use rustc::infer::RegionObligation;
|
||||
use rustc::infer::RegionVariableOrigin;
|
||||
use rustc::infer::SubregionOrigin;
|
||||
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
|
||||
use rustc::infer::region_constraints::{GenericKind, VarInfos};
|
||||
use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
|
||||
Local, Location, Mir};
|
||||
use rustc::mir::{
|
||||
ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
|
||||
Mir,
|
||||
};
|
||||
use rustc::traits::ObligationCause;
|
||||
use rustc::ty::{self, RegionVid, Ty, TypeFoldable};
|
||||
use rustc::util::common::{self, ErrorReported};
|
||||
|
@ -30,8 +33,6 @@ use syntax::ast;
|
|||
use syntax_pos::Span;
|
||||
|
||||
mod annotation;
|
||||
mod dfs;
|
||||
use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource};
|
||||
mod dump_mir;
|
||||
mod graphviz;
|
||||
mod values;
|
||||
|
@ -100,7 +101,7 @@ struct RegionDefinition<'tcx> {
|
|||
/// NB: The variants in `Cause` are intentionally ordered. Lower
|
||||
/// values are preferred when it comes to error messages. Do not
|
||||
/// reorder willy nilly.
|
||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub(crate) enum Cause {
|
||||
/// point inserted because Local was live at the given Location
|
||||
LiveVar(Local, Location),
|
||||
|
@ -114,23 +115,6 @@ pub(crate) enum Cause {
|
|||
|
||||
/// part of the initial set of values for a universally quantified region
|
||||
UniversalRegion(RegionVid),
|
||||
|
||||
/// Element E was added to R because there was some
|
||||
/// outlives obligation `R: R1 @ P` and `R1` contained `E`.
|
||||
Outlives {
|
||||
/// the reason that R1 had E
|
||||
original_cause: Rc<Cause>,
|
||||
|
||||
/// the point P from the relation
|
||||
constraint_location: Location,
|
||||
|
||||
/// The span indicating why we added the outlives constraint.
|
||||
constraint_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) struct RegionCausalInfo {
|
||||
inferred_values: RegionValues,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -325,7 +309,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// Add all nodes in the CFG to liveness constraints
|
||||
for point_index in self.elements.all_point_indices() {
|
||||
self.liveness_constraints.add(
|
||||
self.liveness_constraints.add_element(
|
||||
variable,
|
||||
point_index,
|
||||
&Cause::UniversalRegion(variable),
|
||||
|
@ -333,8 +317,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
|
||||
// Add `end(X)` into the set for X.
|
||||
self.liveness_constraints
|
||||
.add(variable, variable, &Cause::UniversalRegion(variable));
|
||||
self.liveness_constraints.add_element(
|
||||
variable,
|
||||
variable,
|
||||
&Cause::UniversalRegion(variable),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,7 +370,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
debug!("add_live_point: @{:?} Adding cause {:?}", point, cause);
|
||||
|
||||
let element = self.elements.index(point);
|
||||
if self.liveness_constraints.add(v, element, &cause) {
|
||||
if self.liveness_constraints.add_element(v, element, &cause) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -438,9 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
) -> Option<ClosureRegionRequirements<'gcx>> {
|
||||
assert!(self.inferred_values.is_none(), "values already inferred");
|
||||
|
||||
let dfs_storage = &mut self.new_dfs_storage();
|
||||
|
||||
self.propagate_constraints(mir, dfs_storage);
|
||||
self.propagate_constraints(mir);
|
||||
|
||||
// If this is a closure, we can propagate unsatisfied
|
||||
// `outlives_requirements` to our creator, so create a vector
|
||||
|
@ -453,13 +438,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
None
|
||||
};
|
||||
|
||||
self.check_type_tests(
|
||||
infcx,
|
||||
mir,
|
||||
dfs_storage,
|
||||
mir_def_id,
|
||||
outlives_requirements.as_mut(),
|
||||
);
|
||||
self.check_type_tests(infcx, mir, mir_def_id, outlives_requirements.as_mut());
|
||||
|
||||
self.check_universal_regions(infcx, mir_def_id, outlives_requirements.as_mut());
|
||||
|
||||
|
@ -476,31 +455,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Re-execute the region inference, this time tracking causal information.
|
||||
/// This is significantly slower, so it is done only when an error is being reported.
|
||||
pub(super) fn compute_causal_info(&self, mir: &Mir<'tcx>) -> RegionCausalInfo {
|
||||
let dfs_storage = &mut self.new_dfs_storage();
|
||||
let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(true));
|
||||
RegionCausalInfo { inferred_values }
|
||||
}
|
||||
|
||||
/// 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, mir: &Mir<'tcx>, dfs_storage: &mut dfs::DfsStorage) {
|
||||
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
|
||||
self.dependency_map = Some(self.build_dependency_map());
|
||||
let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(false));
|
||||
let inferred_values = self.compute_region_values(mir);
|
||||
self.inferred_values = Some(inferred_values);
|
||||
}
|
||||
|
||||
#[inline(never)] // ensure dfs is identifiable in profiles
|
||||
fn compute_region_values(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
dfs_storage: &mut dfs::DfsStorage,
|
||||
track_causes: TrackCauses,
|
||||
) -> RegionValues {
|
||||
fn compute_region_values(&self, _mir: &Mir<'tcx>) -> RegionValues {
|
||||
debug!("compute_region_values()");
|
||||
debug!("compute_region_values: constraints={:#?}", {
|
||||
let mut constraints: Vec<_> = self.constraints.iter().collect();
|
||||
|
@ -510,7 +476,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
|
||||
// The initial values for each region are derived from the liveness
|
||||
// constraints we have accumulated.
|
||||
let mut inferred_values = self.liveness_constraints.duplicate(track_causes);
|
||||
let mut inferred_values = self.liveness_constraints.duplicate(TrackCauses(false));
|
||||
|
||||
let dependency_map = self.dependency_map.as_ref().unwrap();
|
||||
|
||||
|
@ -527,21 +493,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
let constraint = &self.constraints[constraint_idx];
|
||||
debug!("propagate_constraints: constraint={:?}", constraint);
|
||||
|
||||
// Grow the value as needed to accommodate the
|
||||
// outlives constraint.
|
||||
let Ok(made_changes) = self.dfs(
|
||||
mir,
|
||||
dfs_storage,
|
||||
CopyFromSourceToTarget {
|
||||
source_region: constraint.sub,
|
||||
target_region: constraint.sup,
|
||||
inferred_values: &mut inferred_values,
|
||||
constraint_point: constraint.point,
|
||||
constraint_span: constraint.span,
|
||||
},
|
||||
);
|
||||
|
||||
if made_changes {
|
||||
if inferred_values.add_region(constraint.sup, constraint.sub) {
|
||||
debug!("propagate_constraints: sub={:?}", constraint.sub);
|
||||
debug!("propagate_constraints: sup={:?}", constraint.sup);
|
||||
|
||||
|
@ -586,7 +538,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
&self,
|
||||
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
dfs_storage: &mut dfs::DfsStorage,
|
||||
mir_def_id: DefId,
|
||||
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
|
||||
) {
|
||||
|
@ -595,13 +546,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
for type_test in &self.type_tests {
|
||||
debug!("check_type_test: {:?}", type_test);
|
||||
|
||||
if self.eval_region_test(
|
||||
mir,
|
||||
dfs_storage,
|
||||
type_test.point,
|
||||
type_test.lower_bound,
|
||||
&type_test.test,
|
||||
) {
|
||||
if self.eval_region_test(mir, type_test.point, type_test.lower_bound, &type_test.test) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -858,7 +803,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
fn eval_region_test(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
dfs_storage: &mut dfs::DfsStorage,
|
||||
point: Location,
|
||||
lower_bound: RegionVid,
|
||||
test: &RegionTest,
|
||||
|
@ -871,27 +815,26 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
match test {
|
||||
RegionTest::IsOutlivedByAllRegionsIn(regions) => regions
|
||||
.iter()
|
||||
.all(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)),
|
||||
.all(|&r| self.eval_outlives(mir, r, lower_bound, point)),
|
||||
|
||||
RegionTest::IsOutlivedByAnyRegionIn(regions) => regions
|
||||
.iter()
|
||||
.any(|&r| self.eval_outlives(mir, dfs_storage, r, lower_bound, point)),
|
||||
.any(|&r| self.eval_outlives(mir, r, lower_bound, point)),
|
||||
|
||||
RegionTest::Any(tests) => tests
|
||||
.iter()
|
||||
.any(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)),
|
||||
.any(|test| self.eval_region_test(mir, point, lower_bound, test)),
|
||||
|
||||
RegionTest::All(tests) => tests
|
||||
.iter()
|
||||
.all(|test| self.eval_region_test(mir, dfs_storage, point, lower_bound, test)),
|
||||
.all(|test| self.eval_region_test(mir, point, lower_bound, test)),
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate whether `sup_region: sub_region @ point`.
|
||||
fn eval_outlives(
|
||||
&self,
|
||||
mir: &Mir<'tcx>,
|
||||
dfs_storage: &mut dfs::DfsStorage,
|
||||
_mir: &Mir<'tcx>,
|
||||
sup_region: RegionVid,
|
||||
sub_region: RegionVid,
|
||||
point: Location,
|
||||
|
@ -901,36 +844,46 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
sup_region, sub_region, point
|
||||
);
|
||||
|
||||
// Roughly speaking, do a DFS of all region elements reachable
|
||||
// from `point` contained in `sub_region`. If any of those are
|
||||
// *not* present in `sup_region`, the DFS will abort early and
|
||||
// yield an `Err` result.
|
||||
match self.dfs(
|
||||
mir,
|
||||
dfs_storage,
|
||||
TestTargetOutlivesSource {
|
||||
source_region: sub_region,
|
||||
target_region: sup_region,
|
||||
constraint_point: point,
|
||||
elements: &self.elements,
|
||||
universal_regions: &self.universal_regions,
|
||||
inferred_values: self.inferred_values.as_ref().unwrap(),
|
||||
},
|
||||
) {
|
||||
Ok(_) => {
|
||||
debug!("eval_outlives: true");
|
||||
true
|
||||
}
|
||||
let inferred_values = self.inferred_values
|
||||
.as_ref()
|
||||
.expect("values for regions not yet inferred");
|
||||
|
||||
Err(elem) => {
|
||||
debug!(
|
||||
"eval_outlives: false because `{:?}` is not present in `{:?}`",
|
||||
self.elements.to_element(elem),
|
||||
sup_region
|
||||
);
|
||||
false
|
||||
}
|
||||
debug!(
|
||||
"eval_outlives: sup_region's value = {:?}",
|
||||
inferred_values.region_value_str(sup_region),
|
||||
);
|
||||
debug!(
|
||||
"eval_outlives: sub_region's value = {:?}",
|
||||
inferred_values.region_value_str(sub_region),
|
||||
);
|
||||
|
||||
// Both the `sub_region` and `sup_region` consist of the union
|
||||
// of some number of universal regions (along with the union
|
||||
// of various points in the CFG; ignore those points for
|
||||
// now). Therefore, the sup-region outlives the sub-region if,
|
||||
// for each universal region R1 in the sub-region, there
|
||||
// exists some region R2 in the sup-region that outlives R1.
|
||||
let universal_outlives = inferred_values
|
||||
.universal_regions_outlived_by(sub_region)
|
||||
.all(|r1| {
|
||||
inferred_values
|
||||
.universal_regions_outlived_by(sup_region)
|
||||
.any(|r2| self.universal_regions.outlives(r2, r1))
|
||||
});
|
||||
|
||||
if !universal_outlives {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we have to compare all the points in the sub region and make
|
||||
// sure they exist in the sup region.
|
||||
|
||||
if self.universal_regions.is_universal_region(sup_region) {
|
||||
// Micro-opt: universal regions contain all points.
|
||||
return true;
|
||||
}
|
||||
|
||||
inferred_values.contains_points(sup_region, sub_region)
|
||||
}
|
||||
|
||||
/// Once regions have been propagated, this method is used to see
|
||||
|
@ -1007,7 +960,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
longer_fr, shorter_fr,
|
||||
);
|
||||
|
||||
let blame_span = self.blame_span(longer_fr, shorter_fr);
|
||||
let blame_index = self.blame_constraint(longer_fr, shorter_fr);
|
||||
let blame_span = self.constraints[blame_index].span;
|
||||
|
||||
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
|
||||
// Shrink `fr` until we find a non-local region (if we do).
|
||||
|
@ -1093,9 +1047,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
diag.emit();
|
||||
}
|
||||
|
||||
crate fn why_region_contains_point(&self, fr1: RegionVid, elem: Location) -> Option<Cause> {
|
||||
// Find some constraint `X: Y` where:
|
||||
// - `fr1: X` transitively
|
||||
// - and `Y` is live at `elem`
|
||||
let index = self.blame_constraint(fr1, elem);
|
||||
let region_sub = self.constraints[index].sub;
|
||||
|
||||
// then return why `Y` was live at `elem`
|
||||
self.liveness_constraints.cause(region_sub, elem)
|
||||
}
|
||||
|
||||
/// Tries to finds a good span to blame for the fact that `fr1`
|
||||
/// contains `fr2`.
|
||||
fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
|
||||
fn blame_constraint(&self, fr1: RegionVid, elem: impl ToElementIndex) -> ConstraintIndex {
|
||||
// Find everything that influenced final value of `fr`.
|
||||
let influenced_fr1 = self.dependencies(fr1);
|
||||
|
||||
|
@ -1108,23 +1073,23 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
// of dependencies, which doesn't account for the locations of
|
||||
// contraints at all. But it will do for now.
|
||||
let relevant_constraint = self.constraints
|
||||
.iter()
|
||||
.filter_map(|constraint| {
|
||||
if constraint.sub != fr2 {
|
||||
None
|
||||
} else {
|
||||
influenced_fr1[constraint.sup]
|
||||
.map(|distance| (distance, constraint.span))
|
||||
}
|
||||
})
|
||||
.min() // constraining fr1 with fewer hops *ought* to be more obvious
|
||||
.map(|(_dist, span)| span);
|
||||
.iter_enumerated()
|
||||
.filter_map(|(i, constraint)| {
|
||||
if !self.liveness_constraints.contains(constraint.sub, elem) {
|
||||
None
|
||||
} else {
|
||||
influenced_fr1[constraint.sup]
|
||||
.map(|distance| (distance, i))
|
||||
}
|
||||
})
|
||||
.min() // constraining fr1 with fewer hops *ought* to be more obvious
|
||||
.map(|(_dist, i)| i);
|
||||
|
||||
relevant_constraint.unwrap_or_else(|| {
|
||||
bug!(
|
||||
"could not find any constraint to blame for {:?}: {:?}",
|
||||
fr1,
|
||||
fr2
|
||||
elem,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
@ -1161,16 +1126,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl RegionCausalInfo {
|
||||
/// Returns the *reason* that the region `r` contains the given point.
|
||||
pub(super) fn why_region_contains_point<R>(&self, r: R, p: Location) -> Option<Rc<Cause>>
|
||||
where
|
||||
R: ToRegionVid,
|
||||
{
|
||||
self.inferred_values.cause(r.to_region_vid(), p)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
fn new(origin: RegionVariableOrigin) -> Self {
|
||||
// Create a new region definition. Note that, for free
|
||||
|
@ -1314,31 +1269,3 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
trait CauseExt {
|
||||
fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause;
|
||||
}
|
||||
|
||||
impl CauseExt for Rc<Cause> {
|
||||
/// Creates a derived cause due to an outlives constraint.
|
||||
fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause {
|
||||
Cause::Outlives {
|
||||
original_cause: self.clone(),
|
||||
constraint_location,
|
||||
constraint_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cause {
|
||||
pub(crate) fn root_cause(&self) -> &Cause {
|
||||
match self {
|
||||
Cause::LiveVar(..)
|
||||
| Cause::DropVar(..)
|
||||
| Cause::LiveOther(..)
|
||||
| Cause::UniversalRegion(..) => self,
|
||||
|
||||
Cause::Outlives { original_cause, .. } => original_cause.root_cause(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,17 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::rc::Rc;
|
||||
use borrow_check::nll::region_infer::TrackCauses;
|
||||
use rustc::mir::{BasicBlock, Location, Mir};
|
||||
use rustc::ty::RegionVid;
|
||||
use rustc_data_structures::bitvec::SparseBitMatrix;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc::mir::{BasicBlock, Location, Mir};
|
||||
use rustc::ty::{self, RegionVid};
|
||||
use syntax::codemap::Span;
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{Cause, CauseExt, TrackCauses};
|
||||
use super::Cause;
|
||||
|
||||
/// Maps between the various kinds of elements of a region value to
|
||||
/// the internal indices that w use.
|
||||
|
@ -72,11 +73,6 @@ impl RegionValueElements {
|
|||
(0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
|
||||
}
|
||||
|
||||
/// Iterates over the `RegionElementIndex` for all points in the CFG.
|
||||
pub(super) fn all_universal_region_indices(&self) -> impl Iterator<Item = RegionElementIndex> {
|
||||
(0..self.num_universal_regions).map(move |i| RegionElementIndex::new(i))
|
||||
}
|
||||
|
||||
/// Converts a particular `RegionElementIndex` to the `RegionElement` it represents.
|
||||
pub(super) fn to_element(&self, i: RegionElementIndex) -> RegionElement {
|
||||
debug!("to_element(i={:?})", i);
|
||||
|
@ -152,7 +148,7 @@ pub(super) enum RegionElement {
|
|||
UniversalRegion(RegionVid),
|
||||
}
|
||||
|
||||
pub(super) trait ToElementIndex {
|
||||
pub(super) trait ToElementIndex: Debug + Copy {
|
||||
fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
|
||||
}
|
||||
|
||||
|
@ -195,7 +191,7 @@ pub(super) struct RegionValues {
|
|||
causes: Option<CauseMap>,
|
||||
}
|
||||
|
||||
type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>;
|
||||
type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Cause>;
|
||||
|
||||
impl RegionValues {
|
||||
/// Creates a new set of "region values" that tracks causal information.
|
||||
|
@ -237,11 +233,22 @@ impl RegionValues {
|
|||
|
||||
/// Adds the given element to the value for the given region. Returns true if
|
||||
/// the element is newly added (i.e., was not already present).
|
||||
pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool {
|
||||
pub(super) fn add_element<E: ToElementIndex>(
|
||||
&mut self,
|
||||
r: RegionVid,
|
||||
elem: E,
|
||||
cause: &Cause,
|
||||
) -> bool {
|
||||
let i = self.elements.index(elem);
|
||||
self.add_internal(r, i, |_| cause.clone())
|
||||
}
|
||||
|
||||
/// Add all elements in `r_from` to `r_to` (because e.g. `r_to:
|
||||
/// r_from`).
|
||||
pub(super) fn add_region(&mut self, r_to: RegionVid, r_from: RegionVid) -> bool {
|
||||
self.matrix.merge(r_from, r_to)
|
||||
}
|
||||
|
||||
/// Internal method to add an element to a region.
|
||||
///
|
||||
/// Takes a "lazy" cause -- this function will return the cause, but it will only
|
||||
|
@ -254,7 +261,7 @@ impl RegionValues {
|
|||
debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
|
||||
|
||||
if let Some(causes) = &mut self.causes {
|
||||
let cause = Rc::new(make_cause(causes));
|
||||
let cause = make_cause(causes);
|
||||
causes.insert((r, i), cause);
|
||||
}
|
||||
|
||||
|
@ -266,15 +273,8 @@ impl RegionValues {
|
|||
// #49998: compare using root cause alone to avoid
|
||||
// useless traffic from similar outlives chains.
|
||||
|
||||
let overwrite = if ty::tls::with(|tcx| {
|
||||
tcx.sess.opts.debugging_opts.nll_subminimal_causes
|
||||
}) {
|
||||
cause.root_cause() < old_cause.root_cause()
|
||||
} else {
|
||||
cause < **old_cause
|
||||
};
|
||||
if overwrite {
|
||||
*old_cause = Rc::new(cause);
|
||||
if cause < *old_cause {
|
||||
*old_cause = cause;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -283,62 +283,22 @@ impl RegionValues {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds `elem` to `to_region` because of a relation:
|
||||
///
|
||||
/// to_region: from_region @ constraint_location
|
||||
///
|
||||
/// that was added by the cod at `constraint_span`.
|
||||
pub(super) fn add_due_to_outlives<T: ToElementIndex>(
|
||||
&mut self,
|
||||
from_region: RegionVid,
|
||||
to_region: RegionVid,
|
||||
elem: T,
|
||||
constraint_location: Location,
|
||||
constraint_span: Span,
|
||||
) -> bool {
|
||||
let elem = self.elements.index(elem);
|
||||
self.add_internal(to_region, elem, |causes| {
|
||||
causes[&(from_region, elem)].outlives(constraint_location, constraint_span)
|
||||
})
|
||||
}
|
||||
|
||||
/// Adds all the universal regions outlived by `from_region` to
|
||||
/// `to_region`.
|
||||
pub(super) fn add_universal_regions_outlived_by(
|
||||
&mut self,
|
||||
from_region: RegionVid,
|
||||
to_region: RegionVid,
|
||||
constraint_location: Location,
|
||||
constraint_span: Span,
|
||||
) -> bool {
|
||||
// We could optimize this by improving `SparseBitMatrix::merge` so
|
||||
// it does not always merge an entire row. That would
|
||||
// complicate causal tracking though.
|
||||
debug!(
|
||||
"add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
|
||||
from_region, to_region
|
||||
);
|
||||
let mut changed = false;
|
||||
for elem in self.elements.all_universal_region_indices() {
|
||||
if self.contains(from_region, elem) {
|
||||
changed |= self.add_due_to_outlives(
|
||||
from_region,
|
||||
to_region,
|
||||
elem,
|
||||
constraint_location,
|
||||
constraint_span,
|
||||
);
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
/// True if the region `r` contains the given element.
|
||||
pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
|
||||
let i = self.elements.index(elem);
|
||||
self.matrix.contains(r, i)
|
||||
}
|
||||
|
||||
/// True if `sup_region` contains all the CFG points that
|
||||
/// `sub_region` contains. Ignores universal regions.
|
||||
pub(super) fn contains_points(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
|
||||
// This could be done faster by comparing the bitsets. But I
|
||||
// am lazy.
|
||||
self.element_indices_contained_in(sub_region)
|
||||
.skip_while(|&i| self.elements.to_universal_region(i).is_some())
|
||||
.all(|e| self.contains(sup_region, e))
|
||||
}
|
||||
|
||||
/// Iterate over the value of the region `r`, yielding up element
|
||||
/// indices. You may prefer `universal_regions_outlived_by` or
|
||||
/// `elements_contained_in`.
|
||||
|
@ -444,7 +404,7 @@ impl RegionValues {
|
|||
///
|
||||
/// Returns None if cause tracking is disabled or `elem` is not
|
||||
/// actually found in `r`.
|
||||
pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> {
|
||||
pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Cause> {
|
||||
let index = self.elements.index(elem);
|
||||
if let Some(causes) = &self.causes {
|
||||
causes.get(&(r, index)).cloned()
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
// borrows in `&v[0]` and `&v[1]` each (in theory) have to outlive R3,
|
||||
// but only at a particular point, and hence they wind up including
|
||||
// distinct regions.
|
||||
//
|
||||
// FIXME(#43234) -- Well, this used to be true, but we modified NLL
|
||||
// for the time being to not take location into account.
|
||||
|
||||
// compile-flags:-Zborrowck=mir -Zverbose
|
||||
// ^^^^^^^^^ force compiler to dump more region information
|
||||
|
@ -36,9 +39,9 @@ fn main() {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#2r | {bb2[0..=1], bb3[0..=1]}
|
||||
// | '_#2r | {bb2[0..=1], bb3[0..=1], bb8[2..=4]}
|
||||
// ...
|
||||
// | '_#4r | {bb8[1..=4]}
|
||||
// | '_#4r | {bb2[1], bb3[0..=1], bb8[1..=4]}
|
||||
// | '_#5r | {bb2[1], bb3[0..=1], bb8[2..=4]}
|
||||
// ...
|
||||
// let mut _2: &'_#5r usize;
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// 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.
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn get_default(map: &mut HashMap<usize, String>, key: usize) -> &mut String {
|
||||
match map.get_mut(&key) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
map.insert(key, "".to_string());
|
||||
map.get_mut(&key).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let map = &mut HashMap::new();
|
||||
map.insert(22, format!("Hello, world"));
|
||||
map.insert(44, format!("Goodbye, world"));
|
||||
assert_eq!(&*get_default(map, 22), "Hello, world");
|
||||
assert_eq!(&*get_default(map, 66), "");
|
||||
}
|
|
@ -2,19 +2,28 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time
|
|||
--> $DIR/mut-borrow-in-loop.rs:20:25
|
||||
|
|
||||
LL | (self.func)(arg) //~ ERROR cannot borrow
|
||||
| ^^^ mutable borrow starts here in previous iteration of loop
|
||||
| ------------^^^-
|
||||
| | |
|
||||
| | mutable borrow starts here in previous iteration of loop
|
||||
| borrow later used here
|
||||
|
||||
error[E0499]: cannot borrow `*arg` as mutable more than once at a time
|
||||
--> $DIR/mut-borrow-in-loop.rs:26:25
|
||||
|
|
||||
LL | (self.func)(arg) //~ ERROR cannot borrow
|
||||
| ^^^ mutable borrow starts here in previous iteration of loop
|
||||
| ------------^^^-
|
||||
| | |
|
||||
| | mutable borrow starts here in previous iteration of loop
|
||||
| borrow later used here
|
||||
|
||||
error[E0499]: cannot borrow `*arg` as mutable more than once at a time
|
||||
--> $DIR/mut-borrow-in-loop.rs:33:25
|
||||
|
|
||||
LL | (self.func)(arg) //~ ERROR cannot borrow
|
||||
| ^^^ mutable borrow starts here in previous iteration of loop
|
||||
| ------------^^^-
|
||||
| | |
|
||||
| | mutable borrow starts here in previous iteration of loop
|
||||
| borrow later used here
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm
|
|||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Just AST errors here
|
||||
LL | map.set(String::new()); // Ideally, this would not error.
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
LL | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:44:17
|
||||
--> $DIR/get_default.rs:45:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
|
@ -23,19 +23,40 @@ LL | }
|
|||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:50:17
|
||||
--> $DIR/get_default.rs:51:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Just AST errors here
|
||||
LL | map.set(String::new()); // Ideally, just AST would error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
LL | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:44:17
|
||||
--> $DIR/get_default.rs:33:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Ideally, this would not error.
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
|
|
||||
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1...
|
||||
--> $DIR/get_default.rs:26:1
|
||||
|
|
||||
LL | / fn ok(map: &mut Map) -> &String {
|
||||
LL | | loop {
|
||||
LL | | match map.get() {
|
||||
LL | | Some(v) => {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:45:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
|
@ -46,6 +67,27 @@ LL | map.set(String::new()); // Both AST and MIR error here
|
|||
LL | return v;
|
||||
| - borrow later used here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:51:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Ideally, just AST would error here
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
|
|
||||
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1...
|
||||
--> $DIR/get_default.rs:41:1
|
||||
|
|
||||
LL | / fn err(map: &mut Map) -> &String {
|
||||
LL | | loop {
|
||||
LL | | match map.get() {
|
||||
LL | | Some(v) => {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0502`.
|
||||
|
|
|
@ -30,8 +30,9 @@ fn ok(map: &mut Map) -> &String {
|
|||
return v;
|
||||
}
|
||||
None => {
|
||||
map.set(String::new()); // Just AST errors here
|
||||
map.set(String::new()); // Ideally, this would not error.
|
||||
//~^ ERROR borrowed as immutable (Ast)
|
||||
//~| ERROR borrowed as immutable (Mir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +48,9 @@ fn err(map: &mut Map) -> &String {
|
|||
return v;
|
||||
}
|
||||
None => {
|
||||
map.set(String::new()); // Just AST errors here
|
||||
map.set(String::new()); // Ideally, just AST would error here
|
||||
//~^ ERROR borrowed as immutable (Ast)
|
||||
//~| ERROR borrowed as immutable (Mir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm
|
|||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Just AST errors here
|
||||
LL | map.set(String::new()); // Ideally, this would not error.
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
LL | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:44:17
|
||||
--> $DIR/get_default.rs:45:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
|
@ -23,19 +23,61 @@ LL | }
|
|||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:50:17
|
||||
--> $DIR/get_default.rs:51:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Just AST errors here
|
||||
LL | map.set(String::new()); // Ideally, just AST would error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
LL | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:44:17
|
||||
--> $DIR/get_default.rs:33:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Ideally, this would not error.
|
||||
| ^^^ mutable borrow occurs here
|
||||
|
|
||||
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 26:1...
|
||||
--> $DIR/get_default.rs:26:1
|
||||
|
|
||||
LL | / fn ok(map: &mut Map) -> &String {
|
||||
LL | | loop {
|
||||
LL | | match map.get() {
|
||||
LL | | Some(v) => {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:51:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
LL | map.set(String::new()); // Ideally, just AST would error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
|
|
||||
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 41:1...
|
||||
--> $DIR/get_default.rs:41:1
|
||||
|
|
||||
LL | / fn err(map: &mut Map) -> &String {
|
||||
LL | | loop {
|
||||
LL | | match map.get() {
|
||||
LL | | Some(v) => {
|
||||
... |
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:45:17
|
||||
|
|
||||
LL | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
|
@ -46,6 +88,6 @@ LL | map.set(String::new()); // Both AST and MIR error here
|
|||
LL | return v;
|
||||
| - borrow later used here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0502`.
|
||||
|
|
Loading…
Reference in a new issue