Skip to content

Commit c80e3ba

Browse files
committed
auto merge of #7244 : bblum/rust/once, r=nikomatsakis
@graydon suggested that once closures not be part of the language for 1.0, but that they might be hidden behind a -Z compile flag as an "experimental feature" in case people decide they need them. Regardless of whether ```-Z once-fns``` is set, this PR will parse the ```once``` keyword and will prevent closures labelled with it from being called more than once. It will also permit moving out of captured vars in heap closures, just to let the runtime writers stop using ```Cell``` sooner. Setting ```-Z once-fns``` only toggles whether the move-out-from-capture privilege is also given for stack closures. r? @nikomatsakis
2 parents a229c98 + e0788c7 commit c80e3ba

11 files changed

+304
-52
lines changed

Diff for: src/librustc/driver/session.rs

+30-25
Original file line numberDiff line numberDiff line change
@@ -45,32 +45,33 @@ pub struct config {
4545
float_type: float_ty
4646
}
4747

48-
pub static verbose: uint = 1 << 0;
49-
pub static time_passes: uint = 1 << 1;
50-
pub static count_llvm_insns: uint = 1 << 2;
51-
pub static time_llvm_passes: uint = 1 << 3;
52-
pub static trans_stats: uint = 1 << 4;
53-
pub static asm_comments: uint = 1 << 5;
54-
pub static no_verify: uint = 1 << 6;
55-
pub static trace: uint = 1 << 7;
56-
pub static coherence: uint = 1 << 8;
57-
pub static borrowck_stats: uint = 1 << 9;
58-
pub static borrowck_note_pure: uint = 1 << 10;
59-
pub static borrowck_note_loan: uint = 1 << 11;
60-
pub static no_landing_pads: uint = 1 << 12;
61-
pub static debug_llvm: uint = 1 << 13;
62-
pub static count_type_sizes: uint = 1 << 14;
63-
pub static meta_stats: uint = 1 << 15;
64-
pub static no_opt: uint = 1 << 16;
48+
pub static verbose: uint = 1 << 0;
49+
pub static time_passes: uint = 1 << 1;
50+
pub static count_llvm_insns: uint = 1 << 2;
51+
pub static time_llvm_passes: uint = 1 << 3;
52+
pub static trans_stats: uint = 1 << 4;
53+
pub static asm_comments: uint = 1 << 5;
54+
pub static no_verify: uint = 1 << 6;
55+
pub static trace: uint = 1 << 7;
56+
pub static coherence: uint = 1 << 8;
57+
pub static borrowck_stats: uint = 1 << 9;
58+
pub static borrowck_note_pure: uint = 1 << 10;
59+
pub static borrowck_note_loan: uint = 1 << 11;
60+
pub static no_landing_pads: uint = 1 << 12;
61+
pub static debug_llvm: uint = 1 << 13;
62+
pub static count_type_sizes: uint = 1 << 14;
63+
pub static meta_stats: uint = 1 << 15;
64+
pub static no_opt: uint = 1 << 16;
6565
pub static no_monomorphic_collapse: uint = 1 << 17;
66-
pub static gc: uint = 1 << 18;
67-
pub static jit: uint = 1 << 19;
68-
pub static debug_info: uint = 1 << 20;
69-
pub static extra_debug_info: uint = 1 << 21;
70-
pub static statik: uint = 1 << 22;
71-
pub static print_link_args: uint = 1 << 23;
72-
pub static no_debug_borrows: uint = 1 << 24;
73-
pub static lint_llvm : uint = 1 << 25;
66+
pub static gc: uint = 1 << 18;
67+
pub static jit: uint = 1 << 19;
68+
pub static debug_info: uint = 1 << 20;
69+
pub static extra_debug_info: uint = 1 << 21;
70+
pub static statik: uint = 1 << 22;
71+
pub static print_link_args: uint = 1 << 23;
72+
pub static no_debug_borrows: uint = 1 << 24;
73+
pub static lint_llvm: uint = 1 << 25;
74+
pub static once_fns: uint = 1 << 26;
7475

7576
pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
7677
~[(~"verbose", ~"in general, enable more debug printouts", verbose),
@@ -112,6 +113,9 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
112113
(~"lint-llvm",
113114
~"Run the LLVM lint pass on the pre-optimization IR",
114115
lint_llvm),
116+
(~"once-fns",
117+
~"Allow 'once fn' closures to deinitialize captured variables",
118+
once_fns),
115119
]
116120
}
117121

@@ -293,6 +297,7 @@ impl Session_ {
293297
pub fn debug_borrows(@self) -> bool {
294298
self.opts.optimize == No && !self.debugging_opt(no_debug_borrows)
295299
}
300+
pub fn once_fns(@self) -> bool { self.debugging_opt(once_fns) }
296301

297302
// pointless function, now...
298303
pub fn str_of(@self, id: ast::ident) -> @str {

Diff for: src/librustc/middle/borrowck/gather_loans/gather_moves.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ fn check_is_legal_to_move_from(bccx: @BorrowckCtxt,
101101
cmt0: mc::cmt,
102102
cmt: mc::cmt) -> bool {
103103
match cmt.cat {
104-
mc::cat_stack_upvar(*) |
105104
mc::cat_implicit_self(*) |
106-
mc::cat_copied_upvar(*) |
107105
mc::cat_deref(_, _, mc::region_ptr(*)) |
108106
mc::cat_deref(_, _, mc::gc_ptr(*)) |
109107
mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {
@@ -114,6 +112,27 @@ fn check_is_legal_to_move_from(bccx: @BorrowckCtxt,
114112
false
115113
}
116114

115+
// These are separate from the above cases for a better error message.
116+
mc::cat_stack_upvar(*) |
117+
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, _ }) => {
118+
let once_hint = if bccx.tcx.sess.once_fns() {
119+
" (unless the destination closure type is `once fn')"
120+
} else {
121+
""
122+
};
123+
bccx.span_err(
124+
cmt0.span,
125+
fmt!("cannot move out of %s%s", bccx.cmt_to_str(cmt), once_hint));
126+
false
127+
}
128+
129+
// Can move out of captured upvars only if the destination closure
130+
// type is 'once'. 1-shot stack closures emit the copied_upvar form
131+
// (see mem_categorization.rs).
132+
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Once, _ }) => {
133+
true
134+
}
135+
117136
// It seems strange to allow a move out of a static item,
118137
// but what happens in practice is that you have a
119138
// reference to a constant with a type that should be

Diff for: src/librustc/middle/mem_categorization.rs

+35-24
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub enum categorization {
7878
}
7979

8080
#[deriving(Eq)]
81-
struct CopiedUpvar {
81+
pub struct CopiedUpvar {
8282
upvar_id: ast::node_id,
8383
onceness: ast::Onceness,
8484
}
@@ -507,30 +507,41 @@ impl mem_categorization_ctxt {
507507
let ty = ty::node_id_to_type(self.tcx, fn_node_id);
508508
match ty::get(ty).sty {
509509
ty::ty_closure(ref closure_ty) => {
510-
let sigil = closure_ty.sigil;
511-
match sigil {
512-
ast::BorrowedSigil => {
513-
let upvar_cmt =
514-
self.cat_def(id, span, expr_ty, *inner);
515-
@cmt_ {
516-
id:id,
517-
span:span,
518-
cat:cat_stack_upvar(upvar_cmt),
519-
mutbl:upvar_cmt.mutbl.inherit(),
520-
ty:upvar_cmt.ty
521-
}
510+
// Decide whether to use implicit reference or by copy/move
511+
// capture for the upvar. This, combined with the onceness,
512+
// determines whether the closure can move out of it.
513+
let var_is_refd = match (closure_ty.sigil, closure_ty.onceness) {
514+
// Many-shot stack closures can never move out.
515+
(ast::BorrowedSigil, ast::Many) => true,
516+
// 1-shot stack closures can move out with "-Z once-fns".
517+
(ast::BorrowedSigil, ast::Once)
518+
if self.tcx.sess.once_fns() => false,
519+
(ast::BorrowedSigil, ast::Once) => true,
520+
// Heap closures always capture by copy/move, and can
521+
// move out iff they are once.
522+
(ast::OwnedSigil, _) | (ast::ManagedSigil, _) => false,
523+
524+
};
525+
if var_is_refd {
526+
let upvar_cmt =
527+
self.cat_def(id, span, expr_ty, *inner);
528+
@cmt_ {
529+
id:id,
530+
span:span,
531+
cat:cat_stack_upvar(upvar_cmt),
532+
mutbl:upvar_cmt.mutbl.inherit(),
533+
ty:upvar_cmt.ty
522534
}
523-
ast::OwnedSigil | ast::ManagedSigil => {
524-
// FIXME #2152 allow mutation of moved upvars
525-
@cmt_ {
526-
id:id,
527-
span:span,
528-
cat:cat_copied_upvar(CopiedUpvar {
529-
upvar_id: upvar_id,
530-
onceness: closure_ty.onceness}),
531-
mutbl:McImmutable,
532-
ty:expr_ty
533-
}
535+
} else {
536+
// FIXME #2152 allow mutation of moved upvars
537+
@cmt_ {
538+
id:id,
539+
span:span,
540+
cat:cat_copied_upvar(CopiedUpvar {
541+
upvar_id: upvar_id,
542+
onceness: closure_ty.onceness}),
543+
mutbl:McImmutable,
544+
ty:expr_ty
534545
}
535546
}
536547
}

Diff for: src/librustc/middle/moves.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ struct VisitContext {
183183
move_maps: MoveMaps
184184
}
185185

186+
#[deriving(Eq)]
186187
enum UseMode {
187188
Move, // This value or something owned by it is moved.
188189
Read // Read no matter what the type.
@@ -335,7 +336,27 @@ impl VisitContext {
335336
}
336337

337338
expr_call(callee, ref args, _) => { // callee(args)
338-
self.use_expr(callee, Read, visitor);
339+
// Figure out whether the called function is consumed.
340+
let mode = match ty::get(ty::expr_ty(self.tcx, callee)).sty {
341+
ty::ty_closure(ref cty) => {
342+
match cty.onceness {
343+
Once => Move,
344+
Many => Read,
345+
}
346+
},
347+
ty::ty_bare_fn(*) => Read,
348+
ref x =>
349+
self.tcx.sess.span_bug(callee.span,
350+
fmt!("non-function type in moves for expr_call: %?", x)),
351+
};
352+
// Note we're not using consume_expr, which uses type_moves_by_default
353+
// to determine the mode, for this. The reason is that while stack
354+
// closures should be noncopyable, they shouldn't move by default;
355+
// calling a closure should only consume it if it's once.
356+
if mode == Move {
357+
self.move_maps.moves_map.insert(callee.id);
358+
}
359+
self.use_expr(callee, mode, visitor);
339360
self.use_fn_args(callee.id, *args, visitor);
340361
}
341362

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Testing guarantees provided by once functions.
12+
// This program would segfault if it were legal.
13+
14+
extern mod extra;
15+
use extra::arc;
16+
use std::util;
17+
18+
fn foo(blk: ~once fn()) {
19+
blk();
20+
blk(); //~ ERROR use of moved value
21+
}
22+
23+
fn main() {
24+
let x = arc::ARC(true);
25+
do foo {
26+
assert!(*x.get());
27+
util::ignore(x);
28+
}
29+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Testing guarantees provided by once functions.
12+
// This program would segfault if it were legal.
13+
14+
// compile-flags:-Z once-fns
15+
extern mod extra;
16+
use extra::arc;
17+
use std::util;
18+
19+
fn foo(blk: &once fn()) {
20+
blk();
21+
blk(); //~ ERROR use of moved value
22+
}
23+
24+
fn main() {
25+
let x = arc::ARC(true);
26+
do foo {
27+
assert!(*x.get());
28+
util::ignore(x);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Though it should be legal to copy a heap-allocated "once fn:Copy",
12+
// stack closures are not deep-copied, so (counterintuitively) it should be
13+
// illegal to copy them.
14+
15+
fn foo<'r>(blk: &'r once fn:Copy()) -> (&'r once fn:Copy(), &'r once fn:Copy()) {
16+
(copy blk, blk) //~ ERROR copying a value of non-copyable type
17+
}
18+
19+
fn main() {
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Testing guarantees provided by once functions.
12+
// This program would segfault if it were legal.
13+
14+
extern mod extra;
15+
use extra::arc;
16+
use std::util;
17+
18+
fn foo(blk: ~fn()) {
19+
blk();
20+
blk();
21+
}
22+
23+
fn main() {
24+
let x = arc::ARC(true);
25+
do foo {
26+
assert!(*x.get());
27+
util::ignore(x); //~ ERROR cannot move out of captured outer variable
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Testing guarantees provided by once functions.
12+
// This program would segfault if it were legal.
13+
14+
extern mod extra;
15+
use extra::arc;
16+
use std::util;
17+
18+
fn foo(blk: &fn()) {
19+
blk();
20+
blk();
21+
}
22+
23+
fn main() {
24+
let x = arc::ARC(true);
25+
do foo {
26+
assert!(*x.get());
27+
util::ignore(x); //~ ERROR cannot move out of captured outer variable
28+
}
29+
}

Diff for: src/test/run-pass/once-move-out-on-heap.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Testing guarantees provided by once functions.
12+
13+
// xfail-fast
14+
15+
extern mod extra;
16+
use extra::arc;
17+
use std::util;
18+
19+
fn foo(blk: ~once fn()) {
20+
blk();
21+
}
22+
23+
fn main() {
24+
let x = arc::ARC(true);
25+
do foo {
26+
assert!(*x.get());
27+
util::ignore(x);
28+
}
29+
}

0 commit comments

Comments
 (0)