about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorMichael Sullivan <sully@msully.net>2012-08-24 14:01:08 -0700
committerMichael Sullivan <sully@msully.net>2012-08-24 14:21:27 -0700
commite55c5ceac239417784f9fe8c37f92e974b9cc06e (patch)
treef7f867e744360f4d3539467ebe6f429469280318 /src/rustc
parentb5dd01eb2a641d51f0b3135671a0e674d18f27f7 (diff)
Infer purity for || style closures. Closes #3023.
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/borrowck/check_loans.rs11
-rw-r--r--src/rustc/middle/ty.rs14
-rw-r--r--src/rustc/middle/typeck/check.rs87
3 files changed, 61 insertions, 51 deletions
diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs
index 7c18133ae02..6d9713dcd06 100644
--- a/src/rustc/middle/borrowck/check_loans.rs
+++ b/src/rustc/middle/borrowck/check_loans.rs
@@ -521,8 +521,11 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
         do save_and_restore(self.declared_purity) {
             do save_and_restore(self.fn_args) {
                 let is_stack_closure = self.is_stack_closure(id);
-                let purity =
-                    ty::ty_fn_purity(ty::node_id_to_type(self.tcx(), id));
+                let fty = ty::node_id_to_type(self.tcx(), id);
+                self.declared_purity = ty::determine_inherited_purity(
+                    copy self.declared_purity,
+                    ty::ty_fn_purity(fty),
+                    ty::ty_fn_proto(fty));
 
                 // In principle, we could consider fk_anon(*) or
                 // fk_fn_block(*) to be in a ctor, I suppose, but the
@@ -533,19 +536,17 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
                 match fk {
                   visit::fk_ctor(*) => {
                     self.in_ctor = true;
-                    self.declared_purity = purity;
                     self.fn_args = @decl.inputs.map(|i| i.id );
                   }
                   visit::fk_anon(*) |
                   visit::fk_fn_block(*) if is_stack_closure => {
                     self.in_ctor = false;
-                    // inherits the purity/fn_args from enclosing ctxt
+                    // inherits the fn_args from enclosing ctxt
                   }
                   visit::fk_anon(*) | visit::fk_fn_block(*) |
                   visit::fk_method(*) | visit::fk_item_fn(*) |
                   visit::fk_dtor(*) => {
                     self.in_ctor = false;
-                    self.declared_purity = purity;
                     self.fn_args = @decl.inputs.map(|i| i.id );
                   }
                 }
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index b5411dd597c..9bdf2074ba1 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -187,6 +187,7 @@ export region_variance, rv_covariant, rv_invariant, rv_contravariant;
 export serialize_region_variance, deserialize_region_variance;
 export opt_region_variance;
 export serialize_opt_region_variance, deserialize_opt_region_variance;
+export determine_inherited_purity;
 
 // Data types
 
@@ -3406,6 +3407,19 @@ pure fn is_blockish(proto: fn_proto) -> bool {
     }
 }
 
+// Determine what purity to check a nested function under
+pure fn determine_inherited_purity(parent_purity: ast::purity,
+                                   child_purity: ast::purity,
+                                   child_proto: ty::fn_proto) -> ast::purity {
+    // If the closure is a stack closure and hasn't had some non-standard
+    // purity inferred for it, then check it under its parent's purity.
+    // Otherwise, use its own
+    if ty::is_blockish(child_proto) && child_purity == ast::impure_fn {
+        parent_purity
+    } else { child_purity }
+}
+
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index aa51f2dd4a2..b8ee661b1b0 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -228,10 +228,10 @@ fn check_fn(ccx: @crate_ctxt,
              node_type_substs: map::int_hash()}
           }
           some(fcx) => {
-            assert fn_ty.purity == ast::impure_fn;
             {infcx: fcx.infcx,
              locals: fcx.locals,
-             purity: fcx.purity,
+             purity: ty::determine_inherited_purity(fcx.purity, fn_ty.purity,
+                                                    fn_ty.proto),
              node_types: fcx.node_types,
              node_type_substs: fcx.node_type_substs}
           }
@@ -1187,14 +1187,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         }
     }
 
-    enum fn_or_ast_proto {
-        foap_fn_proto(ty::fn_proto),
-        foap_ast_proto(ast::proto)
-    }
-
     fn check_expr_fn(fcx: @fn_ctxt,
                      expr: @ast::expr,
-                     fn_or_ast_proto: fn_or_ast_proto,
+                     ast_proto_opt: option<ast::proto>,
                      decl: ast::fn_decl,
                      body: ast::blk,
                      is_loop_body: bool,
@@ -1205,44 +1200,48 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // avoid capture of bound regions in the expected type.  See
         // def'n of br_cap_avoid() for a more lengthy explanation of
         // what's going on here.
-        let expected_tys = do unpack_expected(fcx, expected) |sty| {
-            match sty {
-              ty::ty_fn(ref fn_ty) => {
+        // Also try to pick up inferred purity and proto, defaulting
+        // to impure and block. Note that we only will use those for
+        // block syntax lambdas; that is, lambdas without explicit
+        // protos.
+        let expected_sty = unpack_expected(fcx, expected, |x| some(x));
+        let (expected_tys, expected_purity, expected_proto) =
+            match expected_sty {
+              some(ty::ty_fn(ref fn_ty)) => {
                 let {fn_ty, _} =
                     replace_bound_regions_in_fn_ty(
                         tcx, @nil, none, fn_ty,
                         |br| ty::re_bound(ty::br_cap_avoid(expr.id, @br)));
-                some({inputs:fn_ty.inputs,
-                      output:fn_ty.output})
+                (some({inputs:fn_ty.inputs,
+                       output:fn_ty.output}),
+                 fn_ty.purity,
+                 fn_ty.proto)
               }
-              _ => {none}
-            }
-        };
+              _ => {
+                (none, ast::impure_fn, ty::proto_vstore(ty::vstore_box))
+              }
+            };
 
-        let ast_proto;
-        match fn_or_ast_proto {
-            foap_fn_proto(fn_proto) => {
-                // Generate a fake AST prototype. We'll fill in the type with
-                // the real one later.
-                // XXX: This is a hack.
-                ast_proto = ast::proto_box;
-            }
-            foap_ast_proto(existing_ast_proto) => {
-                ast_proto = existing_ast_proto;
-            }
-        }
 
-        let purity = ast::impure_fn;
+        // Generate AST prototypes and purity.
+        // If this is a block lambda (ast_proto == none), these values
+        // are bogus. We'll fill in the type with the real one later.
+        // XXX: This is a hack.
+        let ast_proto = ast_proto_opt.get_default(ast::proto_box);
+        let ast_purity = ast::impure_fn;
 
         // construct the function type
-        let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx, ast_proto, purity,
-                                               @~[],
+        let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx,
+                                               ast_proto, ast_purity, @~[],
                                                decl, expected_tys, expr.span);
 
         // Patch up the function declaration, if necessary.
-        match fn_or_ast_proto {
-            foap_fn_proto(fn_proto) => fn_ty.proto = fn_proto,
-            foap_ast_proto(_) => {}
+        match ast_proto_opt {
+          none => {
+            fn_ty.purity = expected_purity;
+            fn_ty.proto = expected_proto;
+          }
+          some(_) => { }
         }
 
         let fty = ty::mk_fn(tcx, fn_ty);
@@ -1602,17 +1601,13 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         bot = alt::check_alt(fcx, expr, discrim, arms);
       }
       ast::expr_fn(proto, decl, body, cap_clause) => {
-        check_expr_fn(fcx, expr, foap_ast_proto(proto),
+        check_expr_fn(fcx, expr, some(proto),
                       decl, body, false,
                       expected);
         capture::check_capture_clause(tcx, expr.id, cap_clause);
       }
       ast::expr_fn_block(decl, body, cap_clause) => {
-        // Take the prototype from the expected type, but default to block:
-        let proto = do unpack_expected(fcx, expected) |sty| {
-            match sty { ty::ty_fn({proto, _}) => some(proto), _ => none }
-        }.get_default(ty::proto_vstore(ty::vstore_box));
-        check_expr_fn(fcx, expr, foap_fn_proto(proto),
+        check_expr_fn(fcx, expr, none,
                       decl, body, false,
                       expected);
         capture::check_capture_clause(tcx, expr.id, cap_clause);
@@ -1625,7 +1620,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // 1. a closure that returns a bool is expected
         // 2. the cloure that was given returns unit
         let expected_sty = unpack_expected(fcx, expected, |x| some(x));
-        let (inner_ty, proto) = match expected_sty {
+        let inner_ty = match expected_sty {
           some(ty::ty_fn(fty)) => {
             match fcx.mk_subty(false, expr.span,
                                fty.output, ty::mk_bool(tcx)) {
@@ -1637,7 +1632,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                                     fcx.infcx.ty_to_str(fty.output)));
               }
             }
-            (ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty}), fty.proto)
+            ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty})
           }
           _ => {
             tcx.sess.span_fatal(expr.span, ~"a `loop` function's last \
@@ -1647,7 +1642,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         };
         match check b.node {
           ast::expr_fn_block(decl, body, cap_clause) => {
-            check_expr_fn(fcx, b, foap_fn_proto(proto),
+            check_expr_fn(fcx, b, none,
                           decl, body, true,
                           some(inner_ty));
             demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
@@ -1665,9 +1660,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
       }
       ast::expr_do_body(b) => {
         let expected_sty = unpack_expected(fcx, expected, |x| some(x));
-        let (inner_ty, proto) = match expected_sty {
+        let inner_ty = match expected_sty {
           some(ty::ty_fn(fty)) => {
-            (ty::mk_fn(tcx, fty), fty.proto)
+            ty::mk_fn(tcx, fty)
           }
           _ => {
             tcx.sess.span_fatal(expr.span, ~"Non-function passed to a `do` \
@@ -1677,7 +1672,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         };
         match check b.node {
           ast::expr_fn_block(decl, body, cap_clause) => {
-            check_expr_fn(fcx, b, foap_fn_proto(proto),
+            check_expr_fn(fcx, b, none,
                           decl, body, true,
                           some(inner_ty));
             demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));