about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2011-08-08 19:38:19 -0700
committerGraydon Hoare <graydon@mozilla.com>2011-08-08 19:41:54 -0700
commitb2cac5afa36d55818f741f48029d350da35be511 (patch)
tree11ebd3b451ca76c2f548a78faaf3502b04dc23fe /src/comp
parentf7749b16087182813afa715fa828bb674ce9ef72 (diff)
downloadrust-b2cac5afa36d55818f741f48029d350da35be511.tar.gz
rust-b2cac5afa36d55818f741f48029d350da35be511.zip
Implement typestate checking for move-mode args. Un-XFAIL compile-fail/move-arg.rs.
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/middle/tstate/auxiliary.rs45
-rw-r--r--src/comp/middle/tstate/pre_post_conditions.rs29
-rw-r--r--src/comp/middle/tstate/states.rs64
3 files changed, 110 insertions, 28 deletions
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index 2b08dde75c6..92a6cf9ae74 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1093,14 +1093,45 @@ fn locals_to_bindings(locals : &(@local)[]) -> binding[] {
     ivec::map(local_to_bindings, locals)
 }
 
-fn anon_bindings(es : &(@expr)[]) -> binding[] {
-    fn expr_to_initializer(e : &@expr) -> initializer {
-        {op: init_assign, expr: e}
+fn callee_modes(fcx: &fn_ctxt, callee: node_id) -> ty::mode[] {
+    let ty = ty::type_autoderef(fcx.ccx.tcx,
+                                ty::node_id_to_type(fcx.ccx.tcx, callee));
+    alt ty::struct(fcx.ccx.tcx, ty) {
+      ty::ty_fn(_, args, _, _, _)
+      | ty::ty_native_fn(_, args, _) {
+        let modes = ~[];
+        for arg: ty::arg in args {
+            modes += ~[arg.mode];
+        }
+        ret modes;
+      }
+      _ {
+        // Shouldn't happen; callee should be ty_fn.
+        fcx.ccx.tcx.sess.bug("non-fn callee type in callee_modes: "
+                             + util::ppaux::ty_to_str(fcx.ccx.tcx, ty));
+      }
+   }
+}
+
+fn callee_arg_init_ops(fcx: &fn_ctxt, callee: node_id) -> init_op[] {
+    fn mode_to_op(m: &ty::mode) -> init_op {
+        alt m {
+          ty::mo_move. { init_move }
+          _ { init_assign }
+        }
+    }
+    ivec::map(mode_to_op, callee_modes(fcx, callee))
+}
+
+fn anon_bindings(ops: &init_op[], es : &(@expr)[]) -> binding[] {
+    let bindings: binding[] = ~[];
+    let i = 0;
+    for op: init_op in ops {
+        bindings += ~[{lhs: ~[],
+                       rhs: some({op:op, expr: es.(i)})}];
+        i += 1;
     }
-    ret ivec::map(fn (e : &@expr) -> binding {
-        {lhs: ~[],
-         rhs: some(expr_to_initializer(e)) } },
-                  es);
+    ret bindings;
 }
 
 //
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 473e1cfbf2b..bade71525e3 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -316,6 +316,18 @@ fn handle_var(fcx: &fn_ctxt, rslt: &pre_and_post, id: node_id, name: ident) {
     }
 }
 
+fn forget_args_moved_in(fcx: &fn_ctxt, parent: &@expr,
+                        modes: &ty::mode[],
+                        operands: &(@expr)[]) {
+    let i = 0;
+    for mode: ty::mode in modes {
+        if mode == ty::mo_move {
+            forget_in_postcond(fcx, parent.id, operands.(i).id);
+        }
+        i += 1;
+    }
+}
+
 /* Fills in annotations as a side effect. Does not rebuild the expr */
 fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
     let enclosing = fcx.enclosing;
@@ -336,6 +348,8 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
             require(i, expr_pp(fcx.ccx, e));
         }
 
+        forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
+                             operands);
 
         /* if this is a failing call, its postcondition sets everything */
         alt controlflow_expr(fcx.ccx, operator) {
@@ -347,6 +361,8 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
         let /* copy */args = operands;
         args += ~[operator];
         find_pre_post_exprs(fcx, args, e.id);
+        forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
+                             operands);
       }
       expr_vec(args, _, _) { find_pre_post_exprs(fcx, args, e.id); }
       expr_path(p) {
@@ -544,14 +560,21 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
 
       expr_bind(operator, maybe_args) {
         let args = ~[];
-        for expr_opt: option::t[@expr]  in maybe_args {
+        let cmodes = callee_modes(fcx, operator.id);
+        let modes = ~[];
+        let i = 0;
+        for expr_opt: option::t[@expr] in maybe_args {
             alt expr_opt {
               none. {/* no-op */ }
-              some(expr) { args += ~[expr]; }
+              some(expr) {
+                modes += ~[cmodes.(i)];
+                args += ~[expr];
+              }
             }
+            i += 1;
         }
         args += ~[operator]; /* ??? order of eval? */
-
+        forget_args_moved_in(fcx, e, modes, args);
         find_pre_post_exprs(fcx, args, e.id);
       }
       expr_break. { clear_pp(expr_pp(fcx.ccx, e)); }
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index 0f7d920e6ce..420ea4cad8c 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -45,9 +45,6 @@ fn handle_move_or_copy(fcx: &fn_ctxt, post: &poststate, rhs_path: &path,
         // not a local -- do nothing
       }
     }
-    if (init_op == init_move) {
-        forget_in_poststate(fcx, post, rhs_id);
-    }
 }
 
 fn seq_states(fcx: &fn_ctxt, pres: &prestate, bindings: &binding[])
@@ -67,10 +64,14 @@ fn seq_states(fcx: &fn_ctxt, pres: &prestate, bindings: &binding[])
                     handle_move_or_copy(fcx, post, p, an_init.expr.id, i,
                                         an_init.op);
                   }
-                  _ {}
+                  _ { }
                 }
                 set_in_poststate_ident(fcx, i.node, i.ident, post);
             }
+            // Forget the RHS if we just moved it.
+            if an_init.op == init_move {
+                forget_in_poststate(fcx, post, an_init.expr.id);
+            }
           }
           none {
             for i: inst in b.lhs {
@@ -162,16 +163,24 @@ fn find_pre_post_state_two(fcx: &fn_ctxt, pres: &prestate, lhs: &@expr,
 }
 
 fn find_pre_post_state_call(fcx: &fn_ctxt, pres: &prestate, a: &@expr,
-                            id: node_id, bs: &(@expr)[], cf: controlflow) ->
-   bool {
+                            id: node_id, ops: &init_op[], bs: &(@expr)[],
+                            cf: controlflow) -> bool {
     let changed = find_pre_post_state_expr(fcx, pres, a);
-    ret find_pre_post_state_exprs(fcx, expr_poststate(fcx.ccx, a), id, bs, cf)
+    if ivec::len(bs) != ivec::len(ops) {
+        fcx.ccx.tcx.sess.span_bug(a.span,
+                                  #fmt("mismatched arg lengths: \
+                                        %u exprs vs. %u ops",
+                                       ivec::len(bs), ivec::len(ops)));
+    }
+    ret find_pre_post_state_exprs(fcx, expr_poststate(fcx.ccx, a), id,
+                                  ops, bs, cf)
             || changed;
 }
 
 fn find_pre_post_state_exprs(fcx: &fn_ctxt, pres: &prestate, id: node_id,
-                             es: &(@expr)[], cf: controlflow) -> bool {
-    let rs = seq_states(fcx, pres, anon_bindings(es));
+                             ops: &init_op[], es: &(@expr)[],
+                             cf: controlflow) -> bool {
+    let rs = seq_states(fcx, pres, anon_bindings(ops, es));
     let changed = rs.changed | set_prestate_ann(fcx.ccx, id, pres);
     /* if this is a failing call, it sets everything as initialized */
     alt cf {
@@ -304,23 +313,39 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
 
     alt e.node {
       expr_vec(elts, _, _) {
-        ret find_pre_post_state_exprs(fcx, pres, e.id, elts, return);
+        ret find_pre_post_state_exprs(fcx, pres, e.id,
+                                      ivec::init_elt(init_assign,
+                                                     ivec::len(elts)),
+                                      elts, return);
       }
       expr_call(operator, operands) {
-        ret find_pre_post_state_call(fcx, pres, operator, e.id, operands,
+        ret find_pre_post_state_call(fcx, pres, operator, e.id,
+                                     callee_arg_init_ops(fcx, operator.id),
+                                     operands,
                                      controlflow_expr(fcx.ccx, operator));
       }
       expr_spawn(_, _, operator, operands) {
-        ret find_pre_post_state_call(fcx, pres, operator, e.id, operands,
-                                     return);
+        ret find_pre_post_state_call(fcx, pres, operator, e.id,
+                                     callee_arg_init_ops(fcx, operator.id),
+                                     operands, return);
       }
       expr_bind(operator, maybe_args) {
         let args = ~[];
-        for a_opt: option::t[@expr]  in maybe_args {
-            alt a_opt { none. {/* no-op */ } some(a) { args += ~[a]; } }
+        let callee_ops = callee_arg_init_ops(fcx, operator.id);
+        let ops = ~[];
+        let i = 0;
+        for a_opt: option::t[@expr] in maybe_args {
+            alt a_opt {
+              none. {/* no-op */ }
+              some(a) {
+                ops += ~[callee_ops.(i)];
+                args += ~[a];
+              }
+            }
+            i += 1;
         }
-
-        ret find_pre_post_state_call(fcx, pres, operator, e.id, args, return);
+        ret find_pre_post_state_call(fcx, pres, operator, e.id, ops, args,
+                                     return);
       }
       expr_path(_) { ret pure_exp(fcx.ccx, e.id, pres); }
       expr_log(_, ex) {
@@ -347,7 +372,10 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
       }
       expr_rec(fields, maybe_base) {
         let changed =
-            find_pre_post_state_exprs(fcx, pres, e.id, field_exprs(fields),
+            find_pre_post_state_exprs(fcx, pres, e.id,
+                                      ivec::init_elt(init_assign,
+                                                     ivec::len(fields)),
+                                      field_exprs(fields),
                                       return);
         alt maybe_base {
           none. {/* do nothing */ }