diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index ec86b43ffa3..9459116307d 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -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 { diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index c7bad60e90e..e950610cce6 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -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 diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 52c01fa7647..07e65e8af56 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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 } } } diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs index 68307a49d3b..cd9d8738026 100644 --- a/src/librustc/middle/moves.rs +++ b/src/librustc/middle/moves.rs @@ -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); } diff --git a/src/test/compile-fail/once-cant-call-twice-on-heap.rs b/src/test/compile-fail/once-cant-call-twice-on-heap.rs new file mode 100644 index 00000000000..4436675d69a --- /dev/null +++ b/src/test/compile-fail/once-cant-call-twice-on-heap.rs @@ -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 or the MIT license +// , 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); + } +} diff --git a/src/test/compile-fail/once-cant-call-twice-on-stack.rs b/src/test/compile-fail/once-cant-call-twice-on-stack.rs new file mode 100644 index 00000000000..10877be549e --- /dev/null +++ b/src/test/compile-fail/once-cant-call-twice-on-stack.rs @@ -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 or the MIT license +// , 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); + } +} diff --git a/src/test/compile-fail/once-cant-copy-stack-once-fn-copy.rs b/src/test/compile-fail/once-cant-copy-stack-once-fn-copy.rs new file mode 100644 index 00000000000..6f524c0068b --- /dev/null +++ b/src/test/compile-fail/once-cant-copy-stack-once-fn-copy.rs @@ -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 or the MIT license +// , 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() { +} diff --git a/src/test/compile-fail/once-cant-move-out-of-non-once-on-heap.rs b/src/test/compile-fail/once-cant-move-out-of-non-once-on-heap.rs new file mode 100644 index 00000000000..61f158cec27 --- /dev/null +++ b/src/test/compile-fail/once-cant-move-out-of-non-once-on-heap.rs @@ -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 or the MIT license +// , 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 + } +} diff --git a/src/test/compile-fail/once-cant-move-out-of-non-once-on-stack.rs b/src/test/compile-fail/once-cant-move-out-of-non-once-on-stack.rs new file mode 100644 index 00000000000..42c8b9a9998 --- /dev/null +++ b/src/test/compile-fail/once-cant-move-out-of-non-once-on-stack.rs @@ -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 or the MIT license +// , 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 + } +} diff --git a/src/test/run-pass/once-move-out-on-heap.rs b/src/test/run-pass/once-move-out-on-heap.rs new file mode 100644 index 00000000000..38b23fd128d --- /dev/null +++ b/src/test/run-pass/once-move-out-on-heap.rs @@ -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 or the MIT license +// , 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); + } +} diff --git a/src/test/run-pass/once-move-out-on-stack.rs b/src/test/run-pass/once-move-out-on-stack.rs new file mode 100644 index 00000000000..e881f576673 --- /dev/null +++ b/src/test/run-pass/once-move-out-on-stack.rs @@ -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 or the MIT license +// , 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); + } +}