Skip to content

Commit a0f0392

Browse files
committed
rename copy to dfs and make it customizable
1 parent 86334c7 commit a0f0392

File tree

2 files changed

+230
-70
lines changed

2 files changed

+230
-70
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Module defining the `dfs` method on `RegionInferenceContext`, along with
12+
//! its associated helper traits.
13+
14+
use rustc::mir::{Location, Mir};
15+
use rustc::ty::RegionVid;
16+
use rustc_data_structures::fx::FxHashSet;
17+
use super::RegionInferenceContext;
18+
use super::values::{RegionElementIndex, RegionValues, RegionValueElements};
19+
20+
impl<'tcx> RegionInferenceContext<'tcx> {
21+
/// Function used to satisfy or test a `R1: R2 @ P`
22+
/// constraint. The core idea is that it performs a DFS starting
23+
/// from `P`. The precise actions *during* that DFS depend on the
24+
/// `op` supplied, so see (e.g.) `CopyFromSourceToTarget` for more
25+
/// details.
26+
///
27+
/// Returns:
28+
///
29+
/// - `Ok(true)` if the walk was completed and something changed
30+
/// along the way;
31+
/// - `Ok(false)` if the walk was completed with no changes;
32+
/// - `Err(early)` if the walk was existed early by `op`. `earlyelem` is the
33+
/// value that `op` returned.
34+
pub(super) fn dfs<C>(&self, mir: &Mir<'tcx>, mut op: C) -> Result<bool, C::Early>
35+
where
36+
C: DfsOp,
37+
{
38+
let mut changed = false;
39+
40+
let mut stack = vec![];
41+
let mut visited = FxHashSet();
42+
43+
stack.push(op.start_point());
44+
while let Some(p) = stack.pop() {
45+
let point_index = self.elements.index(p);
46+
47+
if !op.source_region_contains(point_index) {
48+
debug!(" not in from-region");
49+
continue;
50+
}
51+
52+
if !visited.insert(p) {
53+
debug!(" already visited");
54+
continue;
55+
}
56+
57+
let new = op.add_to_target_region(point_index)?;
58+
changed |= new;
59+
60+
let block_data = &mir[p.block];
61+
62+
let start_stack_len = stack.len();
63+
64+
if p.statement_index < block_data.statements.len() {
65+
stack.push(Location {
66+
statement_index: p.statement_index + 1,
67+
..p
68+
});
69+
} else {
70+
stack.extend(block_data.terminator().successors().iter().map(
71+
|&basic_block| {
72+
Location {
73+
statement_index: 0,
74+
block: basic_block,
75+
}
76+
},
77+
));
78+
}
79+
80+
if stack.len() == start_stack_len {
81+
// If we reach the END point in the graph, then copy
82+
// over any skolemized end points in the `from_region`
83+
// and make sure they are included in the `to_region`.
84+
changed |= op.add_universal_regions_outlived_by_source_to_target()?;
85+
}
86+
}
87+
88+
Ok(changed)
89+
}
90+
}
91+
92+
/// Customizes the operation of the `dfs` function. This function is
93+
/// used during inference to satisfy a `R1: R2 @ P` constraint.
94+
pub(super) trait DfsOp {
95+
/// If this op stops the walk early, what type does it propagate?
96+
type Early;
97+
98+
/// Returns the point from which to start the DFS.
99+
fn start_point(&self) -> Location;
100+
101+
/// Returns true if the source region contains the given point.
102+
fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool;
103+
104+
/// Adds the given point to the target region, returning true if
105+
/// something has changed. Returns `Err` if we should abort the
106+
/// walk early.
107+
fn add_to_target_region(
108+
&mut self,
109+
point_index: RegionElementIndex,
110+
) -> Result<bool, Self::Early>;
111+
112+
/// Adds all universal regions in the source region to the target region, returning
113+
/// true if something has changed.
114+
fn add_universal_regions_outlived_by_source_to_target(
115+
&mut self,
116+
) -> Result<bool, Self::Early>;
117+
}
118+
119+
/// Used during inference to enforce a `R1: R2 @ P` constraint. For
120+
/// each point Q we reach along the DFS, we check if Q is in R2 (the
121+
/// "source region"). If not, we stop the walk. Otherwise, we add Q to
122+
/// R1 (the "target region") and continue to Q's successors. If we
123+
/// reach the end of the graph, then we add any universal regions from
124+
/// R2 into R1.
125+
pub(super) struct CopyFromSourceToTarget<'v> {
126+
pub source_region: RegionVid,
127+
pub target_region: RegionVid,
128+
pub inferred_values: &'v mut RegionValues,
129+
pub constraint_point: Location,
130+
}
131+
132+
impl<'v> DfsOp for CopyFromSourceToTarget<'v> {
133+
/// We never stop the walk early.
134+
type Early = !;
135+
136+
fn start_point(&self) -> Location {
137+
self.constraint_point
138+
}
139+
140+
fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
141+
self.inferred_values
142+
.contains(self.source_region, point_index)
143+
}
144+
145+
fn add_to_target_region(
146+
&mut self,
147+
point_index: RegionElementIndex,
148+
) -> Result<bool, !> {
149+
Ok(self.inferred_values.add(self.target_region, point_index))
150+
}
151+
152+
fn add_universal_regions_outlived_by_source_to_target(
153+
&mut self,
154+
) -> Result<bool, !> {
155+
Ok(
156+
self.inferred_values
157+
.add_universal_regions_outlived_by(self.source_region, self.target_region),
158+
)
159+
}
160+
}
161+
162+
/// Used after inference to *test* a `R1: R2 @ P` constraint. For
163+
/// each point Q we reach along the DFS, we check if Q in R2 is also
164+
/// contained in R1. If not, we abort the walk early with an `Err`
165+
/// condition. Similarly, if we reach the end of the graph and find
166+
/// that R1 contains some universal region that R2 does not contain,
167+
/// we abort the walk early.
168+
#[allow(dead_code)] // TODO
169+
pub(super) struct TestTarget<'v> {
170+
source_region: RegionVid,
171+
target_region: RegionVid,
172+
elements: &'v RegionValueElements,
173+
inferred_values: &'v RegionValues,
174+
constraint_point: Location,
175+
}
176+
177+
#[allow(dead_code)] // TODO
178+
impl<'v> DfsOp for TestTarget<'v> {
179+
/// The element that was not found within R2.
180+
type Early = RegionElementIndex;
181+
182+
fn start_point(&self) -> Location {
183+
self.constraint_point
184+
}
185+
186+
fn source_region_contains(&mut self, point_index: RegionElementIndex) -> bool {
187+
self.inferred_values
188+
.contains(self.source_region, point_index)
189+
}
190+
191+
fn add_to_target_region(
192+
&mut self,
193+
point_index: RegionElementIndex,
194+
) -> Result<bool, RegionElementIndex> {
195+
if !self.inferred_values
196+
.contains(self.target_region, point_index)
197+
{
198+
return Err(point_index);
199+
}
200+
201+
Ok(false)
202+
}
203+
204+
fn add_universal_regions_outlived_by_source_to_target(
205+
&mut self,
206+
) -> Result<bool, RegionElementIndex> {
207+
for ur in self.inferred_values
208+
.universal_regions_outlived_by(self.source_region)
209+
{
210+
if !self.inferred_values.contains(self.target_region, ur) {
211+
return Err(self.elements.index(ur));
212+
}
213+
}
214+
215+
Ok(false)
216+
}
217+
}

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 13 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ use rustc::infer::region_constraints::VarOrigins;
1818
use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
1919
use rustc::ty::{self, RegionVid};
2020
use rustc_data_structures::indexed_vec::IndexVec;
21-
use rustc_data_structures::fx::FxHashSet;
2221
use std::fmt;
2322
use std::rc::Rc;
2423
use syntax_pos::Span;
2524

2625
mod annotation;
26+
mod dfs;
27+
use self::dfs::CopyFromSourceToTarget;
2728
mod dump_mir;
2829
mod graphviz;
2930
mod values;
@@ -421,14 +422,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
421422

422423
// Grow the value as needed to accommodate the
423424
// outlives constraint.
424-
425-
if self.copy(
426-
&mut inferred_values,
425+
let Ok(made_changes) = self.dfs(
427426
mir,
428-
constraint.sub,
429-
constraint.sup,
430-
constraint.point,
431-
) {
427+
CopyFromSourceToTarget {
428+
source_region: constraint.sub,
429+
target_region: constraint.sup,
430+
inferred_values: &mut inferred_values,
431+
constraint_point: constraint.point,
432+
},
433+
);
434+
435+
if made_changes {
432436
debug!("propagate_constraints: sub={:?}", constraint.sub);
433437
debug!("propagate_constraints: sup={:?}", constraint.sup);
434438
changed = true;
@@ -440,68 +444,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
440444
self.inferred_values = Some(inferred_values);
441445
}
442446

443-
fn copy(
444-
&self,
445-
inferred_values: &mut RegionValues,
446-
mir: &Mir<'tcx>,
447-
from_region: RegionVid,
448-
to_region: RegionVid,
449-
constraint_point: Location,
450-
) -> bool {
451-
let mut changed = false;
452-
453-
let mut stack = vec![];
454-
let mut visited = FxHashSet();
455-
456-
stack.push(constraint_point);
457-
while let Some(p) = stack.pop() {
458-
let point_index = self.elements.index(p);
459-
460-
if !inferred_values.contains(from_region, point_index) {
461-
debug!(" not in from-region");
462-
continue;
463-
}
464-
465-
if !visited.insert(p) {
466-
debug!(" already visited");
467-
continue;
468-
}
469-
470-
let new = inferred_values.add(to_region, point_index);
471-
changed |= new;
472-
473-
let block_data = &mir[p.block];
474-
475-
let start_stack_len = stack.len();
476-
477-
if p.statement_index < block_data.statements.len() {
478-
stack.push(Location {
479-
statement_index: p.statement_index + 1,
480-
..p
481-
});
482-
} else {
483-
stack.extend(block_data.terminator().successors().iter().map(
484-
|&basic_block| {
485-
Location {
486-
statement_index: 0,
487-
block: basic_block,
488-
}
489-
},
490-
));
491-
}
492-
493-
if stack.len() == start_stack_len {
494-
// If we reach the END point in the graph, then copy
495-
// over any skolemized end points in the `from_region`
496-
// and make sure they are included in the `to_region`.
497-
changed |=
498-
inferred_values.add_universal_regions_outlived_by(from_region, to_region);
499-
}
500-
}
501-
502-
changed
503-
}
504-
505447
/// Tries to finds a good span to blame for the fact that `fr1`
506448
/// contains `fr2`.
507449
fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
@@ -647,3 +589,4 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
647589
}
648590
}
649591
}
592+

0 commit comments

Comments
 (0)