about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-05-23 20:55:56 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-05-24 05:19:44 -0700
commitc9eb9ee6121b10927de80d32e448691388545b3e (patch)
treeac9961905de846366549694ea357317d3747cedc /src/rustc
parent6ca6a3b80be1c7a5638156638a8a11e514bc3ef3 (diff)
downloadrust-c9eb9ee6121b10927de80d32e448691388545b3e.tar.gz
rust-c9eb9ee6121b10927de80d32e448691388545b3e.zip
rewrite purity check to search through scope chain
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/borrowck.rs130
1 files changed, 67 insertions, 63 deletions
diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs
index ee01a0e89e2..85ee5b1c957 100644
--- a/src/rustc/middle/borrowck.rs
+++ b/src/rustc/middle/borrowck.rs
@@ -505,14 +505,11 @@ enum check_loan_ctxt = @{
     // we are in a ctor, we track the self id
     mut in_ctor: bool,
 
-    mut is_pure: purity_cause
+    mut declared_purity: ast::purity
 };
 
 // if we are enforcing purity, why are we doing so?
 enum purity_cause {
-    // not enforcing purity:
-    pc_impure,
-
     // enforcing purity because fn was declared pure:
     pc_declaration,
 
@@ -529,7 +526,7 @@ fn check_loans(bccx: borrowck_ctxt,
                                  req_maps: req_maps,
                                  reported: int_hash(),
                                  mut in_ctor: false,
-                                 mut is_pure: pc_impure});
+                                 mut declared_purity: ast::impure_fn});
     let vt = visit::mk_vt(@{visit_expr: check_loans_in_expr,
                             visit_block: check_loans_in_block,
                             visit_fn: check_loans_in_fn
@@ -556,6 +553,37 @@ impl methods for assignment_type {
 impl methods for check_loan_ctxt {
     fn tcx() -> ty::ctxt { self.bccx.tcx }
 
+    fn purity(scope_id: ast::node_id) -> option<purity_cause> {
+        let default_purity = alt self.declared_purity {
+          // an unsafe declaration overrides all
+          ast::unsafe_fn { ret none; }
+
+          // otherwise, remember what was declared as the
+          // default, but we must scan for requirements
+          // imposed by the borrow check
+          ast::pure_fn { some(pc_declaration) }
+          ast::crust_fn | ast::impure_fn { none }
+        };
+
+        // scan to see if this scope or any enclosing scope requires
+        // purity.  if so, that overrides the declaration.
+
+        let mut scope_id = scope_id;
+        let region_map = self.tcx().region_map;
+        let pure_map = self.req_maps.pure_map;
+        loop {
+            alt pure_map.find(scope_id) {
+              none {}
+              some(e) {ret some(pc_cmt(e));}
+            }
+
+            alt region_map.find(scope_id) {
+              none { ret default_purity; }
+              some(next_scope_id) { scope_id = next_scope_id; }
+            }
+        }
+    }
+
     fn walk_loans(scope_id: ast::node_id,
                   f: fn(loan) -> bool) {
         let mut scope_id = scope_id;
@@ -590,7 +618,7 @@ impl methods for check_loan_ctxt {
 
     // when we are in a pure context, we check each call to ensure
     // that the function which is invoked is itself pure.
-    fn check_pure(expr: @ast::expr) {
+    fn check_pure(pc: purity_cause, expr: @ast::expr) {
         let tcx = self.tcx();
         alt ty::get(tcx.ty(expr)).struct {
           ty::ty_fn(_) {
@@ -645,7 +673,7 @@ impl methods for check_loan_ctxt {
               }
               ast::impure_fn | ast::unsafe_fn {
                 self.report_purity_error(
-                    expr.span,
+                    pc, expr.span,
                     "access to non-pure functions");
               }
             }
@@ -654,21 +682,6 @@ impl methods for check_loan_ctxt {
         }
     }
 
-    fn check_for_purity_requirement(scope_id: ast::node_id) {
-        // if we are not already enforcing purity, check whether the
-        // gather pass thought we needed to enforce purity for this
-        // scope.
-        alt self.is_pure {
-          pc_declaration | pc_cmt(*) { }
-          pc_impure {
-            alt self.req_maps.pure_map.find(scope_id) {
-              none {}
-              some(e) {self.is_pure = pc_cmt(e)}
-            }
-          }
-        }
-    }
-
     fn check_for_conflicting_loans(scope_id: ast::node_id) {
         let new_loanss = alt self.req_maps.req_loan_map.find(scope_id) {
             none { ret; }
@@ -739,10 +752,14 @@ impl methods for check_loan_ctxt {
         // if this is a pure function, only loan-able state can be
         // assigned, because it is uniquely tied to this function and
         // is not visible from the outside
-        if self.is_pure != pc_impure && cmt.lp.is_none() {
-            self.report_purity_error(
-                ex.span,
-                at.ing_form(self.bccx.cmt_to_str(cmt)));
+        alt self.purity(ex.id) {
+          none {}
+          some(pc) {
+            if cmt.lp.is_none() {
+                self.report_purity_error(
+                    pc, ex.span, at.ing_form(self.bccx.cmt_to_str(cmt)));
+            }
+          }
         }
 
         // check for a conflicting loan as well, except in the case of
@@ -775,11 +792,8 @@ impl methods for check_loan_ctxt {
         self.bccx.add_to_mutbl_map(cmt);
     }
 
-    fn report_purity_error(sp: span, msg: str) {
-        alt copy self.is_pure {
-          pc_impure {
-            self.tcx().sess.bug("report_purity_error() called when impure");
-          }
+    fn report_purity_error(pc: purity_cause, sp: span, msg: str) {
+        alt pc {
           pc_declaration {
             self.tcx().sess.span_err(
                 sp,
@@ -855,7 +869,7 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
                      visitor: visit::vt<check_loan_ctxt>) {
 
     save_and_restore(self.in_ctor) {||
-        save_and_restore(self.is_pure) {||
+        save_and_restore(self.declared_purity) {||
             // In principle, we could consider fk_anon(*) or
             // fk_fn_block(*) to be in a ctor, I suppose, but the
             // purpose of the in_ctor flag is to allow modifications
@@ -870,10 +884,7 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
             // NDM this doesn't seem algother right, what about fn  items
             // nested in pure fns? etc?
 
-            alt decl.purity {
-              ast::pure_fn { self.is_pure = pc_declaration; }
-              _ { }
-            }
+            self.declared_purity = decl.purity;
 
             visit::visit_fn(fk, decl, body, sp, id, self, visitor);
         }
@@ -884,16 +895,7 @@ fn check_loans_in_expr(expr: @ast::expr,
                        &&self: check_loan_ctxt,
                        vt: visit::vt<check_loan_ctxt>) {
     self.check_for_conflicting_loans(expr.id);
-    save_and_restore(self.is_pure) {||
-        self.check_for_purity_requirement(expr.id);
-        check_loans_in_expr_1(expr, self, vt);
-    }
-}
 
-// avoid rightward drift by breaking this out into its own fn
-fn check_loans_in_expr_1(expr: @ast::expr,
-                         &&self: check_loan_ctxt,
-                         vt: visit::vt<check_loan_ctxt>) {
     alt expr.node {
       ast::expr_swap(l, r) {
         self.check_assignment(at_swap, l);
@@ -936,9 +938,12 @@ fn check_loans_in_expr_1(expr: @ast::expr,
         }
       }
       ast::expr_call(f, args, _) {
-        if self.is_pure != pc_impure {
-            self.check_pure(f);
-            for args.each { |arg| self.check_pure(arg) }
+        alt self.purity(expr.id) {
+          none {}
+          some(pc) {
+            self.check_pure(pc, f);
+            for args.each { |arg| self.check_pure(pc, arg) }
+          }
         }
         let arg_tys = ty::ty_fn_args(ty::expr_ty(self.tcx(), f));
         vec::iter2(args, arg_tys) { |arg, arg_ty|
@@ -963,28 +968,17 @@ fn check_loans_in_expr_1(expr: @ast::expr,
 fn check_loans_in_block(blk: ast::blk,
                         &&self: check_loan_ctxt,
                         vt: visit::vt<check_loan_ctxt>) {
-    save_and_restore(self.is_pure) {||
+    save_and_restore(self.declared_purity) {||
         self.check_for_conflicting_loans(blk.node.id);
-        self.check_for_purity_requirement(blk.node.id);
 
         alt blk.node.rules {
           ast::default_blk {
           }
           ast::unchecked_blk {
-            alt self.is_pure {
-              pc_impure | pc_declaration {
-                self.is_pure = pc_impure;
-              }
-              pc_cmt(_) {
-                // unchecked does not override purity requirements due
-                // to borrows; unchecked didn't seem strong enough to
-                // justify potential memory unsafety to me
-              }
-            }
+            self.declared_purity = ast::impure_fn;
           }
           ast::unsafe_blk {
-            // unsafe blocks override everything
-            self.is_pure = pc_impure;
+            self.declared_purity = ast::unsafe_fn;
           }
         }
 
@@ -1472,6 +1466,15 @@ impl categorize_methods for borrowck_ctxt {
              ty_to_str(self.tcx, cmt.ty)]
     }
 
+    fn pk_to_sigil(pk: ptr_kind) -> str {
+        alt pk {
+          uniq_ptr {"~"}
+          gc_ptr {"@"}
+          region_ptr {"&"}
+          unsafe_ptr {"*"}
+        }
+    }
+
     fn cmt_to_str(cmt: cmt) -> str {
         let mut_str = self.mut_to_str(cmt.mutbl);
         alt cmt.cat {
@@ -1482,7 +1485,8 @@ impl categorize_methods for borrowck_ctxt {
           cat_rvalue { "non-lvalue" }
           cat_local(_) { mut_str + " local variable" }
           cat_arg(_) { mut_str + " argument" }
-          cat_deref(*) { "dereference of " + mut_str + " pointer" }
+          cat_deref(_, _, pk) { #fmt["dereference of %s %s pointer",
+                                     mut_str, self.pk_to_sigil(pk)] }
           cat_stack_upvar(_) { mut_str + " upvar" }
           cat_comp(_, comp_field(_)) { mut_str + " field" }
           cat_comp(_, comp_tuple) { "tuple content" }