aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGraydon Hoare <[email protected]>2011-03-03 15:19:26 -0800
committerGraydon Hoare <[email protected]>2011-03-03 15:19:26 -0800
commit5c7db0cde15adfda5b43112ee86d4bfe3bd9ee82 (patch)
treeb4d391d5b0196cbcbd4f433742c5d6fad5e1e846 /src
parentTurn on Valgrind for Mac (diff)
downloadrust-5c7db0cde15adfda5b43112ee86d4bfe3bd9ee82.tar.xz
rust-5c7db0cde15adfda5b43112ee86d4bfe3bd9ee82.zip
Rewrite rustboot's flow-graph wiring passes to be less awful. Add test for nested control-flow constructs.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile1
-rw-r--r--src/boot/me/typestate.ml352
-rw-r--r--src/test/run-pass/typestate-cfg-nesting.rs26
3 files changed, 131 insertions, 248 deletions
diff --git a/src/Makefile b/src/Makefile
index 37726b6e..49cc0456 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -521,6 +521,7 @@ TEST_XFAILS_RUSTC := $(CONST_TAG_XFAILS) \
task-life-0.rs \
threads.rs \
type-sizes.rs \
+ typestate-cfg-nesting.rs \
use-import-export.rs \
user.rs \
utf8.rs \
diff --git a/src/boot/me/typestate.ml b/src/boot/me/typestate.ml
index ea0204f3..1c9c90ea 100644
--- a/src/boot/me/typestate.ml
+++ b/src/boot/me/typestate.ml
@@ -24,7 +24,6 @@ type typestate_tables =
ts_prestates: (node_id,Bits.t) Hashtbl.t;
ts_poststates: (node_id,Bits.t) Hashtbl.t;
ts_graph: node_graph;
- ts_siblings: sibling_map;
ts_stmts: Ast.stmt Stack.t;
ts_maxid: int ref;
}
@@ -38,7 +37,6 @@ let new_tables _ =
ts_poststates = Hashtbl.create 0;
ts_prestates = Hashtbl.create 0;
ts_graph = Hashtbl.create 0;
- ts_siblings = Hashtbl.create 0;
ts_stmts = Stack.create ();
ts_maxid = ref 0 }
;;
@@ -790,279 +788,143 @@ let show_node cx graph s i =
s (int_of_node i) (lset_fmt (Hashtbl.find graph i)))
;;
-let graph_sequence_building_visitor
- (cx:ctxt)
- (tables_stack:typestate_tables Stack.t)
- (inner:Walk.visitor)
- : Walk.visitor =
+let add_flow_edges (graph:node_graph) (n:node_id) (dsts:node_id list) : unit =
+ if Hashtbl.mem graph n
+ then
+ let existing = Hashtbl.find graph n in
+ Hashtbl.replace graph n (lset_union existing dsts)
+ else
+ Hashtbl.add graph n dsts
+;;
- let tables _ = Stack.top tables_stack in
+let rec build_flow_graph_for_stmt
+ (graph:node_graph)
+ (predecessors:node_id list)
+ (s:Ast.stmt)
+ : node_id list =
- (* Flow each stmt to its sequence-successor. *)
- let visit_stmts stmts =
- let ts = tables () in
- let graph = ts.ts_graph in
- let sibs = ts.ts_siblings in
- let len = Array.length stmts in
- for i = 0 to len - 2
- do
- let stmt = stmts.(i) in
- let next = stmts.(i+1) in
- log cx "sequential stmt edge %d -> %d"
- (int_of_node stmt.id) (int_of_node next.id);
- htab_put graph stmt.id [next.id];
- htab_put sibs stmt.id next.id;
- done;
- (* Flow last node to nowhere. *)
- if len > 0
- then htab_put graph stmts.(len-1).id []
+ let connect ps qs =
+ List.iter
+ (fun pred -> add_flow_edges graph pred qs)
+ ps
in
- let visit_stmt_pre s =
- (* Sequence the prelude nodes on special stmts. *)
- begin
- match s.node with
- Ast.STMT_while sw ->
- let (stmts, _) = sw.Ast.while_lval in
- visit_stmts stmts
- | _ -> ()
- end;
- inner.Walk.visit_stmt_pre s
+ let seq ps (ss:Ast.stmt array) =
+ build_flow_graph_for_stmts graph ps ss
in
- let visit_block_pre b =
- visit_stmts b.node;
- inner.Walk.visit_block_pre b
+ let blk ps b =
+ connect ps [b.id];
+ seq [b.id] b.node
in
- { inner with
- Walk.visit_stmt_pre = visit_stmt_pre;
- Walk.visit_block_pre = visit_block_pre }
-;;
-
-let add_flow_edges (graph:node_graph) (n:node_id) (dsts:node_id list) : unit =
- let existing = Hashtbl.find graph n in
- Hashtbl.replace graph n (lset_union existing dsts)
-;;
+ let first ss =
+ if Array.length ss = 0
+ then []
+ else [ss.(0).id]
+ in
-let remove_flow_edges
- (graph:node_graph)
- (n:node_id)
- (dsts:node_id list)
- : unit =
- let existing = Hashtbl.find graph n in
- Hashtbl.replace graph n (lset_diff existing dsts)
-;;
+ connect [s.id] [];
+ let outs =
+ match s.node with
+ | Ast.STMT_while sw ->
+ let (pre_loop_stmts, _) = sw.Ast.while_lval in
+ let body = sw.Ast.while_body in
+ let preloop_end = seq [s.id] pre_loop_stmts in
+ connect predecessors [s.id];
+ connect (blk preloop_end body) (first pre_loop_stmts);
+ preloop_end
+
+ | Ast.STMT_for sf ->
+ let body_end = blk [s.id] sf.Ast.for_body in
+ connect predecessors [s.id];
+ connect body_end (first sf.Ast.for_body.node);
+ body_end
+
+ | Ast.STMT_for_each sfe ->
+ let head_end = blk [s.id] sfe.Ast.for_each_head in
+ let body_end = blk head_end sfe.Ast.for_each_body in
+ connect predecessors [s.id];
+ connect body_end (first sfe.Ast.for_each_head.node);
+ body_end
+
+ | Ast.STMT_if sif ->
+ connect predecessors [s.id];
+ (blk [s.id] sif.Ast.if_then) @
+ (match sif.Ast.if_else with
+ None -> [s.id]
+ | Some els -> blk [s.id] els)
+
+ | Ast.STMT_alt_tag sat ->
+ connect predecessors [s.id];
+ Array.fold_left
+ (fun ends {node=(_, b); id=_} -> (blk [s.id] b) @ ends)
+ [] sat.Ast.alt_tag_arms
+
+ | Ast.STMT_block b ->
+ blk predecessors b
+
+ | _ ->
+ connect predecessors [s.id];
+ [s.id]
+ in
+ connect outs [];
+ outs
-let last_id (nodes:('a identified) array) : node_id =
- let len = Array.length nodes in
- nodes.(len-1).id
+and build_flow_graph_for_stmts
+ (graph:node_graph)
+ (predecessors:node_id list)
+ (ss:Ast.stmt array)
+ : node_id list =
+ Array.fold_left (build_flow_graph_for_stmt graph) predecessors ss
;;
-let last_id_or_block_id (block:Ast.block) : node_id =
- let len = Array.length block.node in
- if len = 0
- then block.id
- else last_id block.node
-;;
-let graph_general_block_structure_building_visitor
+let graph_building_visitor
(cx:ctxt)
(tables_stack:typestate_tables Stack.t)
(inner:Walk.visitor)
: Walk.visitor =
let tables _ = Stack.top tables_stack in
+ let graph _ = (tables()).ts_graph in
+ let blk b =
+ add_flow_edges (graph()) b.id [];
+ ignore (build_flow_graph_for_stmts (graph()) [b.id] b.node)
+ in
- let visit_stmt_pre s =
- let ts = tables () in
- let stmts = ts.ts_stmts in
- Stack.push s stmts;
- inner.Walk.visit_stmt_pre s
+ let visit_mod_item_pre n p i =
+ begin
+ match i.node.Ast.decl_item with
+ Ast.MOD_ITEM_fn fn -> blk fn.Ast.fn_body
+ | _ -> ()
+ end;
+ inner.Walk.visit_mod_item_pre n p i
in
- let visit_stmt_post s =
- let ts = tables () in
- let stmts = ts.ts_stmts in
- inner.Walk.visit_stmt_post s;
- ignore (Stack.pop stmts)
+ let visit_obj_fn_pre obj ident fn =
+ blk fn.node.Ast.fn_body;
+ inner.Walk.visit_obj_fn_pre obj ident fn
in
- let show_node =
- fun n id -> show_node cx (tables()).ts_graph n id
+ let visit_obj_drop_pre obj b =
+ blk b;
+ inner.Walk.visit_obj_drop_pre obj b
in
let visit_block_pre b =
- begin
- let ts = tables () in
- let graph = ts.ts_graph in
- let sibs = ts.ts_siblings in
- let stmts = ts.ts_stmts in
- let len = Array.length b.node in
- let _ = htab_put graph b.id
- (if len > 0 then [b.node.(0).id] else [])
- in
-
- (*
- * If block has len,
- * then flow block to block.node.(0) and block.node.(len-1) to dsts
- * else flow block to dsts
- *
- * so AST:
- *
- * block#n{ stmt#0 ... stmt#k };
- * stmt#j;
- *
- * turns into graph:
- *
- * block#n -> stmt#0 -> ... -> stmt#k -> stmt#j
- *
- *)
- if Stack.is_empty stmts
- then ()
- else
- let s = Stack.top stmts in
- add_flow_edges graph s.id [b.id];
- match htab_search sibs s.id with
- None -> ()
- | Some sib_id ->
- if len > 0
- then
- add_flow_edges graph (last_id b.node) [sib_id]
- else
- add_flow_edges graph b.id [sib_id]
- end;
- show_node "block" b.id;
+ if Hashtbl.mem cx.ctxt_block_is_loop_body b.id
+ then blk b;
inner.Walk.visit_block_pre b
in
{ inner with
- Walk.visit_stmt_pre = visit_stmt_pre;
- Walk.visit_stmt_post = visit_stmt_post;
+ Walk.visit_mod_item_pre = visit_mod_item_pre;
+ Walk.visit_obj_fn_pre = visit_obj_fn_pre;
+ Walk.visit_obj_drop_pre = visit_obj_drop_pre;
Walk.visit_block_pre = visit_block_pre }
-;;
-
-
-let graph_special_block_structure_building_visitor
- (cx:ctxt)
- (tables_stack:typestate_tables Stack.t)
- (inner:Walk.visitor)
- : Walk.visitor =
- let tables _ = Stack.top tables_stack in
-
- let visit_stmt_pre s =
- begin
- match s.node with
-
- Ast.STMT_if sif ->
- let ts = tables () in
- let graph = ts.ts_graph in
- let cond_id = s.id in
- let succ = Hashtbl.find graph cond_id in
- let then_id = sif.Ast.if_then.id in
- let then_end_id = last_id_or_block_id sif.Ast.if_then in
- let show_node = show_node cx graph in
- let succ = List.filter (fun x -> not (x = then_id)) succ in
- show_node "initial cond" cond_id;
- show_node "initial then" then_id;
- show_node "initial then_end" then_end_id;
- begin
- match sif.Ast.if_else with
- None ->
- Hashtbl.replace graph cond_id (then_id :: succ);
- (* Kill residual messed-up block wiring.*)
- remove_flow_edges graph then_end_id [then_id];
- show_node "cond" cond_id;
- show_node "then" then_id;
- show_node "then_end" then_end_id;
-
- | Some e ->
- let else_id = e.id in
- let succ =
- List.filter (fun x -> not (x = else_id)) succ
- in
- let else_end_id = last_id_or_block_id e in
- show_node "initial else" else_id;
- show_node "initial else_end" else_end_id;
- Hashtbl.replace graph cond_id [then_id; else_id];
- Hashtbl.replace graph then_end_id succ;
- Hashtbl.replace graph else_end_id succ;
-
- (* Kill residual messed-up block wiring.*)
- remove_flow_edges graph then_end_id [then_id];
- remove_flow_edges graph else_id [then_id];
- remove_flow_edges graph else_end_id [then_id];
- show_node "cond" cond_id;
- show_node "then" then_id;
- show_node "then_end" then_end_id;
- show_node "else" else_id;
- show_node "else_end" else_end_id;
- end;
-
- | Ast.STMT_while sw ->
- (* There are a bunch of rewirings to do on 'while' nodes. *)
-
- begin
- let ts = tables () in
- let graph = ts.ts_graph in
- let dsts = Hashtbl.find graph s.id in
- let body = sw.Ast.while_body in
- let succ_stmts =
- List.filter (fun x -> not (x = body.id)) dsts
- in
-
- let (pre_loop_stmts, _) = sw.Ast.while_lval in
- let loop_head_id =
- (* Splice loop prelude into flow graph, save loop-head
- * node.
- *)
- let slen = Array.length pre_loop_stmts in
- if slen > 0
- then
- begin
- let pre_loop_begin = pre_loop_stmts.(0).id in
- let pre_loop_end = last_id pre_loop_stmts in
- remove_flow_edges graph s.id [body.id];
- add_flow_edges graph s.id [pre_loop_begin];
- add_flow_edges graph pre_loop_end [body.id];
- pre_loop_end
- end
- else
- body.id
- in
-
- (* Always flow s into the loop prelude; prelude may end
- * loop.
- *)
- remove_flow_edges graph s.id succ_stmts;
- add_flow_edges graph loop_head_id succ_stmts;
-
- (* Flow loop-end to loop-head. *)
- let loop_end = last_id_or_block_id body in
- add_flow_edges graph loop_end [loop_head_id]
- end
-
- | Ast.STMT_alt_tag at ->
- let ts = tables () in
- let graph = ts.ts_graph in
- let dsts = Hashtbl.find graph s.id in
- let arm_blocks =
- let arm_block_id { node = (_, block); id = _ } = block.id in
- Array.to_list (Array.map arm_block_id at.Ast.alt_tag_arms)
- in
- let succ_stmts =
- List.filter (fun x -> not (List.mem x arm_blocks)) dsts
- in
- remove_flow_edges graph s.id succ_stmts
-
- | _ -> ()
- end;
- inner.Walk.visit_stmt_post s
- in
- { inner with
- Walk.visit_stmt_pre = visit_stmt_pre }
;;
let find_roots
@@ -1631,13 +1493,7 @@ let process_crate
(condition_assigning_visitor cx tables_stack scopes
Walk.empty_visitor)));
(table_managed
- (graph_sequence_building_visitor cx tables_stack
- Walk.empty_visitor));
- (table_managed
- (graph_general_block_structure_building_visitor cx tables_stack
- Walk.empty_visitor));
- (table_managed
- (graph_special_block_structure_building_visitor cx tables_stack
+ (graph_building_visitor cx tables_stack
Walk.empty_visitor));
|]
in
diff --git a/src/test/run-pass/typestate-cfg-nesting.rs b/src/test/run-pass/typestate-cfg-nesting.rs
new file mode 100644
index 00000000..8f050646
--- /dev/null
+++ b/src/test/run-pass/typestate-cfg-nesting.rs
@@ -0,0 +1,26 @@
+
+fn f() {
+
+ auto x = 10;
+ auto y = 11;
+ if (true) {
+ alt (x) {
+ case (_) {
+ y = x;
+ }
+ }
+ } else {
+ }
+}
+
+fn main() {
+
+ auto x = 10;
+ auto y = 11;
+ if (true) {
+ while (false) {
+ y = x;
+ }
+ } else {
+ }
+}