Port region inference code to use new graph

This commit is contained in:
Niko Matsakis 2013-07-10 07:40:27 -04:00
parent 7429e7a114
commit e706590e70
2 changed files with 165 additions and 180 deletions

View File

@ -18,6 +18,8 @@ use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh};
use middle::typeck::infer::cres;
use middle::typeck::infer::{RegionVariableOrigin, SubregionOrigin};
use middle::typeck::infer;
use middle::graph;
use middle::graph::{Direction, NodeIndex};
use util::common::indenter;
use util::ppaux::{Repr};
@ -105,7 +107,7 @@ pub struct RegionVarBindings {
// This contains the results of inference. It begins as an empty
// cell and only acquires a value after inference is complete.
// We use a cell vs a mutable option to circumvent borrowck errors.
values: Cell<~[GraphNodeValue]>,
values: Cell<~[VarValue]>,
}
pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
@ -168,7 +170,7 @@ impl RegionVarBindings {
}
}
pub fn num_vars(&mut self) -> uint {
pub fn num_vars(&self) -> uint {
self.var_origins.len()
}
@ -705,29 +707,14 @@ impl RegionVarBindings {
// ______________________________________________________________________
#[deriving(Eq)]
enum Direction { Incoming = 0, Outgoing = 1 }
#[deriving(Eq)]
enum Classification { Expanding, Contracting }
enum GraphNodeValue { NoValue, Value(Region), ErrorValue }
enum VarValue { NoValue, Value(Region), ErrorValue }
struct GraphNode {
origin: RegionVariableOrigin,
struct VarData {
classification: Classification,
value: GraphNodeValue,
head_edge: [uint, ..2],
}
struct GraphEdge {
next_edge: [uint, ..2],
constraint: Constraint,
}
struct Graph {
nodes: ~[GraphNode],
edges: ~[GraphEdge],
value: VarValue,
}
struct RegionAndOrigin {
@ -735,97 +722,86 @@ struct RegionAndOrigin {
origin: SubregionOrigin,
}
type Graph = graph::Graph<(), Constraint>;
type GraphNode = graph::Node<()>;
type GraphEdge = graph::Edge<Constraint>;
impl RegionVarBindings {
fn infer_variable_values(&mut self,
errors: &mut OptVec<RegionResolutionError>)
-> ~[GraphNodeValue] {
let mut graph = self.construct_graph();
self.expansion(&mut graph);
self.contraction(&mut graph);
-> ~[VarValue] {
let mut var_data = self.construct_var_data();
self.expansion(var_data);
self.contraction(var_data);
let graph = self.construct_graph();
self.collect_concrete_region_errors(&graph, errors);
self.extract_values_and_collect_conflicts(&graph, errors)
self.extract_values_and_collect_conflicts(&graph, var_data, errors)
}
fn construct_graph(&mut self) -> Graph {
let num_vars = self.num_vars();
let num_edges = self.constraints.len();
let nodes = vec::from_fn(num_vars, |var_idx| {
GraphNode {
let mut graph = graph::Graph::with_capacity(num_vars + 1,
num_edges);
for uint::range(0, num_vars) |var_idx| {
graph.add_node(());
}
let dummy_idx = graph.add_node(());
for self.constraints.iter().advance |(constraint, _)| {
match *constraint {
ConstrainVarSubVar(a_id, b_id) => {
graph.add_edge(NodeIndex(a_id.to_uint()),
NodeIndex(b_id.to_uint()),
*constraint);
}
ConstrainRegSubVar(_, b_id) => {
graph.add_edge(dummy_idx,
NodeIndex(b_id.to_uint()),
*constraint);
}
ConstrainVarSubReg(a_id, _) => {
graph.add_edge(NodeIndex(a_id.to_uint()),
dummy_idx,
*constraint);
}
ConstrainRegSubReg(*) => {
// Relations between two concrete regions do not
// require an edge in the graph.
}
}
}
return graph;
}
fn construct_var_data(&mut self) -> ~[VarData] {
vec::from_fn(self.num_vars(), |var_idx| {
VarData {
// All nodes are initially classified as contracting; during
// the expansion phase, we will shift the classification for
// those nodes that have a concrete region predecessor to
// Expanding.
classification: Contracting,
origin: self.var_origins[var_idx],
value: NoValue,
head_edge: [uint::max_value, uint::max_value]
}
});
// It would be nice to write this using map():
let mut edges = vec::with_capacity(num_edges);
for self.constraints.iter().advance |(constraint, _)| {
edges.push(GraphEdge {
next_edge: [uint::max_value, uint::max_value],
constraint: *constraint,
});
}
let mut graph = Graph {
nodes: nodes,
edges: edges
};
for uint::range(0, num_edges) |edge_idx| {
match graph.edges[edge_idx].constraint {
ConstrainVarSubVar(a_id, b_id) => {
insert_edge(&mut graph, a_id, Outgoing, edge_idx);
insert_edge(&mut graph, b_id, Incoming, edge_idx);
}
ConstrainRegSubVar(_, b_id) => {
insert_edge(&mut graph, b_id, Incoming, edge_idx);
}
ConstrainVarSubReg(a_id, _) => {
insert_edge(&mut graph, a_id, Outgoing, edge_idx);
}
ConstrainRegSubReg(*) => {
// Relations between two concrete regions do not
// require an edge in the graph.
}
}
}
return (graph);
fn insert_edge(graph: &mut Graph,
node_id: RegionVid,
edge_dir: Direction,
edge_idx: uint) {
//! Insert edge `edge_idx` on the link list of edges in direction
//! `edge_dir` for the node `node_id`
let edge_dir = edge_dir as uint;
assert_eq!(graph.edges[edge_idx].next_edge[edge_dir],
uint::max_value);
let n = node_id.to_uint();
let prev_head = graph.nodes[n].head_edge[edge_dir];
graph.edges[edge_idx].next_edge[edge_dir] = prev_head;
graph.nodes[n].head_edge[edge_dir] = edge_idx;
}
})
}
fn expansion(&mut self, graph: &mut Graph) {
do iterate_until_fixed_point(~"Expansion", graph) |nodes, edge| {
match edge.constraint {
fn expansion(&mut self, var_data: &mut [VarData]) {
do self.iterate_until_fixed_point("Expansion") |constraint| {
match *constraint {
ConstrainRegSubVar(a_region, b_vid) => {
let b_node = &mut nodes[b_vid.to_uint()];
self.expand_node(a_region, b_vid, b_node)
let b_data = &mut var_data[b_vid.to_uint()];
self.expand_node(a_region, b_vid, b_data)
}
ConstrainVarSubVar(a_vid, b_vid) => {
match nodes[a_vid.to_uint()].value {
match var_data[a_vid.to_uint()].value {
NoValue | ErrorValue => false,
Value(a_region) => {
let b_node = &mut nodes[b_vid.to_uint()];
let b_node = &mut var_data[b_vid.to_uint()];
self.expand_node(a_region, b_vid, b_node)
}
}
@ -842,20 +818,20 @@ impl RegionVarBindings {
}
}
fn expand_node(&mut self,
fn expand_node(&self,
a_region: Region,
b_vid: RegionVid,
b_node: &mut GraphNode)
b_data: &mut VarData)
-> bool {
debug!("expand_node(%?, %? == %?)",
a_region, b_vid, b_node.value);
a_region, b_vid, b_data.value);
b_node.classification = Expanding;
match b_node.value {
b_data.classification = Expanding;
match b_data.value {
NoValue => {
debug!("Setting initial value of %? to %?", b_vid, a_region);
b_node.value = Value(a_region);
b_data.value = Value(a_region);
return true;
}
@ -868,7 +844,7 @@ impl RegionVarBindings {
debug!("Expanding value of %? from %? to %?",
b_vid, cur_region, lub);
b_node.value = Value(lub);
b_data.value = Value(lub);
return true;
}
@ -878,26 +854,26 @@ impl RegionVarBindings {
}
}
fn contraction(&mut self,
graph: &mut Graph) {
do iterate_until_fixed_point(~"Contraction", graph) |nodes, edge| {
match edge.constraint {
fn contraction(&self,
var_data: &mut [VarData]) {
do self.iterate_until_fixed_point("Contraction") |constraint| {
match *constraint {
ConstrainRegSubVar(*) => {
// This is an expansion constraint. Ignore.
false
}
ConstrainVarSubVar(a_vid, b_vid) => {
match nodes[b_vid.to_uint()].value {
match var_data[b_vid.to_uint()].value {
NoValue | ErrorValue => false,
Value(b_region) => {
let a_node = &mut nodes[a_vid.to_uint()];
self.contract_node(a_vid, a_node, b_region)
let a_data = &mut var_data[a_vid.to_uint()];
self.contract_node(a_vid, a_data, b_region)
}
}
}
ConstrainVarSubReg(a_vid, b_region) => {
let a_node = &mut nodes[a_vid.to_uint()];
self.contract_node(a_vid, a_node, b_region)
let a_data = &mut var_data[a_vid.to_uint()];
self.contract_node(a_vid, a_data, b_region)
}
ConstrainRegSubReg(*) => {
// No region variables involved. Ignore.
@ -907,18 +883,18 @@ impl RegionVarBindings {
}
}
fn contract_node(&mut self,
fn contract_node(&self,
a_vid: RegionVid,
a_node: &mut GraphNode,
a_data: &mut VarData,
b_region: Region)
-> bool {
debug!("contract_node(%? == %?/%?, %?)",
a_vid, a_node.value, a_node.classification, b_region);
a_vid, a_data.value, a_data.classification, b_region);
return match a_node.value {
return match a_data.value {
NoValue => {
assert_eq!(a_node.classification, Contracting);
a_node.value = Value(b_region);
assert_eq!(a_data.classification, Contracting);
a_data.value = Value(b_region);
true // changed
}
@ -927,34 +903,34 @@ impl RegionVarBindings {
}
Value(a_region) => {
match a_node.classification {
match a_data.classification {
Expanding => {
check_node(self, a_vid, a_node, a_region, b_region)
check_node(self, a_vid, a_data, a_region, b_region)
}
Contracting => {
adjust_node(self, a_vid, a_node, a_region, b_region)
adjust_node(self, a_vid, a_data, a_region, b_region)
}
}
}
};
fn check_node(this: &mut RegionVarBindings,
fn check_node(this: &RegionVarBindings,
a_vid: RegionVid,
a_node: &mut GraphNode,
a_data: &mut VarData,
a_region: Region,
b_region: Region)
-> bool {
if !this.is_subregion_of(a_region, b_region) {
debug!("Setting %? to ErrorValue: %? not subregion of %?",
a_vid, a_region, b_region);
a_node.value = ErrorValue;
a_data.value = ErrorValue;
}
false
}
fn adjust_node(this: &mut RegionVarBindings,
fn adjust_node(this: &RegionVarBindings,
a_vid: RegionVid,
a_node: &mut GraphNode,
a_data: &mut VarData,
a_region: Region,
b_region: Region)
-> bool {
@ -965,14 +941,14 @@ impl RegionVarBindings {
} else {
debug!("Contracting value of %? from %? to %?",
a_vid, a_region, glb);
a_node.value = Value(glb);
a_data.value = Value(glb);
true
}
}
Err(_) => {
debug!("Setting %? to ErrorValue: no glb of %?, %?",
a_vid, a_region, b_region);
a_node.value = ErrorValue;
a_data.value = ErrorValue;
false
}
}
@ -980,16 +956,12 @@ impl RegionVarBindings {
}
fn collect_concrete_region_errors(
&mut self,
&self,
graph: &Graph,
errors: &mut OptVec<RegionResolutionError>)
{
let num_edges = graph.edges.len();
for uint::range(0, num_edges) |edge_idx| {
let edge = &graph.edges[edge_idx];
let origin = self.constraints.get_copy(&edge.constraint);
let (sub, sup) = match edge.constraint {
for self.constraints.iter().advance |(constraint, _)| {
let (sub, sup) = match *constraint {
ConstrainVarSubVar(*) |
ConstrainRegSubVar(*) |
ConstrainVarSubReg(*) => {
@ -1006,15 +978,17 @@ impl RegionVarBindings {
debug!("ConcreteFailure: !(sub <= sup): sub=%?, sup=%?",
sub, sup);
let origin = self.constraints.get_copy(constraint);
errors.push(ConcreteFailure(origin, sub, sup));
}
}
fn extract_values_and_collect_conflicts(
&mut self,
&self,
graph: &Graph,
var_data: &[VarData],
errors: &mut OptVec<RegionResolutionError>)
-> ~[GraphNodeValue]
-> ~[VarValue]
{
debug!("extract_values_and_collect_conflicts()");
@ -1029,10 +1003,10 @@ impl RegionVarBindings {
// idea is to report errors that derive from independent
// regions of the graph, but not those that derive from
// overlapping locations.
let mut dup_vec = graph.nodes.map(|_| uint::max_value);
let mut dup_vec = vec::from_elem(self.num_vars(), uint::max_value);
graph.nodes.iter().enumerate().transform(|(idx, node)| {
match node.value {
for uint::range(0, self.num_vars()) |idx| {
match var_data[idx].value {
Value(_) => {
/* Inference successful */
}
@ -1067,26 +1041,27 @@ impl RegionVarBindings {
this portion of the code and think hard about it. =) */
let node_vid = RegionVid { id: idx };
match node.classification {
match var_data[idx].classification {
Expanding => {
self.collect_error_for_expanding_node(
graph, dup_vec, node_vid, errors);
graph, var_data, dup_vec, node_vid, errors);
}
Contracting => {
self.collect_error_for_contracting_node(
graph, dup_vec, node_vid, errors);
graph, var_data, dup_vec, node_vid, errors);
}
}
}
}
}
node.value
}).collect()
vec::from_fn(self.num_vars(), |idx| var_data[idx].value)
}
fn collect_error_for_expanding_node(
&mut self,
&self,
graph: &Graph,
var_data: &[VarData],
dup_vec: &mut [uint],
node_idx: RegionVid,
errors: &mut OptVec<RegionResolutionError>)
@ -1094,9 +1069,11 @@ impl RegionVarBindings {
// Errors in expanding nodes result from a lower-bound that is
// not contained by an upper-bound.
let (lower_bounds, lower_dup) =
self.collect_concrete_regions(graph, node_idx, Incoming, dup_vec);
self.collect_concrete_regions(graph, var_data, node_idx,
graph::Incoming, dup_vec);
let (upper_bounds, upper_dup) =
self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec);
self.collect_concrete_regions(graph, var_data, node_idx,
graph::Outgoing, dup_vec);
if lower_dup || upper_dup {
return;
@ -1127,8 +1104,9 @@ impl RegionVarBindings {
}
fn collect_error_for_contracting_node(
&mut self,
&self,
graph: &Graph,
var_data: &[VarData],
dup_vec: &mut [uint],
node_idx: RegionVid,
errors: &mut OptVec<RegionResolutionError>)
@ -1136,7 +1114,8 @@ impl RegionVarBindings {
// Errors in contracting nodes result from two upper-bounds
// that have no intersection.
let (upper_bounds, dup_found) =
self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec);
self.collect_concrete_regions(graph, var_data, node_idx,
graph::Outgoing, dup_vec);
if dup_found {
return;
@ -1168,8 +1147,9 @@ impl RegionVarBindings {
upper_bounds.map(|x| x.region).repr(self.tcx)));
}
fn collect_concrete_regions(&mut self,
fn collect_concrete_regions(&self,
graph: &Graph,
var_data: &[VarData],
orig_node_idx: RegionVid,
dir: Direction,
dup_vec: &mut [uint])
@ -1194,7 +1174,7 @@ impl RegionVarBindings {
while !state.stack.is_empty() {
let node_idx = state.stack.pop();
let classification = graph.nodes[node_idx.to_uint()].classification;
let classification = var_data[node_idx.to_uint()].classification;
// check whether we've visited this node on some previous walk
if dup_vec[node_idx.to_uint()] == uint::max_value {
@ -1210,8 +1190,8 @@ impl RegionVarBindings {
// figure out the direction from which this node takes its
// values, and search for concrete regions etc in that direction
let dir = match classification {
Expanding => Incoming,
Contracting => Outgoing
Expanding => graph::Incoming,
Contracting => graph::Outgoing,
};
process_edges(self, &mut state, graph, node_idx, dir);
@ -1220,15 +1200,16 @@ impl RegionVarBindings {
let WalkState {result, dup_found, _} = state;
return (result, dup_found);
fn process_edges(this: &mut RegionVarBindings,
fn process_edges(this: &RegionVarBindings,
state: &mut WalkState,
graph: &Graph,
source_vid: RegionVid,
dir: Direction) {
debug!("process_edges(source_vid=%?, dir=%?)", source_vid, dir);
for this.each_edge(graph, source_vid, dir) |edge| {
match edge.constraint {
let source_node_index = NodeIndex(source_vid.to_uint());
for graph.each_adjacent_edge(source_node_index, dir) |_, edge| {
match edge.data {
ConstrainVarSubVar(from_vid, to_vid) => {
let opp_vid =
if from_vid == source_vid {to_vid} else {from_vid};
@ -1241,7 +1222,7 @@ impl RegionVarBindings {
ConstrainVarSubReg(_, region) => {
state.result.push(RegionAndOrigin {
region: region,
origin: this.constraints.get_copy(&edge.constraint)
origin: this.constraints.get_copy(&edge.data)
});
}
@ -1251,42 +1232,40 @@ impl RegionVarBindings {
}
}
pub fn each_edge(&self,
graph: &Graph,
node_idx: RegionVid,
dir: Direction,
op: &fn(edge: &GraphEdge) -> bool)
-> bool {
let mut edge_idx =
graph.nodes[node_idx.to_uint()].head_edge[dir as uint];
while edge_idx != uint::max_value {
let edge_ptr = &graph.edges[edge_idx];
if !op(edge_ptr) {
return false;
fn iterate_until_fixed_point(&self,
tag: &str,
body: &fn(constraint: &Constraint) -> bool) {
let mut iteration = 0;
let mut changed = true;
while changed {
changed = false;
iteration += 1;
debug!("---- %s Iteration #%u", tag, iteration);
for self.constraints.iter().advance |(constraint, _)| {
let edge_changed = body(constraint);
if edge_changed {
debug!("Updated due to constraint %s",
constraint.repr(self.tcx));
changed = true;
}
}
edge_idx = edge_ptr.next_edge[dir as uint];
}
return true;
debug!("---- %s Complete after %u iteration(s)", tag, iteration);
}
}
fn iterate_until_fixed_point(
tag: ~str,
graph: &mut Graph,
body: &fn(nodes: &mut [GraphNode], edge: &GraphEdge) -> bool)
{
let mut iteration = 0;
let mut changed = true;
let num_edges = graph.edges.len();
while changed {
changed = false;
iteration += 1;
debug!("---- %s Iteration #%u", tag, iteration);
for uint::range(0, num_edges) |edge_idx| {
changed |= body(graph.nodes, &graph.edges[edge_idx]);
debug!(" >> Change after edge #%?: %?",
edge_idx, graph.edges[edge_idx]);
impl Repr for Constraint {
fn repr(&self, tcx: ty::ctxt) -> ~str {
match *self {
ConstrainVarSubVar(a, b) => fmt!("ConstrainVarSubVar(%s, %s)",
a.repr(tcx), b.repr(tcx)),
ConstrainRegSubVar(a, b) => fmt!("ConstrainRegSubVar(%s, %s)",
a.repr(tcx), b.repr(tcx)),
ConstrainVarSubReg(a, b) => fmt!("ConstrainVarSubReg(%s, %s)",
a.repr(tcx), b.repr(tcx)),
ConstrainRegSubReg(a, b) => fmt!("ConstrainRegSubReg(%s, %s)",
a.repr(tcx), b.repr(tcx)),
}
}
debug!("---- %s Complete after %u iteration(s)", tag, iteration);
}

View File

@ -751,6 +751,12 @@ impl Repr for typeck::method_param {
}
}
impl Repr for ty::RegionVid {
fn repr(&self, tcx: ctxt) -> ~str {
fmt!("%?", *self)
}
}
impl Repr for ty::TraitStore {
fn repr(&self, tcx: ctxt) -> ~str {
match self {