From a0f0392a6d20dda6fcf7169a81688a0f8e1b51da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sun, 3 Dec 2017 05:49:27 -0500 Subject: [PATCH] rename `copy` to `dfs` and make it customizable --- .../borrow_check/nll/region_infer/dfs.rs | 217 ++++++++++++++++++ .../borrow_check/nll/region_infer/mod.rs | 83 ++----- 2 files changed, 230 insertions(+), 70 deletions(-) create mode 100644 src/librustc_mir/borrow_check/nll/region_infer/dfs.rs diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs new file mode 100644 index 000000000000..c8ac0fa233ff --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/region_infer/dfs.rs @@ -0,0 +1,217 @@ +// 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. + +//! Module defining the `dfs` method on `RegionInferenceContext`, along with +//! its associated helper traits. + +use rustc::mir::{Location, Mir}; +use rustc::ty::RegionVid; +use rustc_data_structures::fx::FxHashSet; +use super::RegionInferenceContext; +use super::values::{RegionElementIndex, RegionValues, RegionValueElements}; + +impl<'tcx> RegionInferenceContext<'tcx> { + /// 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. + pub(super) fn dfs(&self, mir: &Mir<'tcx>, mut op: C) -> Result + where + C: DfsOp, + { + let mut changed = false; + + let mut stack = vec![]; + let mut visited = FxHashSet(); + + stack.push(op.start_point()); + while let Some(p) = stack.pop() { + let point_index = self.elements.index(p); + + if !op.source_region_contains(point_index) { + debug!(" not in from-region"); + continue; + } + + if !visited.insert(p) { + 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 = stack.len(); + + if p.statement_index < block_data.statements.len() { + stack.push(Location { + statement_index: p.statement_index + 1, + ..p + }); + } else { + stack.extend(block_data.terminator().successors().iter().map( + |&basic_block| { + Location { + statement_index: 0, + block: basic_block, + } + }, + )); + } + + if 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; + + /// 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; +} + +/// 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, +} + +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 { + Ok(self.inferred_values.add(self.target_region, point_index)) + } + + fn add_universal_regions_outlived_by_source_to_target( + &mut self, + ) -> Result { + Ok( + self.inferred_values + .add_universal_regions_outlived_by(self.source_region, self.target_region), + ) + } +} + +/// 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. +#[allow(dead_code)] // TODO +pub(super) struct TestTarget<'v> { + source_region: RegionVid, + target_region: RegionVid, + elements: &'v RegionValueElements, + inferred_values: &'v RegionValues, + constraint_point: Location, +} + +#[allow(dead_code)] // TODO +impl<'v> DfsOp for TestTarget<'v> { + /// 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 { + 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 { + for ur in self.inferred_values + .universal_regions_outlived_by(self.source_region) + { + if !self.inferred_values.contains(self.target_region, ur) { + return Err(self.elements.index(ur)); + } + } + + Ok(false) + } +} diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index dfa7ce868756..6d76398b941e 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -18,12 +18,13 @@ use rustc::infer::region_constraints::VarOrigins; use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir}; use rustc::ty::{self, RegionVid}; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_data_structures::fx::FxHashSet; use std::fmt; use std::rc::Rc; use syntax_pos::Span; mod annotation; +mod dfs; +use self::dfs::CopyFromSourceToTarget; mod dump_mir; mod graphviz; mod values; @@ -421,14 +422,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Grow the value as needed to accommodate the // outlives constraint. - - if self.copy( - &mut inferred_values, + let Ok(made_changes) = self.dfs( mir, - constraint.sub, - constraint.sup, - constraint.point, - ) { + CopyFromSourceToTarget { + source_region: constraint.sub, + target_region: constraint.sup, + inferred_values: &mut inferred_values, + constraint_point: constraint.point, + }, + ); + + if made_changes { debug!("propagate_constraints: sub={:?}", constraint.sub); debug!("propagate_constraints: sup={:?}", constraint.sup); changed = true; @@ -440,68 +444,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.inferred_values = Some(inferred_values); } - fn copy( - &self, - inferred_values: &mut RegionValues, - mir: &Mir<'tcx>, - from_region: RegionVid, - to_region: RegionVid, - constraint_point: Location, - ) -> bool { - let mut changed = false; - - let mut stack = vec![]; - let mut visited = FxHashSet(); - - stack.push(constraint_point); - while let Some(p) = stack.pop() { - let point_index = self.elements.index(p); - - if !inferred_values.contains(from_region, point_index) { - debug!(" not in from-region"); - continue; - } - - if !visited.insert(p) { - debug!(" already visited"); - continue; - } - - let new = inferred_values.add(to_region, point_index); - changed |= new; - - let block_data = &mir[p.block]; - - let start_stack_len = stack.len(); - - if p.statement_index < block_data.statements.len() { - stack.push(Location { - statement_index: p.statement_index + 1, - ..p - }); - } else { - stack.extend(block_data.terminator().successors().iter().map( - |&basic_block| { - Location { - statement_index: 0, - block: basic_block, - } - }, - )); - } - - if 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 |= - inferred_values.add_universal_regions_outlived_by(from_region, to_region); - } - } - - changed - } - /// Tries to finds a good span to blame for the fact that `fr1` /// contains `fr2`. fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span { @@ -647,3 +589,4 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements { } } } +