Skip to content

Commit 2d75213

Browse files
committed
Auto merge of #45155 - Nashenas88:nll-infer, r=nikomatsakis
NLL infer r? @nikomatsakis
2 parents 3037965 + 9603e24 commit 2d75213

File tree

2 files changed

+245
-9
lines changed

2 files changed

+245
-9
lines changed
+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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+
use super::{Region, RegionIndex};
12+
use std::mem;
13+
use rustc::infer::InferCtxt;
14+
use rustc::mir::{Location, Mir};
15+
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
16+
use rustc_data_structures::fx::FxHashSet;
17+
18+
pub struct InferenceContext {
19+
definitions: IndexVec<RegionIndex, VarDefinition>,
20+
constraints: IndexVec<ConstraintIndex, Constraint>,
21+
errors: IndexVec<InferenceErrorIndex, InferenceError>,
22+
}
23+
24+
pub struct InferenceError {
25+
pub constraint_point: Location,
26+
pub name: (), // FIXME(nashenas88) RegionName
27+
}
28+
29+
newtype_index!(InferenceErrorIndex);
30+
31+
struct VarDefinition {
32+
name: (), // FIXME(nashenas88) RegionName
33+
value: Region,
34+
capped: bool,
35+
}
36+
37+
impl VarDefinition {
38+
pub fn new(value: Region) -> Self {
39+
Self {
40+
name: (),
41+
value,
42+
capped: false,
43+
}
44+
}
45+
}
46+
47+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
48+
pub struct Constraint {
49+
sub: RegionIndex,
50+
sup: RegionIndex,
51+
point: Location,
52+
}
53+
54+
newtype_index!(ConstraintIndex);
55+
56+
impl InferenceContext {
57+
pub fn new(values: IndexVec<RegionIndex, Region>) -> Self {
58+
Self {
59+
definitions: values.into_iter().map(VarDefinition::new).collect(),
60+
constraints: IndexVec::new(),
61+
errors: IndexVec::new(),
62+
}
63+
}
64+
65+
#[allow(dead_code)]
66+
pub fn cap_var(&mut self, v: RegionIndex) {
67+
self.definitions[v].capped = true;
68+
}
69+
70+
#[allow(dead_code)]
71+
pub fn add_live_point(&mut self, v: RegionIndex, point: Location) {
72+
debug!("add_live_point({:?}, {:?})", v, point);
73+
let definition = &mut self.definitions[v];
74+
if definition.value.add_point(point) {
75+
if definition.capped {
76+
self.errors.push(InferenceError {
77+
constraint_point: point,
78+
name: definition.name,
79+
});
80+
}
81+
}
82+
}
83+
84+
#[allow(dead_code)]
85+
pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
86+
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
87+
self.constraints.push(Constraint { sup, sub, point });
88+
}
89+
90+
#[allow(dead_code)]
91+
pub fn region(&self, v: RegionIndex) -> &Region {
92+
&self.definitions[v].value
93+
}
94+
95+
pub fn solve<'a, 'gcx, 'tcx>(
96+
&mut self,
97+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
98+
mir: &'a Mir<'tcx>,
99+
) -> IndexVec<InferenceErrorIndex, InferenceError>
100+
where
101+
'gcx: 'tcx + 'a,
102+
'tcx: 'a,
103+
{
104+
let mut changed = true;
105+
let mut dfs = Dfs::new(infcx, mir);
106+
while changed {
107+
changed = false;
108+
for constraint in &self.constraints {
109+
let sub = &self.definitions[constraint.sub].value.clone();
110+
let sup_def = &mut self.definitions[constraint.sup];
111+
debug!("constraint: {:?}", constraint);
112+
debug!(" sub (before): {:?}", sub);
113+
debug!(" sup (before): {:?}", sup_def.value);
114+
115+
if dfs.copy(sub, &mut sup_def.value, constraint.point) {
116+
changed = true;
117+
if sup_def.capped {
118+
// This is kind of a hack, but when we add a
119+
// constraint, the "point" is always the point
120+
// AFTER the action that induced the
121+
// constraint. So report the error on the
122+
// action BEFORE that.
123+
assert!(constraint.point.statement_index > 0);
124+
let p = Location {
125+
block: constraint.point.block,
126+
statement_index: constraint.point.statement_index - 1,
127+
};
128+
129+
self.errors.push(InferenceError {
130+
constraint_point: p,
131+
name: sup_def.name,
132+
});
133+
}
134+
}
135+
136+
debug!(" sup (after) : {:?}", sup_def.value);
137+
debug!(" changed : {:?}", changed);
138+
}
139+
debug!("\n");
140+
}
141+
142+
mem::replace(&mut self.errors, IndexVec::new())
143+
}
144+
}
145+
146+
struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
147+
#[allow(dead_code)]
148+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
149+
mir: &'a Mir<'tcx>,
150+
}
151+
152+
impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
153+
fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
154+
Self { infcx, mir }
155+
}
156+
157+
fn copy(
158+
&mut self,
159+
from_region: &Region,
160+
to_region: &mut Region,
161+
start_point: Location,
162+
) -> bool {
163+
let mut changed = false;
164+
165+
let mut stack = vec![];
166+
let mut visited = FxHashSet();
167+
168+
stack.push(start_point);
169+
while let Some(p) = stack.pop() {
170+
debug!(" dfs: p={:?}", p);
171+
172+
if !from_region.may_contain(p) {
173+
debug!(" not in from-region");
174+
continue;
175+
}
176+
177+
if !visited.insert(p) {
178+
debug!(" already visited");
179+
continue;
180+
}
181+
182+
changed |= to_region.add_point(p);
183+
184+
let block_data = &self.mir[p.block];
185+
let successor_points = if p.statement_index < block_data.statements.len() {
186+
vec![Location {
187+
statement_index: p.statement_index + 1,
188+
..p
189+
}]
190+
} else {
191+
block_data.terminator()
192+
.successors()
193+
.iter()
194+
.map(|&basic_block| Location {
195+
statement_index: 0,
196+
block: basic_block,
197+
})
198+
.collect::<Vec<_>>()
199+
};
200+
201+
if successor_points.is_empty() {
202+
// FIXME handle free regions
203+
// If we reach the END point in the graph, then copy
204+
// over any skolemized end points in the `from_region`
205+
// and make sure they are included in the `to_region`.
206+
// for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
207+
// // FIXME(nashenas88) figure out skolemized_end points
208+
// let block = self.env.graph.skolemized_end(region_decl.name);
209+
// let skolemized_end_point = Location {
210+
// block,
211+
// statement_index: 0,
212+
// };
213+
// changed |= to_region.add_point(skolemized_end_point);
214+
// }
215+
} else {
216+
stack.extend(successor_points);
217+
}
218+
}
219+
220+
changed
221+
}
222+
}

src/librustc_mir/transform/nll/mod.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use self::infer::InferenceContext;
1112
use rustc::ty::TypeFoldable;
1213
use rustc::ty::subst::{Kind, Substs};
1314
use rustc::ty::{Ty, TyCtxt, ClosureSubsts, RegionVid, RegionKind};
1415
use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
1516
use rustc::mir::visit::{MutVisitor, Lookup};
1617
use rustc::mir::transform::{MirPass, MirSource};
17-
use rustc::infer::{self, InferCtxt};
18+
use rustc::infer::{self as rustc_infer, InferCtxt};
1819
use rustc::util::nodemap::FxHashSet;
1920
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
2021
use syntax_pos::DUMMY_SP;
@@ -24,30 +25,33 @@ use std::fmt;
2425
use util as mir_util;
2526
use self::mir_util::PassWhere;
2627

28+
mod infer;
29+
2730
#[allow(dead_code)]
2831
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
2932
lookup_map: HashMap<RegionVid, Lookup>,
3033
regions: IndexVec<RegionIndex, Region>,
31-
infcx: InferCtxt<'a, 'gcx, 'tcx>,
34+
#[allow(dead_code)]
35+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
3236
}
3337

3438
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
35-
pub fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>) -> Self {
39+
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
3640
NLLVisitor {
3741
infcx,
3842
lookup_map: HashMap::new(),
3943
regions: IndexVec::new(),
4044
}
4145
}
4246

43-
pub fn into_results(self) -> HashMap<RegionVid, Lookup> {
44-
self.lookup_map
47+
pub fn into_results(self) -> (HashMap<RegionVid, Lookup>, IndexVec<RegionIndex, Region>) {
48+
(self.lookup_map, self.regions)
4549
}
4650

4751
fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
4852
self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
4953
self.regions.push(Region::default());
50-
self.infcx.next_region_var(infer::MiscVariable(DUMMY_SP))
54+
self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
5155
})
5256
}
5357

@@ -147,7 +151,7 @@ impl MirPass for NLL {
147151
tcx.infer_ctxt().enter(|infcx| {
148152
// Clone mir so we can mutate it without disturbing the rest of the compiler
149153
let mut renumbered_mir = mir.clone();
150-
let mut visitor = NLLVisitor::new(infcx);
154+
let mut visitor = NLLVisitor::new(&infcx);
151155
visitor.visit_mir(&mut renumbered_mir);
152156
mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| {
153157
if let PassWhere::BeforeCFG = pass_where {
@@ -157,13 +161,15 @@ impl MirPass for NLL {
157161
}
158162
Ok(())
159163
});
160-
let _results = visitor.into_results();
164+
let (_lookup_map, regions) = visitor.into_results();
165+
let mut inference_context = InferenceContext::new(regions);
166+
inference_context.solve(&infcx, &renumbered_mir);
161167
})
162168
}
163169
}
164170

165171
#[derive(Clone, Default, PartialEq, Eq)]
166-
struct Region {
172+
pub struct Region {
167173
points: FxHashSet<Location>,
168174
}
169175

@@ -173,6 +179,14 @@ impl fmt::Debug for Region {
173179
}
174180
}
175181

182+
impl Region {
183+
pub fn add_point(&mut self, point: Location) -> bool {
184+
self.points.insert(point)
185+
}
176186

187+
pub fn may_contain(&self, point: Location) -> bool {
188+
self.points.contains(&point)
189+
}
190+
}
177191

178192
newtype_index!(RegionIndex);

0 commit comments

Comments
 (0)