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
This commit is contained in:
commit
c80e3bac3e
@ -45,32 +45,33 @@ pub struct config {
|
||||
float_type: float_ty
|
||||
}
|
||||
|
||||
pub static verbose: uint = 1 << 0;
|
||||
pub static time_passes: uint = 1 << 1;
|
||||
pub static count_llvm_insns: uint = 1 << 2;
|
||||
pub static time_llvm_passes: uint = 1 << 3;
|
||||
pub static trans_stats: uint = 1 << 4;
|
||||
pub static asm_comments: uint = 1 << 5;
|
||||
pub static no_verify: uint = 1 << 6;
|
||||
pub static trace: uint = 1 << 7;
|
||||
pub static coherence: uint = 1 << 8;
|
||||
pub static borrowck_stats: uint = 1 << 9;
|
||||
pub static borrowck_note_pure: uint = 1 << 10;
|
||||
pub static borrowck_note_loan: uint = 1 << 11;
|
||||
pub static no_landing_pads: uint = 1 << 12;
|
||||
pub static debug_llvm: uint = 1 << 13;
|
||||
pub static count_type_sizes: uint = 1 << 14;
|
||||
pub static meta_stats: uint = 1 << 15;
|
||||
pub static no_opt: uint = 1 << 16;
|
||||
pub static verbose: uint = 1 << 0;
|
||||
pub static time_passes: uint = 1 << 1;
|
||||
pub static count_llvm_insns: uint = 1 << 2;
|
||||
pub static time_llvm_passes: uint = 1 << 3;
|
||||
pub static trans_stats: uint = 1 << 4;
|
||||
pub static asm_comments: uint = 1 << 5;
|
||||
pub static no_verify: uint = 1 << 6;
|
||||
pub static trace: uint = 1 << 7;
|
||||
pub static coherence: uint = 1 << 8;
|
||||
pub static borrowck_stats: uint = 1 << 9;
|
||||
pub static borrowck_note_pure: uint = 1 << 10;
|
||||
pub static borrowck_note_loan: uint = 1 << 11;
|
||||
pub static no_landing_pads: uint = 1 << 12;
|
||||
pub static debug_llvm: uint = 1 << 13;
|
||||
pub static count_type_sizes: uint = 1 << 14;
|
||||
pub static meta_stats: uint = 1 << 15;
|
||||
pub static no_opt: uint = 1 << 16;
|
||||
pub static no_monomorphic_collapse: uint = 1 << 17;
|
||||
pub static gc: uint = 1 << 18;
|
||||
pub static jit: uint = 1 << 19;
|
||||
pub static debug_info: uint = 1 << 20;
|
||||
pub static extra_debug_info: uint = 1 << 21;
|
||||
pub static statik: uint = 1 << 22;
|
||||
pub static print_link_args: uint = 1 << 23;
|
||||
pub static no_debug_borrows: uint = 1 << 24;
|
||||
pub static lint_llvm : uint = 1 << 25;
|
||||
pub static gc: uint = 1 << 18;
|
||||
pub static jit: uint = 1 << 19;
|
||||
pub static debug_info: uint = 1 << 20;
|
||||
pub static extra_debug_info: uint = 1 << 21;
|
||||
pub static statik: uint = 1 << 22;
|
||||
pub static print_link_args: uint = 1 << 23;
|
||||
pub static no_debug_borrows: uint = 1 << 24;
|
||||
pub static lint_llvm: uint = 1 << 25;
|
||||
pub static once_fns: uint = 1 << 26;
|
||||
|
||||
pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
|
||||
~[(~"verbose", ~"in general, enable more debug printouts", verbose),
|
||||
@ -112,6 +113,9 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
|
||||
(~"lint-llvm",
|
||||
~"Run the LLVM lint pass on the pre-optimization IR",
|
||||
lint_llvm),
|
||||
(~"once-fns",
|
||||
~"Allow 'once fn' closures to deinitialize captured variables",
|
||||
once_fns),
|
||||
]
|
||||
}
|
||||
|
||||
@ -293,6 +297,7 @@ impl Session_ {
|
||||
pub fn debug_borrows(@self) -> bool {
|
||||
self.opts.optimize == No && !self.debugging_opt(no_debug_borrows)
|
||||
}
|
||||
pub fn once_fns(@self) -> bool { self.debugging_opt(once_fns) }
|
||||
|
||||
// pointless function, now...
|
||||
pub fn str_of(@self, id: ast::ident) -> @str {
|
||||
|
@ -101,9 +101,7 @@ fn check_is_legal_to_move_from(bccx: @BorrowckCtxt,
|
||||
cmt0: mc::cmt,
|
||||
cmt: mc::cmt) -> bool {
|
||||
match cmt.cat {
|
||||
mc::cat_stack_upvar(*) |
|
||||
mc::cat_implicit_self(*) |
|
||||
mc::cat_copied_upvar(*) |
|
||||
mc::cat_deref(_, _, mc::region_ptr(*)) |
|
||||
mc::cat_deref(_, _, mc::gc_ptr(*)) |
|
||||
mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {
|
||||
@ -114,6 +112,27 @@ fn check_is_legal_to_move_from(bccx: @BorrowckCtxt,
|
||||
false
|
||||
}
|
||||
|
||||
// These are separate from the above cases for a better error message.
|
||||
mc::cat_stack_upvar(*) |
|
||||
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, _ }) => {
|
||||
let once_hint = if bccx.tcx.sess.once_fns() {
|
||||
" (unless the destination closure type is `once fn')"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
bccx.span_err(
|
||||
cmt0.span,
|
||||
fmt!("cannot move out of %s%s", bccx.cmt_to_str(cmt), once_hint));
|
||||
false
|
||||
}
|
||||
|
||||
// Can move out of captured upvars only if the destination closure
|
||||
// type is 'once'. 1-shot stack closures emit the copied_upvar form
|
||||
// (see mem_categorization.rs).
|
||||
mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Once, _ }) => {
|
||||
true
|
||||
}
|
||||
|
||||
// It seems strange to allow a move out of a static item,
|
||||
// but what happens in practice is that you have a
|
||||
// reference to a constant with a type that should be
|
||||
|
@ -78,7 +78,7 @@ pub enum categorization {
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
struct CopiedUpvar {
|
||||
pub struct CopiedUpvar {
|
||||
upvar_id: ast::node_id,
|
||||
onceness: ast::Onceness,
|
||||
}
|
||||
@ -507,30 +507,41 @@ impl mem_categorization_ctxt {
|
||||
let ty = ty::node_id_to_type(self.tcx, fn_node_id);
|
||||
match ty::get(ty).sty {
|
||||
ty::ty_closure(ref closure_ty) => {
|
||||
let sigil = closure_ty.sigil;
|
||||
match sigil {
|
||||
ast::BorrowedSigil => {
|
||||
let upvar_cmt =
|
||||
self.cat_def(id, span, expr_ty, *inner);
|
||||
@cmt_ {
|
||||
id:id,
|
||||
span:span,
|
||||
cat:cat_stack_upvar(upvar_cmt),
|
||||
mutbl:upvar_cmt.mutbl.inherit(),
|
||||
ty:upvar_cmt.ty
|
||||
}
|
||||
// Decide whether to use implicit reference or by copy/move
|
||||
// capture for the upvar. This, combined with the onceness,
|
||||
// determines whether the closure can move out of it.
|
||||
let var_is_refd = match (closure_ty.sigil, closure_ty.onceness) {
|
||||
// Many-shot stack closures can never move out.
|
||||
(ast::BorrowedSigil, ast::Many) => true,
|
||||
// 1-shot stack closures can move out with "-Z once-fns".
|
||||
(ast::BorrowedSigil, ast::Once)
|
||||
if self.tcx.sess.once_fns() => false,
|
||||
(ast::BorrowedSigil, ast::Once) => true,
|
||||
// Heap closures always capture by copy/move, and can
|
||||
// move out iff they are once.
|
||||
(ast::OwnedSigil, _) | (ast::ManagedSigil, _) => false,
|
||||
|
||||
};
|
||||
if var_is_refd {
|
||||
let upvar_cmt =
|
||||
self.cat_def(id, span, expr_ty, *inner);
|
||||
@cmt_ {
|
||||
id:id,
|
||||
span:span,
|
||||
cat:cat_stack_upvar(upvar_cmt),
|
||||
mutbl:upvar_cmt.mutbl.inherit(),
|
||||
ty:upvar_cmt.ty
|
||||
}
|
||||
ast::OwnedSigil | ast::ManagedSigil => {
|
||||
// FIXME #2152 allow mutation of moved upvars
|
||||
@cmt_ {
|
||||
id:id,
|
||||
span:span,
|
||||
cat:cat_copied_upvar(CopiedUpvar {
|
||||
upvar_id: upvar_id,
|
||||
onceness: closure_ty.onceness}),
|
||||
mutbl:McImmutable,
|
||||
ty:expr_ty
|
||||
}
|
||||
} else {
|
||||
// FIXME #2152 allow mutation of moved upvars
|
||||
@cmt_ {
|
||||
id:id,
|
||||
span:span,
|
||||
cat:cat_copied_upvar(CopiedUpvar {
|
||||
upvar_id: upvar_id,
|
||||
onceness: closure_ty.onceness}),
|
||||
mutbl:McImmutable,
|
||||
ty:expr_ty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,6 +183,7 @@ struct VisitContext {
|
||||
move_maps: MoveMaps
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
enum UseMode {
|
||||
Move, // This value or something owned by it is moved.
|
||||
Read // Read no matter what the type.
|
||||
@ -335,7 +336,27 @@ impl VisitContext {
|
||||
}
|
||||
|
||||
expr_call(callee, ref args, _) => { // callee(args)
|
||||
self.use_expr(callee, Read, visitor);
|
||||
// Figure out whether the called function is consumed.
|
||||
let mode = match ty::get(ty::expr_ty(self.tcx, callee)).sty {
|
||||
ty::ty_closure(ref cty) => {
|
||||
match cty.onceness {
|
||||
Once => Move,
|
||||
Many => Read,
|
||||
}
|
||||
},
|
||||
ty::ty_bare_fn(*) => Read,
|
||||
ref x =>
|
||||
self.tcx.sess.span_bug(callee.span,
|
||||
fmt!("non-function type in moves for expr_call: %?", x)),
|
||||
};
|
||||
// Note we're not using consume_expr, which uses type_moves_by_default
|
||||
// to determine the mode, for this. The reason is that while stack
|
||||
// closures should be noncopyable, they shouldn't move by default;
|
||||
// calling a closure should only consume it if it's once.
|
||||
if mode == Move {
|
||||
self.move_maps.moves_map.insert(callee.id);
|
||||
}
|
||||
self.use_expr(callee, mode, visitor);
|
||||
self.use_fn_args(callee.id, *args, visitor);
|
||||
}
|
||||
|
||||
|
29
src/test/compile-fail/once-cant-call-twice-on-heap.rs
Normal file
29
src/test/compile-fail/once-cant-call-twice-on-heap.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Testing guarantees provided by once functions.
|
||||
// This program would segfault if it were legal.
|
||||
|
||||
extern mod extra;
|
||||
use extra::arc;
|
||||
use std::util;
|
||||
|
||||
fn foo(blk: ~once fn()) {
|
||||
blk();
|
||||
blk(); //~ ERROR use of moved value
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = arc::ARC(true);
|
||||
do foo {
|
||||
assert!(*x.get());
|
||||
util::ignore(x);
|
||||
}
|
||||
}
|
30
src/test/compile-fail/once-cant-call-twice-on-stack.rs
Normal file
30
src/test/compile-fail/once-cant-call-twice-on-stack.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Testing guarantees provided by once functions.
|
||||
// This program would segfault if it were legal.
|
||||
|
||||
// compile-flags:-Z once-fns
|
||||
extern mod extra;
|
||||
use extra::arc;
|
||||
use std::util;
|
||||
|
||||
fn foo(blk: &once fn()) {
|
||||
blk();
|
||||
blk(); //~ ERROR use of moved value
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = arc::ARC(true);
|
||||
do foo {
|
||||
assert!(*x.get());
|
||||
util::ignore(x);
|
||||
}
|
||||
}
|
20
src/test/compile-fail/once-cant-copy-stack-once-fn-copy.rs
Normal file
20
src/test/compile-fail/once-cant-copy-stack-once-fn-copy.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Though it should be legal to copy a heap-allocated "once fn:Copy",
|
||||
// stack closures are not deep-copied, so (counterintuitively) it should be
|
||||
// illegal to copy them.
|
||||
|
||||
fn foo<'r>(blk: &'r once fn:Copy()) -> (&'r once fn:Copy(), &'r once fn:Copy()) {
|
||||
(copy blk, blk) //~ ERROR copying a value of non-copyable type
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Testing guarantees provided by once functions.
|
||||
// This program would segfault if it were legal.
|
||||
|
||||
extern mod extra;
|
||||
use extra::arc;
|
||||
use std::util;
|
||||
|
||||
fn foo(blk: ~fn()) {
|
||||
blk();
|
||||
blk();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = arc::ARC(true);
|
||||
do foo {
|
||||
assert!(*x.get());
|
||||
util::ignore(x); //~ ERROR cannot move out of captured outer variable
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Testing guarantees provided by once functions.
|
||||
// This program would segfault if it were legal.
|
||||
|
||||
extern mod extra;
|
||||
use extra::arc;
|
||||
use std::util;
|
||||
|
||||
fn foo(blk: &fn()) {
|
||||
blk();
|
||||
blk();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = arc::ARC(true);
|
||||
do foo {
|
||||
assert!(*x.get());
|
||||
util::ignore(x); //~ ERROR cannot move out of captured outer variable
|
||||
}
|
||||
}
|
29
src/test/run-pass/once-move-out-on-heap.rs
Normal file
29
src/test/run-pass/once-move-out-on-heap.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Testing guarantees provided by once functions.
|
||||
|
||||
// xfail-fast
|
||||
|
||||
extern mod extra;
|
||||
use extra::arc;
|
||||
use std::util;
|
||||
|
||||
fn foo(blk: ~once fn()) {
|
||||
blk();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = arc::ARC(true);
|
||||
do foo {
|
||||
assert!(*x.get());
|
||||
util::ignore(x);
|
||||
}
|
||||
}
|
30
src/test/run-pass/once-move-out-on-stack.rs
Normal file
30
src/test/run-pass/once-move-out-on-stack.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Testing guarantees provided by once functions.
|
||||
|
||||
// xfail-fast
|
||||
|
||||
// compile-flags:-Z once-fns
|
||||
extern mod extra;
|
||||
use extra::arc;
|
||||
use std::util;
|
||||
|
||||
fn foo(blk: &once fn()) {
|
||||
blk();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = arc::ARC(true);
|
||||
do foo {
|
||||
assert!(*x.get());
|
||||
util::ignore(x);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user