提交 434d59a2 编写于 作者: N Niko Matsakis

ignore the point where the outlives requirement was added

上级 964e0691
......@@ -326,7 +326,7 @@ pub fn merge(&mut self, read: R, write: R) -> bool {
}
/// True if `sub` is a subset of `sup`
pub fn subset(&self, sub: R, sup: R) -> bool {
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];
......
// 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)
}
}
......@@ -11,15 +11,17 @@
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};
......@@ -31,8 +33,6 @@
use syntax_pos::Span;
mod annotation;
mod dfs;
use self::dfs::{CopyFromSourceToTarget, TestTargetOutlivesSource};
mod dump_mir;
mod graphviz;
mod values;
......@@ -422,9 +422,7 @@ fn solve_inner<'gcx>(
) -> 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
......@@ -437,13 +435,7 @@ fn solve_inner<'gcx>(
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());
......@@ -464,18 +456,14 @@ fn solve_inner<'gcx>(
/// 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);
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,
) -> 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();
......@@ -502,21 +490,7 @@ fn compute_region_values(
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);
......@@ -561,7 +535,6 @@ fn check_type_tests<'gcx>(
&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>>>,
) {
......@@ -570,13 +543,7 @@ fn check_type_tests<'gcx>(
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;
}
......@@ -833,7 +800,6 @@ fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
fn eval_region_test(
&self,
mir: &Mir<'tcx>,
dfs_storage: &mut dfs::DfsStorage,
point: Location,
lower_bound: RegionVid,
test: &RegionTest,
......@@ -846,27 +812,26 @@ fn eval_region_test(
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,
......@@ -876,36 +841,43 @@ fn eval_outlives(
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
......
......@@ -17,7 +17,6 @@
use rustc::ty::RegionVid;
use std::fmt::Debug;
use std::rc::Rc;
use syntax::codemap::Span;
use super::Cause;
......@@ -74,11 +73,6 @@ pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElem
(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);
......@@ -244,6 +238,12 @@ pub(super) fn add_element<E: ToElementIndex>(&mut self, r: RegionVid, elem: E, c
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
......@@ -278,60 +278,22 @@ fn add_internal<F>(&mut self, r: RegionVid, i: RegionElementIndex, make_cause: F
}
}
/// 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)])
}
/// 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`.
......
......@@ -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`.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册