about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2012-03-27 17:22:57 +0200
committerMarijn Haverbeke <marijnh@gmail.com>2012-03-27 17:22:57 +0200
commit73d6df32cd3f729120a28166218ff093d2c31cd2 (patch)
tree5f6b8e2d7385c306d1d1b5b830a1438ab243ee0d /src/rustc
parentb5a4fa9456683f40539f885d4e555b87384df09a (diff)
downloadrust-73d6df32cd3f729120a28166218ff093d2c31cd2.tar.gz
rust-73d6df32cd3f729120a28166218ff093d2c31cd2.zip
Emergency safe-ref-checker maintenance
It still has some big problems, but at least it more or less
understands block arguments now.

Closes #1925
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/alias.rs172
-rw-r--r--src/rustc/middle/trans/base.rs2
2 files changed, 92 insertions, 82 deletions
diff --git a/src/rustc/middle/alias.rs b/src/rustc/middle/alias.rs
index 32e5ef91524..3fe17f96bbd 100644
--- a/src/rustc/middle/alias.rs
+++ b/src/rustc/middle/alias.rs
@@ -99,8 +99,8 @@ fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
     let mut handled = true;
     alt ex.node {
       ast::expr_call(f, args, _) {
-        check_call(*cx, sc, f, args);
-        handled = false;
+        check_call(cx, sc, f, args, v);
+        visit_expr(cx, f, sc, v);
       }
       ast::expr_alt(input, arms, _) { check_alt(*cx, input, arms, sc, v); }
       ast::expr_for(decl, seq, blk) {
@@ -163,33 +163,6 @@ fn visit_block(cx: @ctx, b: ast::blk, sc: scope, v: vt<scope>) {
     visit::visit_expr_opt(b.node.expr, sc, v);
 }
 
-fn add_bindings_for_let(cx: ctx, &bs: [binding], loc: @ast::local) {
-    alt loc.node.init {
-      some(init) {
-        if init.op == ast::init_move {
-            err(cx, loc.span, "can not move into a by-reference binding");
-        }
-        let root = expr_root(cx, init.expr, false);
-        let root_var = path_def_id(cx, root.ex);
-        if is_none(root_var) {
-            err(cx, loc.span, "a reference binding can't be \
-                               rooted in a temporary");
-        }
-        for proot in pattern_roots(cx.tcx, root.mutbl, loc.node.pat) {
-            let bnd = mk_binding(cx, proot.id, proot.span, root_var,
-                                 unsafe_set(proot.mutbl));
-            // Don't implicitly copy explicit references
-            bnd.copied = not_allowed;
-            bs += [bnd];
-        }
-      }
-      _ {
-        err(cx, loc.span, "by-reference bindings must be initialized");
-      }
-    }
-}
-
-
 fn cant_copy(cx: ctx, b: binding) -> bool {
     alt b.copied {
       not_allowed { ret true; }
@@ -209,47 +182,67 @@ fn cant_copy(cx: ctx, b: binding) -> bool {
     } else { ret true; }
 }
 
-fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
-    -> [binding] {
+// FIXME this is a really awful hack
+fn local_id_for_args(cx: ctx, args: [@ast::expr]) -> uint {
+    for vec::each(args) {|arg|
+        alt arg.node {
+          ast::expr_fn_block(decl, _) | ast::expr_fn(_, decl, _, _) |
+          ast::expr_loop_body(@{node: ast::expr_fn_block(decl, _), _}) {
+            if decl.inputs.len() > 0u {
+                ret local_id_of_node(cx, decl.inputs[0].id);
+            }
+          }
+          _ {}
+        }
+    }
+    0xFFFFFFFFu
+}
+
+fn check_call(cx: @ctx, sc: scope, f: @ast::expr, args: [@ast::expr],
+              v: vt<scope>) {
     let fty = ty::expr_ty(cx.tcx, f);
     let arg_ts = ty::ty_fn_args(fty);
-    let mut mut_roots: [{arg: uint, node: node_id}] = [];
+    let mut mut_roots: [{arg: @ast::expr, node: node_id}] = [];
     let mut bindings = [];
-    let mut i = 0u;
-    for arg_t: ty::arg in arg_ts {
-        let arg = args[i];
-        let root = expr_root(cx, arg, false);
+    let mut blocks = [], loc_id = local_id_for_args(*cx, args);
+    vec::iter2(args, arg_ts) {|arg, arg_t|
+        let root = expr_root(*cx, arg, false);
         alt ty::resolved_mode(cx.tcx, arg_t.mode) {
           ast::by_mutbl_ref {
-            alt path_def(cx, arg) {
+            alt path_def(*cx, arg) {
               some(def) {
                 let dnum = ast_util::def_id_of_def(def).node;
-                mut_roots += [{arg: i, node: dnum}];
+                mut_roots += [{arg: arg, node: dnum}];
               }
               _ { }
             }
           }
-          ast::by_ref | ast::by_val | ast::by_move | ast::by_copy { }
+          ast::by_ref | ast::by_val | ast::by_move | ast::by_copy {}
+        }
+        alt arg.node {
+          ast::expr_fn_block(_, _) { blocks += [arg]; }
+          ast::expr_loop_body(b) { blocks += [b]; }
+          _ {
+            let root_var = path_def_id(*cx, root.ex);
+            let arg_copied = alt ty::resolved_mode(cx.tcx, arg_t.mode) {
+              ast::by_move | ast::by_copy { copied }
+              ast::by_mutbl_ref { not_allowed }
+              ast::by_ref | ast::by_val { not_copied }
+            };
+            visit_expr(cx, arg, sc, v);
+            bindings += [@{node_id: arg.id,
+                           span: arg.span,
+                           root_var: root_var,
+                           local_id: loc_id,
+                           unsafe_tys: unsafe_set(root.mutbl),
+                           mut copied: arg_copied}];
+          }
         }
-        let root_var = path_def_id(cx, root.ex);
-        let arg_copied = alt ty::resolved_mode(cx.tcx, arg_t.mode) {
-          ast::by_move | ast::by_copy { copied }
-          ast::by_mutbl_ref { not_allowed }
-          ast::by_ref | ast::by_val { not_copied }
-        };
-        bindings += [@{node_id: arg.id,
-                       span: arg.span,
-                       root_var: root_var,
-                       local_id: 0u,
-                       unsafe_tys: unsafe_set(root.mutbl),
-                       mut copied: arg_copied}];
-        i += 1u;
     }
-    let f_may_close =
-        alt f.node {
-          ast::expr_path(_) { def_is_local_or_self(cx.tcx.def_map.get(f.id)) }
-          _ { true }
-        };
+    let f_may_close = alt f.node {
+      ast::expr_path(_) { def_is_local_or_self(cx.tcx.def_map.get(f.id)) }
+      _ { true }
+    };
     if f_may_close {
         let mut i = 0u;
         for b in bindings {
@@ -264,9 +257,9 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
               }
               _ {}
             }
-            if unsfe && cant_copy(cx, b) {
-                err(cx, f.span, #fmt["function may alias with argument \
-                                     %u, which is not immutably rooted", i]);
+            if unsfe && cant_copy(*cx, b) {
+                err(*cx, f.span, #fmt["function may alias with argument \
+                                      %u, which is not immutably rooted", i]);
             }
             i += 1u;
         }
@@ -274,33 +267,34 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
     let mut j = 0u;
     for b in bindings {
         for unsafe_ty in b.unsafe_tys {
-            let mut i = 0u;
-            for arg_t: ty::arg in arg_ts {
+            vec::iteri(arg_ts) {|i, arg_t|
                 let mut_alias =
                     (ast::by_mutbl_ref == ty::arg_mode(cx.tcx, arg_t));
-                if i != j &&
-                       ty_can_unsafely_include(cx, unsafe_ty, arg_t.ty,
-                                               mut_alias) &&
-                       cant_copy(cx, b) {
-                    err(cx, args[i].span,
-                        #fmt["argument %u may alias with argument %u, \
-                             which is not immutably rooted", i, j]);
+                alt args[i].node {
+                  ast::expr_fn_block(_, _) | ast::expr_loop_body(_) {}
+                  _ {
+                    if i != j && ty_can_unsafely_include(
+                        *cx, unsafe_ty, arg_t.ty, mut_alias) &&
+                       cant_copy(*cx, b) {
+                        err(*cx, args[i].span,
+                            #fmt["argument %u may alias with argument %u, \
+                                  which is not immutably rooted", i, j]);
+                    }
+                  }
                 }
-                i += 1u;
             }
         }
         j += 1u;
     }
-    // Ensure we're not passing a root by mut alias.
 
+    // Ensure we're not passing a root by mut alias.
     for {node: node, arg: arg} in mut_roots {
-        let mut i = 0u;
         for b in bindings {
-            if i != arg {
+            if b.node_id != arg.id {
                 alt b.root_var {
                   some(root) {
-                    if node == root && cant_copy(cx, b) {
-                        err(cx, args[arg].span,
+                    if node == root && cant_copy(*cx, b) {
+                        err(*cx, arg.span,
                             "passing a mut reference to a \
                              variable that roots another reference");
                         break;
@@ -309,10 +303,22 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
                   none { }
                 }
             }
-            i += 1u;
         }
     }
-    ret bindings;
+    // Check the bodies of block arguments against the current scope
+    if blocks.len() > 0u {
+        let inner_sc = {bs: bindings + sc.bs, invalid: sc.invalid};
+        for blk in blocks {
+            alt check blk.node {
+              ast::expr_fn_block(_, body) {
+                v.visit_block(body, inner_sc, v);
+              }
+            }
+        }
+        for binding in bindings {
+            test_scope(*cx, sc, binding, none);
+        }
+    }
 }
 
 fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
@@ -397,7 +403,7 @@ fn check_var(cx: ctx, ex: @ast::expr, p: @ast::path, id: ast::node_id,
                 }
             }
         } else if b.node_id == my_defnum {
-            test_scope(cx, sc, b, p);
+            test_scope(cx, sc, b, some(p));
         }
     }
 }
@@ -444,7 +450,7 @@ fn check_loop(cx: ctx, sc: scope, checker: fn()) {
     *sc.invalid = new_invalid;
 }
 
-fn test_scope(cx: ctx, sc: scope, b: binding, p: @ast::path) {
+fn test_scope(cx: ctx, sc: scope, b: binding, p: option<@ast::path>) {
     let mut prob = find_invalid(b.node_id, *sc.invalid);
     alt b.root_var {
       some(dn) {
@@ -463,8 +469,12 @@ fn test_scope(cx: ctx, sc: scope, b: binding, p: @ast::path) {
           overwritten { "overwriting " + ast_util::path_name(i.path) }
           val_taken { "taking the value of " + ast_util::path_name(i.path) }
         };
-        err(cx, i.sp, msg + " will invalidate reference " +
-            ast_util::path_name(p) + ", which is still used");
+        let refname = alt p {
+          some(pt) { "reference " + ast_util::path_name(pt) +
+                        ", which is still used" }
+          none { "an argument" }
+        };
+        err(cx, i.sp, msg + " will invalidate " + refname);
     }
 }
 
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index c03c5c756ac..f8baf6b93aa 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -3677,7 +3677,7 @@ fn trans_block_cleanups(bcx: block, cleanup_cx: block) ->
     let mut bcx = bcx;
     alt check cleanup_cx.kind {
       block_scope({cleanups, _}) {
-        vec::riter(cleanups) {|cu|
+        vec::riter(copy cleanups) {|cu|
             alt cu { clean(cfn) | clean_temp(_, cfn) { bcx = cfn(bcx); } }
         }
       }