Skip to content

One-shot closures. #7244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 29, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 30 additions & 25 deletions src/librustc/driver/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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),
]
}

Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 21 additions & 2 deletions src/librustc/middle/borrowck/gather_loans/gather_moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(*)) => {
Expand All @@ -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
Expand Down
59 changes: 35 additions & 24 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub enum categorization {
}

#[deriving(Eq)]
struct CopiedUpvar {
pub struct CopiedUpvar {
upvar_id: ast::node_id,
onceness: ast::Onceness,
}
Expand Down Expand Up @@ -497,30 +497,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
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion src/librustc/middle/moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}

Expand Down
29 changes: 29 additions & 0 deletions src/test/compile-fail/once-cant-call-twice-on-heap.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 30 additions & 0 deletions src/test/compile-fail/once-cant-call-twice-on-stack.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 20 additions & 0 deletions src/test/compile-fail/once-cant-copy-stack-once-fn-copy.rs
Original file line number Diff line number Diff line change
@@ -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() {
}
29 changes: 29 additions & 0 deletions src/test/compile-fail/once-cant-move-out-of-non-once-on-heap.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions src/test/compile-fail/once-cant-move-out-of-non-once-on-stack.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions src/test/run-pass/once-move-out-on-heap.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading