diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index 50290ba752e..c41ce40a995 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -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; + impl RegionVarBindings { fn infer_variable_values(&mut self, errors: &mut OptVec) - -> ~[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) { - 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) - -> ~[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) @@ -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) @@ -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); } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 0aae41941cd..ae91713be04 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -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 {