Skip to content

Commit e55c5ce

Browse files
committed
Infer purity for || style closures. Closes #3023.
1 parent b5dd01e commit e55c5ce

File tree

5 files changed

+72
-51
lines changed

5 files changed

+72
-51
lines changed

src/rustc/middle/borrowck/check_loans.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,11 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
521521
do save_and_restore(self.declared_purity) {
522522
do save_and_restore(self.fn_args) {
523523
let is_stack_closure = self.is_stack_closure(id);
524-
let purity =
525-
ty::ty_fn_purity(ty::node_id_to_type(self.tcx(), id));
524+
let fty = ty::node_id_to_type(self.tcx(), id);
525+
self.declared_purity = ty::determine_inherited_purity(
526+
copy self.declared_purity,
527+
ty::ty_fn_purity(fty),
528+
ty::ty_fn_proto(fty));
526529

527530
// In principle, we could consider fk_anon(*) or
528531
// 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,
533536
match fk {
534537
visit::fk_ctor(*) => {
535538
self.in_ctor = true;
536-
self.declared_purity = purity;
537539
self.fn_args = @decl.inputs.map(|i| i.id );
538540
}
539541
visit::fk_anon(*) |
540542
visit::fk_fn_block(*) if is_stack_closure => {
541543
self.in_ctor = false;
542-
// inherits the purity/fn_args from enclosing ctxt
544+
// inherits the fn_args from enclosing ctxt
543545
}
544546
visit::fk_anon(*) | visit::fk_fn_block(*) |
545547
visit::fk_method(*) | visit::fk_item_fn(*) |
546548
visit::fk_dtor(*) => {
547549
self.in_ctor = false;
548-
self.declared_purity = purity;
549550
self.fn_args = @decl.inputs.map(|i| i.id );
550551
}
551552
}

src/rustc/middle/ty.rs

+14
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export region_variance, rv_covariant, rv_invariant, rv_contravariant;
187187
export serialize_region_variance, deserialize_region_variance;
188188
export opt_region_variance;
189189
export serialize_opt_region_variance, deserialize_opt_region_variance;
190+
export determine_inherited_purity;
190191

191192
// Data types
192193

@@ -3406,6 +3407,19 @@ pure fn is_blockish(proto: fn_proto) -> bool {
34063407
}
34073408
}
34083409

3410+
// Determine what purity to check a nested function under
3411+
pure fn determine_inherited_purity(parent_purity: ast::purity,
3412+
child_purity: ast::purity,
3413+
child_proto: ty::fn_proto) -> ast::purity {
3414+
// If the closure is a stack closure and hasn't had some non-standard
3415+
// purity inferred for it, then check it under its parent's purity.
3416+
// Otherwise, use its own
3417+
if ty::is_blockish(child_proto) && child_purity == ast::impure_fn {
3418+
parent_purity
3419+
} else { child_purity }
3420+
}
3421+
3422+
34093423
// Local Variables:
34103424
// mode: rust
34113425
// fill-column: 78;

src/rustc/middle/typeck/check.rs

+41-46
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,10 @@ fn check_fn(ccx: @crate_ctxt,
228228
node_type_substs: map::int_hash()}
229229
}
230230
some(fcx) => {
231-
assert fn_ty.purity == ast::impure_fn;
232231
{infcx: fcx.infcx,
233232
locals: fcx.locals,
234-
purity: fcx.purity,
233+
purity: ty::determine_inherited_purity(fcx.purity, fn_ty.purity,
234+
fn_ty.proto),
235235
node_types: fcx.node_types,
236236
node_type_substs: fcx.node_type_substs}
237237
}
@@ -1187,14 +1187,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
11871187
}
11881188
}
11891189

1190-
enum fn_or_ast_proto {
1191-
foap_fn_proto(ty::fn_proto),
1192-
foap_ast_proto(ast::proto)
1193-
}
1194-
11951190
fn check_expr_fn(fcx: @fn_ctxt,
11961191
expr: @ast::expr,
1197-
fn_or_ast_proto: fn_or_ast_proto,
1192+
ast_proto_opt: option<ast::proto>,
11981193
decl: ast::fn_decl,
11991194
body: ast::blk,
12001195
is_loop_body: bool,
@@ -1205,44 +1200,48 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
12051200
// avoid capture of bound regions in the expected type. See
12061201
// def'n of br_cap_avoid() for a more lengthy explanation of
12071202
// what's going on here.
1208-
let expected_tys = do unpack_expected(fcx, expected) |sty| {
1209-
match sty {
1210-
ty::ty_fn(ref fn_ty) => {
1203+
// Also try to pick up inferred purity and proto, defaulting
1204+
// to impure and block. Note that we only will use those for
1205+
// block syntax lambdas; that is, lambdas without explicit
1206+
// protos.
1207+
let expected_sty = unpack_expected(fcx, expected, |x| some(x));
1208+
let (expected_tys, expected_purity, expected_proto) =
1209+
match expected_sty {
1210+
some(ty::ty_fn(ref fn_ty)) => {
12111211
let {fn_ty, _} =
12121212
replace_bound_regions_in_fn_ty(
12131213
tcx, @nil, none, fn_ty,
12141214
|br| ty::re_bound(ty::br_cap_avoid(expr.id, @br)));
1215-
some({inputs:fn_ty.inputs,
1216-
output:fn_ty.output})
1215+
(some({inputs:fn_ty.inputs,
1216+
output:fn_ty.output}),
1217+
fn_ty.purity,
1218+
fn_ty.proto)
12171219
}
1218-
_ => {none}
1219-
}
1220-
};
1220+
_ => {
1221+
(none, ast::impure_fn, ty::proto_vstore(ty::vstore_box))
1222+
}
1223+
};
12211224

1222-
let ast_proto;
1223-
match fn_or_ast_proto {
1224-
foap_fn_proto(fn_proto) => {
1225-
// Generate a fake AST prototype. We'll fill in the type with
1226-
// the real one later.
1227-
// XXX: This is a hack.
1228-
ast_proto = ast::proto_box;
1229-
}
1230-
foap_ast_proto(existing_ast_proto) => {
1231-
ast_proto = existing_ast_proto;
1232-
}
1233-
}
12341225

1235-
let purity = ast::impure_fn;
1226+
// Generate AST prototypes and purity.
1227+
// If this is a block lambda (ast_proto == none), these values
1228+
// are bogus. We'll fill in the type with the real one later.
1229+
// XXX: This is a hack.
1230+
let ast_proto = ast_proto_opt.get_default(ast::proto_box);
1231+
let ast_purity = ast::impure_fn;
12361232

12371233
// construct the function type
1238-
let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx, ast_proto, purity,
1239-
@~[],
1234+
let mut fn_ty = astconv::ty_of_fn_decl(fcx, fcx,
1235+
ast_proto, ast_purity, @~[],
12401236
decl, expected_tys, expr.span);
12411237

12421238
// Patch up the function declaration, if necessary.
1243-
match fn_or_ast_proto {
1244-
foap_fn_proto(fn_proto) => fn_ty.proto = fn_proto,
1245-
foap_ast_proto(_) => {}
1239+
match ast_proto_opt {
1240+
none => {
1241+
fn_ty.purity = expected_purity;
1242+
fn_ty.proto = expected_proto;
1243+
}
1244+
some(_) => { }
12461245
}
12471246

12481247
let fty = ty::mk_fn(tcx, fn_ty);
@@ -1602,17 +1601,13 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16021601
bot = alt::check_alt(fcx, expr, discrim, arms);
16031602
}
16041603
ast::expr_fn(proto, decl, body, cap_clause) => {
1605-
check_expr_fn(fcx, expr, foap_ast_proto(proto),
1604+
check_expr_fn(fcx, expr, some(proto),
16061605
decl, body, false,
16071606
expected);
16081607
capture::check_capture_clause(tcx, expr.id, cap_clause);
16091608
}
16101609
ast::expr_fn_block(decl, body, cap_clause) => {
1611-
// Take the prototype from the expected type, but default to block:
1612-
let proto = do unpack_expected(fcx, expected) |sty| {
1613-
match sty { ty::ty_fn({proto, _}) => some(proto), _ => none }
1614-
}.get_default(ty::proto_vstore(ty::vstore_box));
1615-
check_expr_fn(fcx, expr, foap_fn_proto(proto),
1610+
check_expr_fn(fcx, expr, none,
16161611
decl, body, false,
16171612
expected);
16181613
capture::check_capture_clause(tcx, expr.id, cap_clause);
@@ -1625,7 +1620,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16251620
// 1. a closure that returns a bool is expected
16261621
// 2. the cloure that was given returns unit
16271622
let expected_sty = unpack_expected(fcx, expected, |x| some(x));
1628-
let (inner_ty, proto) = match expected_sty {
1623+
let inner_ty = match expected_sty {
16291624
some(ty::ty_fn(fty)) => {
16301625
match fcx.mk_subty(false, expr.span,
16311626
fty.output, ty::mk_bool(tcx)) {
@@ -1637,7 +1632,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16371632
fcx.infcx.ty_to_str(fty.output)));
16381633
}
16391634
}
1640-
(ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty}), fty.proto)
1635+
ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty})
16411636
}
16421637
_ => {
16431638
tcx.sess.span_fatal(expr.span, ~"a `loop` function's last \
@@ -1647,7 +1642,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16471642
};
16481643
match check b.node {
16491644
ast::expr_fn_block(decl, body, cap_clause) => {
1650-
check_expr_fn(fcx, b, foap_fn_proto(proto),
1645+
check_expr_fn(fcx, b, none,
16511646
decl, body, true,
16521647
some(inner_ty));
16531648
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
@@ -1665,9 +1660,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16651660
}
16661661
ast::expr_do_body(b) => {
16671662
let expected_sty = unpack_expected(fcx, expected, |x| some(x));
1668-
let (inner_ty, proto) = match expected_sty {
1663+
let inner_ty = match expected_sty {
16691664
some(ty::ty_fn(fty)) => {
1670-
(ty::mk_fn(tcx, fty), fty.proto)
1665+
ty::mk_fn(tcx, fty)
16711666
}
16721667
_ => {
16731668
tcx.sess.span_fatal(expr.span, ~"Non-function passed to a `do` \
@@ -1677,7 +1672,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
16771672
};
16781673
match check b.node {
16791674
ast::expr_fn_block(decl, body, cap_clause) => {
1680-
check_expr_fn(fcx, b, foap_fn_proto(proto),
1675+
check_expr_fn(fcx, b, none,
16811676
decl, body, true,
16821677
some(inner_ty));
16831678
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn something(f: pure fn()) { f(); }
2+
3+
fn main() {
4+
let mut x = ~[];
5+
something(|| vec::push(x, 0) ); //~ ERROR access to impure function prohibited in pure context
6+
}

src/test/run-pass/purity-infer.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
fn something(f: pure fn()) { f(); }
3+
fn main() {
4+
something(|| log(error, "hi!") );
5+
}

0 commit comments

Comments
 (0)