1
Fork 0

Finalize moves-based-on-type implementation.

Changes:

- Refactor move mode computation
- Removes move mode arguments, unary move, capture clauses
  (though they still parse for backwards compatibility)
- Simplify how moves are handled in trans
- Fix a number of illegal copies that cropped up
- Workaround for bug involving def-ids in params
  (see details below)

Future work (I'll open bugs for these...):

- Improve error messages for moves that are due
  to bindings
- Add support for moving owned content like a.b.c
  to borrow check, test in trans (but I think it'll
  "just work")
- Proper fix for def-ids in params

Def ids in params:

Move captures into a map instead of recomputing.

This is a workaround for a larger bug having to do with the def-ids associated
with ty_params, which are not always properly preserved when inlining.  I am
not sure of my preferred fix for the larger bug yet.  This current fix removes
the only code in trans that I know of which relies on ty_param def-ids, but
feels fragile.
This commit is contained in:
Niko Matsakis 2013-01-10 10:59:58 -08:00
parent 42b462e076
commit 0682ad0eb9
125 changed files with 2674 additions and 2633 deletions

View file

@ -1234,7 +1234,7 @@ For example:
~~~~ ~~~~
trait Num { trait Num {
static pure fn from_int(n: int) -> self; static pure fn from_int(n: int) -> Self;
} }
impl float: Num { impl float: Num {
static pure fn from_int(n: int) -> float { n as float } static pure fn from_int(n: int) -> float { n as float }

View file

@ -440,8 +440,8 @@ Finally, tasks can be configured to not propagate failure to each
other at all, using `task::spawn_unlinked` for _isolated failure_. other at all, using `task::spawn_unlinked` for _isolated failure_.
~~~ ~~~
# fn random() -> int { 100 } # fn random() -> uint { 100 }
# fn sleep_for(i: int) { for i.times { task::yield() } } # fn sleep_for(i: uint) { for i.times { task::yield() } }
# do task::try::<()> { # do task::try::<()> {
let (time1, time2) = (random(), random()); let (time1, time2) = (random(), random());
do task::spawn_unlinked { do task::spawn_unlinked {

View file

@ -51,7 +51,7 @@ try:
report_err("TODO is deprecated; use FIXME") report_err("TODO is deprecated; use FIXME")
idx = line.find("// NOTE") idx = line.find("// NOTE")
if idx != -1: if idx != -1:
report_warn("NOTE:" + line[idx + len("// NOTE"):]) report_warn("NOTE" + line[idx + len("// NOTE"):])
if (line.find('\t') != -1 and if (line.find('\t') != -1 and
fileinput.filename().find("Makefile") == -1): fileinput.filename().find("Makefile") == -1):
report_err("tab character") report_err("tab character")

View file

@ -1965,19 +1965,17 @@ pub fn main() {
c = configure(o); c = configure(o);
} }
let c = &move c;
match o.free[1] { match o.free[1] {
~"init" => cmd_init(c), ~"init" => cmd_init(&c),
~"install" => cmd_install(c), ~"install" => cmd_install(&c),
~"uninstall" => cmd_uninstall(c), ~"uninstall" => cmd_uninstall(&c),
~"list" => cmd_list(c), ~"list" => cmd_list(&c),
~"search" => cmd_search(c), ~"search" => cmd_search(&c),
~"sources" => cmd_sources(c), ~"sources" => cmd_sources(&c),
_ => cmd_usage() _ => cmd_usage()
} }
dump_cache(c); dump_cache(&c);
dump_sources(c); dump_sources(&c);
} }

View file

@ -177,8 +177,10 @@ fn get_global_state() -> Exclusive<GlobalState> {
let state = ~exclusive(state); let state = ~exclusive(state);
// Convert it to an integer // Convert it to an integer
let state_ptr: &Exclusive<GlobalState> = state; let state_i: int = unsafe {
let state_i: int = unsafe { transmute(state_ptr) }; let state_ptr: &Exclusive<GlobalState> = state;
transmute(state_ptr)
};
// Swap our structure into the global pointer // Swap our structure into the global pointer
let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) }; let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) };

View file

@ -836,12 +836,20 @@ fn test_run_basic() {
po.recv(); po.recv();
} }
#[test]
struct Wrapper {
mut f: Option<Chan<()>>
}
#[test] #[test]
fn test_add_wrapper() { fn test_add_wrapper() {
let (po, ch) = stream::<()>(); let (po, ch) = stream::<()>();
let b0 = task(); let b0 = task();
let ch = Wrapper { f: Some(ch) };
let b1 = do b0.add_wrapper |body| { let b1 = do b0.add_wrapper |body| {
let ch = Wrapper { f: Some(ch.f.swap_unwrap()) };
fn~(move body) { fn~(move body) {
let ch = ch.f.swap_unwrap();
body(); body();
ch.send(()); ch.send(());
} }
@ -929,9 +937,12 @@ fn test_spawn_sched_childs_on_default_sched() {
// Assuming tests run on the default scheduler // Assuming tests run on the default scheduler
let default_id = unsafe { rt::rust_get_sched_id() }; let default_id = unsafe { rt::rust_get_sched_id() };
let ch = Wrapper { f: Some(ch) };
do spawn_sched(SingleThreaded) { do spawn_sched(SingleThreaded) {
let parent_sched_id = unsafe { rt::rust_get_sched_id() }; let parent_sched_id = unsafe { rt::rust_get_sched_id() };
let ch = Wrapper { f: Some(ch.f.swap_unwrap()) };
do spawn { do spawn {
let ch = ch.f.swap_unwrap();
let child_sched_id = unsafe { rt::rust_get_sched_id() }; let child_sched_id = unsafe { rt::rust_get_sched_id() };
assert parent_sched_id != child_sched_id; assert parent_sched_id != child_sched_id;
assert child_sched_id == default_id; assert child_sched_id == default_id;

View file

@ -191,7 +191,7 @@ pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] {
let mut minimized = ~[]; let mut minimized = ~[];
for rpaths.each |rpath| { for rpaths.each |rpath| {
let s = rpath.to_str(); let s = rpath.to_str();
if !set.contains_key(s) { if !set.contains_key_ref(&s) {
minimized.push(/*bad*/copy *rpath); minimized.push(/*bad*/copy *rpath);
set.insert(s, ()); set.insert(s, ());
} }

View file

@ -16,7 +16,7 @@ use front;
use lib::llvm::llvm; use lib::llvm::llvm;
use metadata::{creader, cstore, filesearch}; use metadata::{creader, cstore, filesearch};
use metadata; use metadata;
use middle::{trans, freevars, kind, ty, typeck, lint}; use middle::{trans, freevars, kind, ty, typeck, lint, astencode};
use middle; use middle;
use session::{Session, Session_, OptLevel, No, Less, Default, Aggressive}; use session::{Session, Session_, OptLevel, No, Less, Default, Aggressive};
use session; use session;
@ -281,20 +281,26 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
time(time_passes, ~"loop checking", || time(time_passes, ~"loop checking", ||
middle::check_loop::check_crate(ty_cx, crate)); middle::check_loop::check_crate(ty_cx, crate));
time(time_passes, ~"mode computation", || let middle::moves::MoveMaps {moves_map, variable_moves_map,
middle::mode::compute_modes(ty_cx, method_map, crate)); capture_map} =
time(time_passes, ~"compute moves", ||
middle::moves::compute_moves(ty_cx, method_map, crate));
time(time_passes, ~"match checking", || time(time_passes, ~"match checking", ||
middle::check_match::check_crate(ty_cx, method_map, crate)); middle::check_match::check_crate(ty_cx, method_map,
moves_map, crate));
let last_use_map = let last_use_map =
time(time_passes, ~"liveness checking", || time(time_passes, ~"liveness checking", ||
middle::liveness::check_crate(ty_cx, method_map, crate)); middle::liveness::check_crate(ty_cx, method_map,
variable_moves_map,
capture_map, crate));
let (root_map, mutbl_map, write_guard_map) = let (root_map, mutbl_map, write_guard_map) =
time(time_passes, ~"borrow checking", || time(time_passes, ~"borrow checking", ||
middle::borrowck::check_crate(ty_cx, method_map, middle::borrowck::check_crate(ty_cx, method_map,
last_use_map, crate)); moves_map, capture_map,
crate));
time(time_passes, ~"kind checking", || time(time_passes, ~"kind checking", ||
kind::check_crate(ty_cx, method_map, last_use_map, crate)); kind::check_crate(ty_cx, method_map, last_use_map, crate));
@ -304,12 +310,16 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
if upto == cu_no_trans { return {crate: crate, tcx: Some(ty_cx)}; } if upto == cu_no_trans { return {crate: crate, tcx: Some(ty_cx)}; }
let maps = {mutbl_map: mutbl_map, let maps = astencode::Maps {
root_map: root_map, mutbl_map: mutbl_map,
last_use_map: last_use_map, root_map: root_map,
method_map: method_map, last_use_map: last_use_map,
vtable_map: vtable_map, method_map: method_map,
write_guard_map: write_guard_map}; vtable_map: vtable_map,
write_guard_map: write_guard_map,
moves_map: moves_map,
capture_map: capture_map
};
time(time_passes, ~"translation", || time(time_passes, ~"translation", ||
trans::base::trans_crate(sess, crate, ty_cx, trans::base::trans_crate(sess, crate, ty_cx,
@ -528,7 +538,7 @@ pub fn build_session_options(+binary: ~str,
getopts::opt_strs(matches, level_name)); getopts::opt_strs(matches, level_name));
for flags.each |lint_name| { for flags.each |lint_name| {
let lint_name = str::replace(*lint_name, ~"-", ~"_"); let lint_name = str::replace(*lint_name, ~"-", ~"_");
match lint_dict.find(lint_name) { match lint_dict.find(/*bad*/ copy lint_name) {
None => { None => {
early_error(demitter, fmt!("unknown %s flag: %s", early_error(demitter, fmt!("unknown %s flag: %s",
level_name, lint_name)); level_name, lint_name));

View file

@ -190,7 +190,7 @@ pub fn metas_in_cfg(cfg: ast::crate_cfg,
if !has_cfg_metas { return true; } if !has_cfg_metas { return true; }
for cfg_metas.each |cfg_mi| { for cfg_metas.each |cfg_mi| {
if attr::contains(/*bad*/copy cfg, *cfg_mi) { return true; } if attr::contains(cfg, *cfg_mi) { return true; }
} }
return false; return false;

View file

@ -46,7 +46,7 @@ pub fn modify_for_testing(sess: session::Session,
// We generate the test harness when building in the 'test' // We generate the test harness when building in the 'test'
// configuration, either with the '--test' or '--cfg test' // configuration, either with the '--test' or '--cfg test'
// command line options. // command line options.
let should_test = attr::contains(/*bad*/copy crate.node.config, let should_test = attr::contains(crate.node.config,
attr::mk_word_item(~"test")); attr::mk_word_item(~"test"));
if should_test { if should_test {
@ -510,7 +510,7 @@ fn mk_test_wrapper(cx: test_ctxt,
let wrapper_expr = ast::expr { let wrapper_expr = ast::expr {
id: cx.sess.next_node_id(), id: cx.sess.next_node_id(),
callee_id: cx.sess.next_node_id(), callee_id: cx.sess.next_node_id(),
node: ast::expr_fn(ast::ProtoBare, wrapper_decl, wrapper_body, @~[]), node: ast::expr_fn(ast::ProtoBare, wrapper_decl, wrapper_body),
span: span span: span
}; };

View file

@ -1344,10 +1344,10 @@ pub fn mk_type_names() -> type_names {
} }
pub fn type_to_str(names: type_names, ty: TypeRef) -> @str { pub fn type_to_str(names: type_names, ty: TypeRef) -> @str {
return type_to_str_inner(names, ~[], ty); return type_to_str_inner(names, [], ty);
} }
pub fn type_to_str_inner(names: type_names, +outer0: ~[TypeRef], ty: TypeRef) pub fn type_to_str_inner(names: type_names, +outer0: &[TypeRef], ty: TypeRef)
-> @str { -> @str {
unsafe { unsafe {
match type_has_name(names, ty) { match type_has_name(names, ty) {
@ -1355,12 +1355,11 @@ pub fn type_to_str_inner(names: type_names, +outer0: ~[TypeRef], ty: TypeRef)
_ => {} _ => {}
} }
// FIXME #2543: Bad copy. let outer = vec::append_one(outer0.to_vec(), ty);
let outer = vec::append_one(copy outer0, ty);
let kind = llvm::LLVMGetTypeKind(ty); let kind = llvm::LLVMGetTypeKind(ty);
fn tys_str(names: type_names, outer: ~[TypeRef], fn tys_str(names: type_names, outer: &[TypeRef],
tys: ~[TypeRef]) -> @str { tys: ~[TypeRef]) -> @str {
let mut s = ~""; let mut s = ~"";
let mut first: bool = true; let mut first: bool = true;

View file

@ -133,7 +133,8 @@ pub enum astencode_tag { // Reserves 0x50 -- 0x6f
tag_table_vtable_map = 0x61, tag_table_vtable_map = 0x61,
tag_table_adjustments = 0x62, tag_table_adjustments = 0x62,
tag_table_legacy_boxed_trait = 0x63, tag_table_legacy_boxed_trait = 0x63,
tag_table_value_mode = 0x64 tag_table_moves_map = 0x64,
tag_table_capture_map = 0x65
} }
pub const tag_item_trait_method_sort: uint = 0x70; pub const tag_item_trait_method_sort: uint = 0x70;

View file

@ -163,8 +163,8 @@ fn visit_item(e: env, i: @ast::item) {
None => /*bad*/copy *e.intr.get(i.ident) None => /*bad*/copy *e.intr.get(i.ident)
}; };
if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() { if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
already_added = !cstore::add_used_library(cstore, already_added =
foreign_name); !cstore::add_used_library(cstore, copy foreign_name);
} }
if !link_args.is_empty() && already_added { if !link_args.is_empty() && already_added {
e.diag.span_fatal(i.span, ~"library '" + foreign_name + e.diag.span_fatal(i.span, ~"library '" + foreign_name +
@ -281,7 +281,8 @@ fn resolve_crate_deps(e: env, cdata: @~[u8]) -> cstore::cnum_map {
let cmetas = metas_with(/*bad*/copy dep.vers, ~"vers", ~[]); let cmetas = metas_with(/*bad*/copy dep.vers, ~"vers", ~[]);
debug!("resolving dep crate %s ver: %s hash: %s", debug!("resolving dep crate %s ver: %s hash: %s",
*e.intr.get(dep.name), dep.vers, dep.hash); *e.intr.get(dep.name), dep.vers, dep.hash);
match existing_match(e, metas_with_ident(*e.intr.get(cname), cmetas), match existing_match(e, metas_with_ident(copy *e.intr.get(cname),
copy cmetas),
dep.hash) { dep.hash) {
Some(local_cnum) => { Some(local_cnum) => {
debug!("already have it"); debug!("already have it");

View file

@ -648,14 +648,22 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: writer::Encoder,
/* Encode the dtor */ /* Encode the dtor */
do struct_def.dtor.iter |dtor| { do struct_def.dtor.iter |dtor| {
index.push({val: dtor.node.id, pos: ebml_w.writer.tell()}); index.push({val: dtor.node.id, pos: ebml_w.writer.tell()});
encode_info_for_ctor(ecx, ebml_w, dtor.node.id, encode_info_for_ctor(ecx,
ebml_w,
dtor.node.id,
ecx.tcx.sess.ident_of( ecx.tcx.sess.ident_of(
ecx.tcx.sess.str_of(item.ident) + ecx.tcx.sess.str_of(item.ident) +
~"_dtor"), ~"_dtor"),
path, if tps.len() > 0u { path,
Some(ii_dtor(*dtor, item.ident, tps, if tps.len() > 0u {
Some(ii_dtor(copy *dtor,
item.ident,
copy tps,
local_def(item.id))) } local_def(item.id))) }
else { None }, tps); else {
None
},
tps);
} }
/* Index the class*/ /* Index the class*/
@ -869,27 +877,35 @@ fn encode_info_for_items(ecx: @encode_ctxt, ebml_w: writer::Encoder,
syntax::parse::token::special_idents::invalid); syntax::parse::token::special_idents::invalid);
visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor { visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor {
visit_expr: |_e, _cx, _v| { }, visit_expr: |_e, _cx, _v| { },
visit_item: |i, cx, v, copy ebml_w| { visit_item: {
visit::visit_item(i, cx, v); let ebml_w = copy ebml_w;
match ecx.tcx.items.get(i.id) { |i, cx, v| {
ast_map::node_item(_, pt) => { visit::visit_item(i, cx, v);
encode_info_for_item(ecx, ebml_w, i, index, *pt); match ecx.tcx.items.get(i.id) {
} ast_map::node_item(_, pt) => {
_ => fail ~"bad item" encode_info_for_item(ecx, ebml_w, i,
index, *pt);
}
_ => fail ~"bad item"
}
} }
}, },
visit_foreign_item: |ni, cx, v, copy ebml_w| { visit_foreign_item: {
visit::visit_foreign_item(ni, cx, v); let ebml_w = copy ebml_w;
match ecx.tcx.items.get(ni.id) { |ni, cx, v| {
ast_map::node_foreign_item(_, abi, pt) => { visit::visit_foreign_item(ni, cx, v);
encode_info_for_foreign_item(ecx, ebml_w, ni, match ecx.tcx.items.get(ni.id) {
index, /*bad*/copy *pt, abi); ast_map::node_foreign_item(_, abi, pt) => {
} encode_info_for_foreign_item(ecx, ebml_w, ni,
// case for separate item and foreign-item tables index, /*bad*/copy *pt,
_ => fail ~"bad foreign item" abi);
}
// case for separate item and foreign-item tables
_ => fail ~"bad foreign item"
}
} }
} },
,.. *visit::default_visitor() ..*visit::default_visitor()
})); }));
ebml_w.end_tag(); ebml_w.end_tag();
return /*bad*/copy *index; return /*bad*/copy *index;

View file

@ -165,7 +165,8 @@ pub fn note_linkage_attrs(intr: @ident_interner, diag: span_handler,
} }
} }
fn crate_matches(crate_data: @~[u8], +metas: ~[@ast::meta_item], fn crate_matches(crate_data: @~[u8],
metas: &[@ast::meta_item],
hash: ~str) -> bool { hash: ~str) -> bool {
let attrs = decoder::get_crate_attributes(crate_data); let attrs = decoder::get_crate_attributes(crate_data);
let linkage_metas = attr::find_linkage_metas(attrs); let linkage_metas = attr::find_linkage_metas(attrs);
@ -177,7 +178,7 @@ fn crate_matches(crate_data: @~[u8], +metas: ~[@ast::meta_item],
} }
pub fn metadata_matches(extern_metas: ~[@ast::meta_item], pub fn metadata_matches(extern_metas: ~[@ast::meta_item],
local_metas: ~[@ast::meta_item]) -> bool { local_metas: &[@ast::meta_item]) -> bool {
debug!("matching %u metadata requirements against %u items", debug!("matching %u metadata requirements against %u items",
vec::len(local_metas), vec::len(extern_metas)); vec::len(local_metas), vec::len(extern_metas));

View file

@ -275,6 +275,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
} }
'p' => { 'p' => {
let did = parse_def(st, TypeParameter, conv); let did = parse_def(st, TypeParameter, conv);
debug!("parsed ty_param: did=%?", did);
return ty::mk_param(st.tcx, parse_int(st) as uint, did); return ty::mk_param(st.tcx, parse_int(st) as uint, did);
} }
's' => { 's' => {
@ -422,7 +423,6 @@ fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
fn parse_mode(st: @pstate) -> ast::mode { fn parse_mode(st: @pstate) -> ast::mode {
let m = ast::expl(match next(st) { let m = ast::expl(match next(st) {
'-' => ast::by_move,
'+' => ast::by_copy, '+' => ast::by_copy,
'=' => ast::by_ref, '=' => ast::by_ref,
'#' => ast::by_val, '#' => ast::by_val,

View file

@ -342,7 +342,6 @@ pub fn enc_arg(w: io::Writer, cx: @ctxt, arg: ty::arg) {
pub fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) { pub fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
match ty::resolved_mode(cx.tcx, m) { match ty::resolved_mode(cx.tcx, m) {
by_move => w.write_char('-'),
by_copy => w.write_char('+'), by_copy => w.write_char('+'),
by_ref => w.write_char('='), by_ref => w.write_char('='),
by_val => w.write_char('#') by_val => w.write_char('#')

View file

@ -22,7 +22,7 @@ use metadata::tyencode;
use middle::freevars::freevar_entry; use middle::freevars::freevar_entry;
use middle::typeck::{method_origin, method_map_entry, vtable_res}; use middle::typeck::{method_origin, method_map_entry, vtable_res};
use middle::typeck::{vtable_origin}; use middle::typeck::{vtable_origin};
use middle::{ty, typeck}; use middle::{ty, typeck, moves};
use middle; use middle;
use util::ppaux::ty_to_str; use util::ppaux::ty_to_str;
@ -51,19 +51,21 @@ use syntax;
use writer = std::ebml::writer; use writer = std::ebml::writer;
// Auxiliary maps of things to be encoded // Auxiliary maps of things to be encoded
pub type maps = { pub struct Maps {
mutbl_map: middle::borrowck::mutbl_map, mutbl_map: middle::borrowck::mutbl_map,
root_map: middle::borrowck::root_map, root_map: middle::borrowck::root_map,
last_use_map: middle::liveness::last_use_map, last_use_map: middle::liveness::last_use_map,
method_map: middle::typeck::method_map, method_map: middle::typeck::method_map,
vtable_map: middle::typeck::vtable_map, vtable_map: middle::typeck::vtable_map,
write_guard_map: middle::borrowck::write_guard_map, write_guard_map: middle::borrowck::write_guard_map,
}; moves_map: middle::moves::MovesMap,
capture_map: middle::moves::CaptureMap,
}
type decode_ctxt = @{ type decode_ctxt = @{
cdata: cstore::crate_metadata, cdata: cstore::crate_metadata,
tcx: ty::ctxt, tcx: ty::ctxt,
maps: maps maps: Maps
}; };
type extended_decode_ctxt_ = { type extended_decode_ctxt_ = {
@ -91,7 +93,7 @@ pub fn encode_inlined_item(ecx: @e::encode_ctxt,
ebml_w: writer::Encoder, ebml_w: writer::Encoder,
path: &[ast_map::path_elt], path: &[ast_map::path_elt],
ii: ast::inlined_item, ii: ast::inlined_item,
maps: maps) { maps: Maps) {
debug!("> Encoding inlined item: %s::%s (%u)", debug!("> Encoding inlined item: %s::%s (%u)",
ast_map::path_to_str(path, ecx.tcx.sess.parse_sess.interner), ast_map::path_to_str(path, ecx.tcx.sess.parse_sess.interner),
ecx.tcx.sess.str_of(ii.ident()), ecx.tcx.sess.str_of(ii.ident()),
@ -112,7 +114,7 @@ pub fn encode_inlined_item(ecx: @e::encode_ctxt,
pub fn decode_inlined_item(cdata: cstore::crate_metadata, pub fn decode_inlined_item(cdata: cstore::crate_metadata,
tcx: ty::ctxt, tcx: ty::ctxt,
maps: maps, maps: Maps,
+path: ast_map::path, +path: ast_map::path,
par_doc: ebml::Doc) par_doc: ebml::Doc)
-> Option<ast::inlined_item> { -> Option<ast::inlined_item> {
@ -513,6 +515,30 @@ impl freevar_entry: tr {
} }
} }
// ______________________________________________________________________
// Encoding and decoding of CaptureVar information
trait capture_var_helper {
fn read_capture_var(xcx: extended_decode_ctxt) -> moves::CaptureVar;
}
impl reader::Decoder : capture_var_helper {
fn read_capture_var(xcx: extended_decode_ctxt) -> moves::CaptureVar {
let cvar: moves::CaptureVar = Decodable::decode(&self);
cvar.tr(xcx)
}
}
impl moves::CaptureVar : tr {
fn tr(xcx: extended_decode_ctxt) -> moves::CaptureVar {
moves::CaptureVar {
def: self.def.tr(xcx),
span: self.span.tr(xcx),
mode: self.mode
}
}
}
// ______________________________________________________________________ // ______________________________________________________________________
// Encoding and decoding of method_map_entry // Encoding and decoding of method_map_entry
@ -788,10 +814,11 @@ impl writer::Encoder: write_tag_and_id {
} }
fn encode_side_tables_for_ii(ecx: @e::encode_ctxt, fn encode_side_tables_for_ii(ecx: @e::encode_ctxt,
maps: maps, maps: Maps,
ebml_w: writer::Encoder, ebml_w: writer::Encoder,
ii: ast::inlined_item) { ii: ast::inlined_item) {
do ebml_w.wr_tag(c::tag_table as uint) { do ebml_w.wr_tag(c::tag_table as uint) {
let ebml_w = copy ebml_w;
ast_util::visit_ids_for_inlined_item( ast_util::visit_ids_for_inlined_item(
ii, ii,
fn@(id: ast::node_id, copy ebml_w) { fn@(id: ast::node_id, copy ebml_w) {
@ -804,7 +831,7 @@ fn encode_side_tables_for_ii(ecx: @e::encode_ctxt,
} }
fn encode_side_tables_for_id(ecx: @e::encode_ctxt, fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
maps: maps, maps: Maps,
ebml_w: writer::Encoder, ebml_w: writer::Encoder,
id: ast::node_id) { id: ast::node_id) {
let tcx = ecx.tcx; let tcx = ecx.tcx;
@ -931,11 +958,19 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
} }
} }
do option::iter(&tcx.value_modes.find(id)) |vm| { for maps.moves_map.find(id).each |_| {
do ebml_w.tag(c::tag_table_value_mode) { do ebml_w.tag(c::tag_table_moves_map) {
ebml_w.id(id);
}
}
for maps.capture_map.find(id).each |cap_vars| {
do ebml_w.tag(c::tag_table_capture_map) {
ebml_w.id(id); ebml_w.id(id);
do ebml_w.tag(c::tag_table_val) { do ebml_w.tag(c::tag_table_val) {
(*vm).encode(&ebml_w) do ebml_w.emit_from_vec(*cap_vars) |cap_var| {
cap_var.encode(&ebml_w);
}
} }
} }
} }
@ -980,10 +1015,24 @@ impl reader::Decoder: ebml_decoder_decoder_helpers {
// context. However, we do not bother, because region types // context. However, we do not bother, because region types
// are not used during trans. // are not used during trans.
do self.read_opaque |doc| { return do self.read_opaque |doc| {
tydecode::parse_ty_data(
let ty = tydecode::parse_ty_data(
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx, doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|s, a| self.convert_def_id(xcx, s, a)) |s, a| self.convert_def_id(xcx, s, a));
debug!("read_ty(%s) = %s",
type_string(doc), ty_to_str(xcx.dcx.tcx, ty));
ty
};
fn type_string(doc: ebml::Doc) -> ~str {
let mut str = ~"";
for uint::range(doc.start, doc.end) |i| {
str::push_char(&mut str, doc.data[i] as char);
}
str
} }
} }
@ -1034,10 +1083,12 @@ impl reader::Decoder: ebml_decoder_decoder_helpers {
* to refer to the new, cloned copy of the type parameter. * to refer to the new, cloned copy of the type parameter.
*/ */
match source { let r = match source {
NominalType | TypeWithId => xcx.tr_def_id(did), NominalType | TypeWithId => xcx.tr_def_id(did),
TypeParameter => xcx.tr_intern_def_id(did) TypeParameter => xcx.tr_intern_def_id(did)
} };
debug!("convert_def_id(source=%?, did=%?)=%?", source, did, r);
return r;
} }
} }
@ -1057,6 +1108,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
dcx.maps.mutbl_map.insert(id, ()); dcx.maps.mutbl_map.insert(id, ());
} else if tag == (c::tag_table_legacy_boxed_trait as uint) { } else if tag == (c::tag_table_legacy_boxed_trait as uint) {
dcx.tcx.legacy_boxed_traits.insert(id, ()); dcx.tcx.legacy_boxed_traits.insert(id, ());
} else if tag == (c::tag_table_moves_map as uint) {
dcx.maps.moves_map.insert(id, ());
} else { } else {
let val_doc = entry_doc[c::tag_table_val as uint]; let val_doc = entry_doc[c::tag_table_val as uint];
let val_dsr = &reader::Decoder(val_doc); let val_dsr = &reader::Decoder(val_doc);
@ -1065,6 +1118,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
dcx.tcx.def_map.insert(id, def); dcx.tcx.def_map.insert(id, def);
} else if tag == (c::tag_table_node_type as uint) { } else if tag == (c::tag_table_node_type as uint) {
let ty = val_dsr.read_ty(xcx); let ty = val_dsr.read_ty(xcx);
debug!("inserting ty for node %?: %s",
id, ty_to_str(dcx.tcx, ty));
(*dcx.tcx.node_types).insert(id as uint, ty); (*dcx.tcx.node_types).insert(id as uint, ty);
} else if tag == (c::tag_table_node_type_subst as uint) { } else if tag == (c::tag_table_node_type_subst as uint) {
let tys = val_dsr.read_tys(xcx); let tys = val_dsr.read_tys(xcx);
@ -1098,9 +1153,12 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
let adj: @ty::AutoAdjustment = @Decodable::decode(val_dsr); let adj: @ty::AutoAdjustment = @Decodable::decode(val_dsr);
adj.tr(xcx); adj.tr(xcx);
dcx.tcx.adjustments.insert(id, adj); dcx.tcx.adjustments.insert(id, adj);
} else if tag == (c::tag_table_value_mode as uint) { } else if tag == (c::tag_table_capture_map as uint) {
let vm: ty::ValueMode = Decodable::decode(val_dsr); let cvars =
dcx.tcx.value_modes.insert(id, vm); at_vec::from_owned(
val_dsr.read_to_vec(
|| val_dsr.read_capture_var(xcx)));
dcx.maps.capture_map.insert(id, cvars);
} else { } else {
xcx.dcx.tcx.sess.bug( xcx.dcx.tcx.sess.bug(
fmt!("unknown tag found in side tables: %x", tag)); fmt!("unknown tag found in side tables: %x", tag));

View file

@ -19,13 +19,15 @@
use core::prelude::*; use core::prelude::*;
use middle::borrowck::{Loan, bckerr, borrowck_ctxt, inherent_mutability}; use middle::moves;
use middle::borrowck::{Loan, bckerr, BorrowckCtxt, inherent_mutability};
use middle::borrowck::{req_maps, root_map_key, save_and_restore}; use middle::borrowck::{req_maps, root_map_key, save_and_restore};
use middle::borrowck::{MoveError, MoveOk, MoveFromIllegalCmt};
use middle::borrowck::{MoveWhileBorrowed};
use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref}; use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
use middle::mem_categorization::{cat_local, cat_rvalue, cat_self}; use middle::mem_categorization::{cat_local, cat_rvalue, cat_self};
use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg}; use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg};
use middle::mem_categorization::{lp_comp, lp_deref, lp_local}; use middle::mem_categorization::{lp_comp, lp_deref, lp_local};
use middle::ty::{CopyValue, MoveValue, ReadValue};
use middle::ty; use middle::ty;
use util::ppaux::ty_to_str; use util::ppaux::ty_to_str;
@ -42,7 +44,7 @@ use syntax::print::pprust;
use syntax::visit; use syntax::visit;
enum check_loan_ctxt = @{ enum check_loan_ctxt = @{
bccx: borrowck_ctxt, bccx: @BorrowckCtxt,
req_maps: req_maps, req_maps: req_maps,
reported: HashMap<ast::node_id, ()>, reported: HashMap<ast::node_id, ()>,
@ -63,9 +65,9 @@ enum purity_cause {
pc_cmt(bckerr) pc_cmt(bckerr)
} }
pub fn check_loans(bccx: borrowck_ctxt, pub fn check_loans(bccx: @BorrowckCtxt,
req_maps: req_maps, req_maps: req_maps,
crate: @ast::crate) { crate: @ast::crate) {
let clcx = check_loan_ctxt(@{bccx: bccx, let clcx = check_loan_ctxt(@{bccx: bccx,
req_maps: req_maps, req_maps: req_maps,
reported: HashMap(), reported: HashMap(),
@ -471,12 +473,40 @@ impl check_loan_ctxt {
} }
} }
fn check_move_out(ex: @ast::expr) { fn check_move_out_from_expr(ex: @ast::expr) {
let cmt = self.bccx.cat_expr(ex); match ex.node {
self.check_move_out_from_cmt(cmt); ast::expr_paren(*) => {
/* In the case of an expr_paren(), the expression inside
* the parens will also be marked as being moved. Ignore
* the parents then so as not to report duplicate errors. */
}
_ => {
let cmt = self.bccx.cat_expr(ex);
match self.analyze_move_out_from_cmt(cmt) {
MoveOk => {}
MoveFromIllegalCmt(_) => {
self.bccx.span_err(
cmt.span,
fmt!("moving out of %s",
self.bccx.cmt_to_str(cmt)));
}
MoveWhileBorrowed(_, loan_cmt) => {
self.bccx.span_err(
cmt.span,
fmt!("moving out of %s prohibited \
due to outstanding loan",
self.bccx.cmt_to_str(cmt)));
self.bccx.span_note(
loan_cmt.span,
fmt!("loan of %s granted here",
self.bccx.cmt_to_str(loan_cmt)));
}
}
}
}
} }
fn check_move_out_from_cmt(cmt: cmt) { fn analyze_move_out_from_cmt(cmt: cmt) -> MoveError {
debug!("check_move_out_from_cmt(cmt=%s)", debug!("check_move_out_from_cmt(cmt=%s)",
self.bccx.cmt_to_repr(cmt)); self.bccx.cmt_to_repr(cmt));
@ -493,59 +523,27 @@ impl check_loan_ctxt {
// Nothing else. // Nothing else.
_ => { _ => {
self.bccx.span_err( return MoveFromIllegalCmt(cmt);
cmt.span,
fmt!("moving out of %s", self.bccx.cmt_to_str(cmt)));
return;
} }
} }
self.bccx.add_to_mutbl_map(cmt); self.bccx.add_to_mutbl_map(cmt);
// check for a conflicting loan: // check for a conflicting loan:
let lp = match cmt.lp { for cmt.lp.each |lp| {
None => return, for self.walk_loans_of(cmt.id, *lp) |loan| {
Some(lp) => lp return MoveWhileBorrowed(cmt, loan.cmt);
};
for self.walk_loans_of(cmt.id, lp) |loan| {
self.bccx.span_err(
cmt.span,
fmt!("moving out of %s prohibited due to outstanding loan",
self.bccx.cmt_to_str(cmt)));
self.bccx.span_note(
loan.cmt.span,
fmt!("loan of %s granted here",
self.bccx.cmt_to_str(loan.cmt)));
return;
}
}
// Very subtle (#2633): liveness can mark options as last_use even
// when there is an outstanding loan. In that case, it is not
// safe to consider the use a last_use.
fn check_last_use(expr: @ast::expr) {
debug!("Checking last use of expr %?", expr.id);
let cmt = self.bccx.cat_expr(expr);
let lp = match cmt.lp {
None => {
debug!("Not a loanable expression");
return;
} }
Some(lp) => lp
};
for self.walk_loans_of(cmt.id, lp) |_loan| {
debug!("Removing last use entry %? due to outstanding loan",
expr.id);
self.bccx.last_use_map.remove(expr.id);
return;
} }
return MoveOk;
} }
fn check_call(expr: @ast::expr, fn check_call(expr: @ast::expr,
callee: Option<@ast::expr>, callee: Option<@ast::expr>,
callee_id: ast::node_id, callee_id: ast::node_id,
callee_span: span, callee_span: span,
args: ~[@ast::expr]) { args: &[@ast::expr]) {
match self.purity(expr.id) { match self.purity(expr.id) {
None => {} None => {}
Some(ref pc) => { Some(ref pc) => {
@ -557,35 +555,26 @@ impl check_loan_ctxt {
} }
} }
} }
let arg_tys =
ty::ty_fn_args(
ty::node_id_to_type(self.tcx(), callee_id));
for vec::each2(args, arg_tys) |arg, arg_ty| {
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
ast::by_move => {
self.check_move_out(*arg);
}
ast::by_ref |
ast::by_copy | ast::by_val => {
}
}
}
} }
} }
fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk, fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
sp: span, id: ast::node_id, &&self: check_loan_ctxt, sp: span, id: ast::node_id, &&self: check_loan_ctxt,
visitor: visit::vt<check_loan_ctxt>) { visitor: visit::vt<check_loan_ctxt>)
{
let is_stack_closure = self.is_stack_closure(id);
let fty = ty::node_id_to_type(self.tcx(), id);
let fty_proto = ty::ty_fn_proto(fty);
check_moves_from_captured_variables(self, id, fty_proto);
debug!("purity on entry=%?", copy self.declared_purity); debug!("purity on entry=%?", copy self.declared_purity);
do save_and_restore(&mut(self.declared_purity)) { do save_and_restore(&mut(self.declared_purity)) {
do save_and_restore(&mut(self.fn_args)) { do save_and_restore(&mut(self.fn_args)) {
let is_stack_closure = self.is_stack_closure(id);
let fty = ty::node_id_to_type(self.tcx(), id);
self.declared_purity = ty::determine_inherited_purity( self.declared_purity = ty::determine_inherited_purity(
copy self.declared_purity, copy self.declared_purity,
ty::ty_fn_purity(fty), ty::ty_fn_purity(fty),
ty::ty_fn_proto(fty)); fty_proto);
match fk { match fk {
visit::fk_anon(*) | visit::fk_anon(*) |
@ -616,6 +605,50 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
} }
} }
debug!("purity on exit=%?", copy self.declared_purity); debug!("purity on exit=%?", copy self.declared_purity);
fn check_moves_from_captured_variables(&&self: check_loan_ctxt,
id: ast::node_id,
fty_proto: ast::Proto)
{
match fty_proto {
ast::ProtoBox | ast::ProtoUniq => {
let cap_vars = self.bccx.capture_map.get(id);
for cap_vars.each |cap_var| {
match cap_var.mode {
moves::CapRef | moves::CapCopy => { loop; }
moves::CapMove => { }
}
let def_id = ast_util::def_id_of_def(cap_var.def).node;
let ty = ty::node_id_to_type(self.tcx(), def_id);
let cmt = self.bccx.cat_def(id, cap_var.span,
ty, cap_var.def);
let move_err = self.analyze_move_out_from_cmt(cmt);
match move_err {
MoveOk => {}
MoveFromIllegalCmt(move_cmt) => {
self.bccx.span_err(
cap_var.span,
fmt!("illegal by-move capture of %s",
self.bccx.cmt_to_str(move_cmt)));
}
MoveWhileBorrowed(move_cmt, loan_cmt) => {
self.bccx.span_err(
cap_var.span,
fmt!("by-move capture of %s prohibited \
due to outstanding loan",
self.bccx.cmt_to_str(move_cmt)));
self.bccx.span_note(
loan_cmt.span,
fmt!("loan of %s granted here",
self.bccx.cmt_to_str(loan_cmt)));
}
}
}
}
ast::ProtoBorrowed | ast::ProtoBare => {}
}
}
} }
fn check_loans_in_local(local: @ast::local, fn check_loans_in_local(local: @ast::local,
@ -632,48 +665,24 @@ fn check_loans_in_expr(expr: @ast::expr,
self.check_for_conflicting_loans(expr.id); self.check_for_conflicting_loans(expr.id);
// If this is a move, check it. if self.bccx.moves_map.contains_key(expr.id) {
match self.tcx().value_modes.find(expr.id) { self.check_move_out_from_expr(expr);
Some(MoveValue) => self.check_move_out(expr),
Some(ReadValue) | Some(CopyValue) | None => {}
} }
match /*bad*/copy expr.node { match expr.node {
ast::expr_path(*) if self.bccx.last_use_map.contains_key(expr.id) => {
self.check_last_use(expr);
}
ast::expr_swap(l, r) => { ast::expr_swap(l, r) => {
self.check_assignment(at_swap, l); self.check_assignment(at_swap, l);
self.check_assignment(at_swap, r); self.check_assignment(at_swap, r);
} }
ast::expr_unary_move(src) => {
self.check_move_out(src);
}
ast::expr_assign(dest, _) | ast::expr_assign(dest, _) |
ast::expr_assign_op(_, dest, _) => { ast::expr_assign_op(_, dest, _) => {
self.check_assignment(at_straight_up, dest); self.check_assignment(at_straight_up, dest);
} }
ast::expr_fn(_, _, _, cap_clause) | ast::expr_call(f, ref args, _) => {
ast::expr_fn_block(_, _, cap_clause) => { self.check_call(expr, Some(f), f.id, f.span, *args);
for (*cap_clause).each |cap_item| {
if cap_item.is_move {
let def = self.tcx().def_map.get(cap_item.id);
// Hack: the type that is used in the cmt doesn't actually
// matter here, so just subst nil instead of looking up
// the type of the def that is referred to
let cmt = self.bccx.cat_def(cap_item.id, cap_item.span,
ty::mk_nil(self.tcx()), def);
self.check_move_out_from_cmt(cmt);
}
}
} }
ast::expr_call(f, args, _) => { ast::expr_method_call(_, _, _, ref args, _) => {
self.check_call(expr, Some(f), f.id, f.span, args); self.check_call(expr, None, expr.callee_id, expr.span, *args);
}
ast::expr_method_call(_, _, _, args, _) => {
self.check_call(expr, None, expr.callee_id, expr.span, args);
} }
ast::expr_index(_, rval) | ast::expr_index(_, rval) |
ast::expr_binary(_, _, rval) ast::expr_binary(_, _, rval)
@ -692,6 +701,18 @@ fn check_loans_in_expr(expr: @ast::expr,
expr.span, expr.span,
~[]); ~[]);
} }
ast::expr_match(*) => {
// Note: moves out of pattern bindings are not checked by
// the borrow checker, at least not directly. What happens
// is that if there are any moved bindings, the discriminant
// will be considered a move, and this will be checked as
// normal. Then, in `middle::check_match`, we will check
// that no move occurs in a binding that is underneath an
// `@` or `&`. Together these give the same guarantees as
// `check_move_out_from_expr()` without requiring us to
// rewalk the patterns and rebuild the pattern
// categorizations.
}
_ => { } _ => { }
} }

View file

@ -18,8 +18,8 @@
use core::prelude::*; use core::prelude::*;
use middle::borrowck::preserve::{preserve_condition, pc_ok, pc_if_pure}; use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure};
use middle::borrowck::{Loan, bckerr, bckres, borrowck_ctxt, err_mutbl}; use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
use middle::borrowck::{req_maps}; use middle::borrowck::{req_maps};
use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant}; use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant};
use middle::mem_categorization::{mem_categorization_ctxt}; use middle::mem_categorization::{mem_categorization_ctxt};
@ -68,13 +68,13 @@ use syntax::visit;
/// No good. Instead what will happen is that `root_ub` will be set to the /// No good. Instead what will happen is that `root_ub` will be set to the
/// body of the while loop and we will refuse to root the pointer `&*x` /// body of the while loop and we will refuse to root the pointer `&*x`
/// because it would have to be rooted for a region greater than `root_ub`. /// because it would have to be rooted for a region greater than `root_ub`.
enum gather_loan_ctxt = @{bccx: borrowck_ctxt, enum gather_loan_ctxt = @{bccx: @BorrowckCtxt,
req_maps: req_maps, req_maps: req_maps,
mut item_ub: ast::node_id, mut item_ub: ast::node_id,
mut root_ub: ast::node_id, mut root_ub: ast::node_id,
mut ignore_adjustments: LinearSet<ast::node_id>}; mut ignore_adjustments: LinearSet<ast::node_id>};
pub fn gather_loans(bccx: borrowck_ctxt, crate: @ast::crate) -> req_maps { pub fn gather_loans(bccx: @BorrowckCtxt, crate: @ast::crate) -> req_maps {
let glcx = gather_loan_ctxt(@{bccx: bccx, let glcx = gather_loan_ctxt(@{bccx: bccx,
req_maps: {req_loan_map: HashMap(), req_maps: {req_loan_map: HashMap(),
pure_map: HashMap()}, pure_map: HashMap()},
@ -148,11 +148,11 @@ fn req_loans_in_expr(ex: @ast::expr,
let scope_r = ty::re_scope(ex.id); let scope_r = ty::re_scope(ex.id);
for vec::each2(args, arg_tys) |arg, arg_ty| { for vec::each2(args, arg_tys) |arg, arg_ty| {
match ty::resolved_mode(self.tcx(), arg_ty.mode) { match ty::resolved_mode(self.tcx(), arg_ty.mode) {
ast::by_ref => { ast::by_ref => {
let arg_cmt = self.bccx.cat_expr(*arg); let arg_cmt = self.bccx.cat_expr(*arg);
self.guarantee_valid(arg_cmt, m_imm, scope_r); self.guarantee_valid(arg_cmt, m_imm, scope_r);
} }
ast::by_val | ast::by_move | ast::by_copy => {} ast::by_val | ast::by_copy => {}
} }
} }
visit::visit_expr(ex, self, vt); visit::visit_expr(ex, self, vt);
@ -164,11 +164,11 @@ fn req_loans_in_expr(ex: @ast::expr,
let scope_r = ty::re_scope(ex.id); let scope_r = ty::re_scope(ex.id);
for vec::each2(args, arg_tys) |arg, arg_ty| { for vec::each2(args, arg_tys) |arg, arg_ty| {
match ty::resolved_mode(self.tcx(), arg_ty.mode) { match ty::resolved_mode(self.tcx(), arg_ty.mode) {
ast::by_ref => { ast::by_ref => {
let arg_cmt = self.bccx.cat_expr(*arg); let arg_cmt = self.bccx.cat_expr(*arg);
self.guarantee_valid(arg_cmt, m_imm, scope_r); self.guarantee_valid(arg_cmt, m_imm, scope_r);
} }
ast::by_val | ast::by_move | ast::by_copy => {} ast::by_val | ast::by_copy => {}
} }
} }
@ -374,7 +374,7 @@ impl gather_loan_ctxt {
// matches with the actual mutability (but if an immutable // matches with the actual mutability (but if an immutable
// pointer is desired, that is ok as long as we are pure) // pointer is desired, that is ok as long as we are pure)
None => { None => {
let result: bckres<preserve_condition> = { let result: bckres<PreserveCondition> = {
do self.check_mutbl(req_mutbl, cmt).chain |pc1| { do self.check_mutbl(req_mutbl, cmt).chain |pc1| {
do self.bccx.preserve(cmt, scope_r, do self.bccx.preserve(cmt, scope_r,
self.item_ub, self.item_ub,
@ -385,16 +385,16 @@ impl gather_loan_ctxt {
}; };
match result { match result {
Ok(pc_ok) => { Ok(PcOk) => {
debug!("result of preserve: pc_ok"); debug!("result of preserve: PcOk");
// we were able guarantee the validity of the ptr, // we were able guarantee the validity of the ptr,
// perhaps by rooting or because it is immutably // perhaps by rooting or because it is immutably
// rooted. good. // rooted. good.
self.bccx.stable_paths += 1; self.bccx.stable_paths += 1;
} }
Ok(pc_if_pure(ref e)) => { Ok(PcIfPure(ref e)) => {
debug!("result of preserve: %?", pc_if_pure((*e))); debug!("result of preserve: %?", PcIfPure((*e)));
// we are only able to guarantee the validity if // we are only able to guarantee the validity if
// the scope is pure // the scope is pure
@ -443,25 +443,25 @@ impl gather_loan_ctxt {
// mutable memory. // mutable memory.
fn check_mutbl(&self, fn check_mutbl(&self,
req_mutbl: ast::mutability, req_mutbl: ast::mutability,
cmt: cmt) -> bckres<preserve_condition> { cmt: cmt) -> bckres<PreserveCondition> {
debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)", debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)",
req_mutbl, cmt.mutbl); req_mutbl, cmt.mutbl);
if req_mutbl == m_const || req_mutbl == cmt.mutbl { if req_mutbl == m_const || req_mutbl == cmt.mutbl {
debug!("required is const or they are the same"); debug!("required is const or they are the same");
Ok(pc_ok) Ok(PcOk)
} else { } else {
let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) }; let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) };
if req_mutbl == m_imm { if req_mutbl == m_imm {
// if this is an @mut box, then it's generally OK to borrow as // if this is an @mut box, then it's generally OK to borrow as
// &imm; this will result in a write guard // &imm; this will result in a write guard
if cmt.cat.is_mutable_box() { if cmt.cat.is_mutable_box() {
Ok(pc_ok) Ok(PcOk)
} else { } else {
// you can treat mutable things as imm if you are pure // you can treat mutable things as imm if you are pure
debug!("imm required, must be pure"); debug!("imm required, must be pure");
Ok(pc_if_pure(e)) Ok(PcIfPure(e))
} }
} else { } else {
Err(e) Err(e)
@ -556,11 +556,6 @@ impl gather_loan_ctxt {
match pat.node { match pat.node {
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => { ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
match bm { match bm {
ast::bind_by_value | ast::bind_by_move => {
// copying does not borrow anything, so no check
// is required
// as for move, check::_match ensures it's from an rvalue.
}
ast::bind_by_ref(mutbl) => { ast::bind_by_ref(mutbl) => {
// ref x or ref x @ p --- creates a ptr which must // ref x or ref x @ p --- creates a ptr which must
// remain valid for the scope of the match // remain valid for the scope of the match
@ -582,9 +577,9 @@ impl gather_loan_ctxt {
self.guarantee_valid(cmt, mutbl, scope_r); self.guarantee_valid(cmt, mutbl, scope_r);
} }
} }
ast::bind_infer => { ast::bind_by_copy | ast::bind_infer => {
// Nothing to do here; this is either a copy or a move; // Nothing to do here; neither copies nor moves induce
// thus either way there is nothing to check. Yay! // borrows.
} }
} }
} }

View file

@ -43,7 +43,7 @@ XXX --- much more needed, don't have time to write this all up now
use core::prelude::*; use core::prelude::*;
use middle::borrowck::{Loan, bckerr, bckres, borrowck_ctxt, err_mutbl}; use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
use middle::borrowck::{err_out_of_scope}; use middle::borrowck::{err_out_of_scope};
use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp}; use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp};
use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self}; use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self};
@ -57,8 +57,9 @@ use core::result::{Err, Ok, Result};
use syntax::ast::{m_const, m_imm, m_mutbl}; use syntax::ast::{m_const, m_imm, m_mutbl};
use syntax::ast; use syntax::ast;
impl borrowck_ctxt { impl BorrowckCtxt {
fn loan(cmt: cmt, fn loan(&self,
cmt: cmt,
scope_region: ty::Region, scope_region: ty::Region,
mutbl: ast::mutability) -> bckres<~[Loan]> { mutbl: ast::mutability) -> bckres<~[Loan]> {
let lc = LoanContext { let lc = LoanContext {
@ -77,7 +78,7 @@ impl borrowck_ctxt {
} }
struct LoanContext { struct LoanContext {
bccx: borrowck_ctxt, bccx: &BorrowckCtxt,
// the region scope for which we must preserve the memory // the region scope for which we must preserve the memory
scope_region: ty::Region, scope_region: ty::Region,

View file

@ -230,6 +230,7 @@ use middle::liveness;
use middle::mem_categorization::*; use middle::mem_categorization::*;
use middle::region; use middle::region;
use middle::ty; use middle::ty;
use middle::moves;
use util::common::{indenter, stmt_set}; use util::common::{indenter, stmt_set};
use util::ppaux::{expr_repr, note_and_explain_region}; use util::ppaux::{expr_repr, note_and_explain_region};
use util::ppaux::{ty_to_str, region_to_str, explain_region}; use util::ppaux::{ty_to_str, region_to_str, explain_region};
@ -254,15 +255,17 @@ pub mod gather_loans;
pub mod loan; pub mod loan;
pub mod preserve; pub mod preserve;
pub fn check_crate(tcx: ty::ctxt, pub fn check_crate(
method_map: typeck::method_map, tcx: ty::ctxt,
last_use_map: liveness::last_use_map, method_map: typeck::method_map,
crate: @ast::crate) moves_map: moves::MovesMap,
-> (root_map, mutbl_map, write_guard_map) { capture_map: moves::CaptureMap,
crate: @ast::crate) -> (root_map, mutbl_map, write_guard_map)
let bccx = borrowck_ctxt_(@{tcx: tcx, {
let bccx = @BorrowckCtxt {tcx: tcx,
method_map: method_map, method_map: method_map,
last_use_map: last_use_map, moves_map: moves_map,
capture_map: capture_map,
root_map: root_map(), root_map: root_map(),
mutbl_map: HashMap(), mutbl_map: HashMap(),
write_guard_map: HashMap(), write_guard_map: HashMap(),
@ -271,7 +274,7 @@ pub fn check_crate(tcx: ty::ctxt,
mut loaned_paths_imm: 0, mut loaned_paths_imm: 0,
mut stable_paths: 0, mut stable_paths: 0,
mut req_pure_paths: 0, mut req_pure_paths: 0,
mut guaranteed_paths: 0}); mut guaranteed_paths: 0};
let req_maps = gather_loans::gather_loans(bccx, crate); let req_maps = gather_loans::gather_loans(bccx, crate);
check_loans::check_loans(bccx, req_maps, crate); check_loans::check_loans(bccx, req_maps, crate);
@ -292,7 +295,7 @@ pub fn check_crate(tcx: ty::ctxt,
return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map); return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str { fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str {
let stat_f = stat as float; let stat_f = stat as float;
let total = bccx.guaranteed_paths as float; let total = bccx.guaranteed_paths as float;
fmt!("%u (%.0f%%)", stat , stat_f * 100f / total) fmt!("%u (%.0f%%)", stat , stat_f * 100f / total)
@ -302,23 +305,22 @@ pub fn check_crate(tcx: ty::ctxt,
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// Type definitions // Type definitions
pub type borrowck_ctxt_ = {tcx: ty::ctxt, pub struct BorrowckCtxt {
method_map: typeck::method_map, tcx: ty::ctxt,
last_use_map: liveness::last_use_map, method_map: typeck::method_map,
root_map: root_map, moves_map: moves::MovesMap,
mutbl_map: mutbl_map, capture_map: moves::CaptureMap,
write_guard_map: write_guard_map, root_map: root_map,
stmt_map: stmt_set, mutbl_map: mutbl_map,
write_guard_map: write_guard_map,
stmt_map: stmt_set,
// Statistics: // Statistics:
mut loaned_paths_same: uint, mut loaned_paths_same: uint,
mut loaned_paths_imm: uint, mut loaned_paths_imm: uint,
mut stable_paths: uint, mut stable_paths: uint,
mut req_pure_paths: uint, mut req_pure_paths: uint,
mut guaranteed_paths: uint}; mut guaranteed_paths: uint
pub enum borrowck_ctxt {
borrowck_ctxt_(@borrowck_ctxt_)
} }
pub struct RootInfo { pub struct RootInfo {
@ -371,6 +373,12 @@ pub struct bckerr {
code: bckerr_code code: bckerr_code
} }
pub enum MoveError {
MoveOk,
MoveFromIllegalCmt(cmt),
MoveWhileBorrowed(/*move*/ cmt, /*loan*/ cmt)
}
// shorthand for something that fails with `bckerr` or succeeds with `T` // shorthand for something that fails with `bckerr` or succeeds with `T`
pub type bckres<T> = Result<T, bckerr>; pub type bckres<T> = Result<T, bckerr>;
@ -411,60 +419,62 @@ pub fn root_map() -> root_map {
// ___________________________________________________________________________ // ___________________________________________________________________________
// Misc // Misc
pub impl borrowck_ctxt { pub impl BorrowckCtxt {
fn is_subregion_of(r_sub: ty::Region, r_sup: ty::Region) -> bool { fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) -> bool {
region::is_subregion_of(self.tcx.region_map, r_sub, r_sup) region::is_subregion_of(self.tcx.region_map, r_sub, r_sup)
} }
fn cat_expr(expr: @ast::expr) -> cmt { fn cat_expr(&self, expr: @ast::expr) -> cmt {
cat_expr(self.tcx, self.method_map, expr) cat_expr(self.tcx, self.method_map, expr)
} }
fn cat_expr_unadjusted(expr: @ast::expr) -> cmt { fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt {
cat_expr_unadjusted(self.tcx, self.method_map, expr) cat_expr_unadjusted(self.tcx, self.method_map, expr)
} }
fn cat_expr_autoderefd(expr: @ast::expr, fn cat_expr_autoderefd(&self, expr: @ast::expr,
adj: @ty::AutoAdjustment) adj: @ty::AutoAdjustment)
-> cmt { -> cmt {
cat_expr_autoderefd(self.tcx, self.method_map, expr, adj) cat_expr_autoderefd(self.tcx, self.method_map, expr, adj)
} }
fn cat_def(id: ast::node_id, fn cat_def(&self,
id: ast::node_id,
span: span, span: span,
ty: ty::t, ty: ty::t,
def: ast::def) -> cmt { def: ast::def) -> cmt {
cat_def(self.tcx, self.method_map, id, span, ty, def) cat_def(self.tcx, self.method_map, id, span, ty, def)
} }
fn cat_variant<N: ast_node>(arg: N, fn cat_variant<N: ast_node>(&self,
arg: N,
enum_did: ast::def_id, enum_did: ast::def_id,
cmt: cmt) -> cmt { cmt: cmt) -> cmt {
cat_variant(self.tcx, self.method_map, arg, enum_did, cmt) cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
} }
fn cat_discr(cmt: cmt, match_id: ast::node_id) -> cmt { fn cat_discr(&self, cmt: cmt, match_id: ast::node_id) -> cmt {
return @cmt_ { cat: cat_discr(cmt, match_id),.. *cmt }; return @cmt_ {cat:cat_discr(cmt, match_id),.. *cmt};
} }
fn mc_ctxt() -> mem_categorization_ctxt { fn mc_ctxt(&self) -> mem_categorization_ctxt {
mem_categorization_ctxt {tcx: self.tcx, mem_categorization_ctxt {tcx: self.tcx,
method_map: self.method_map} method_map: self.method_map}
} }
fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) { fn cat_pattern(&self, cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
let mc = self.mc_ctxt(); let mc = self.mc_ctxt();
mc.cat_pattern(cmt, pat, op); mc.cat_pattern(cmt, pat, op);
} }
fn report_if_err(bres: bckres<()>) { fn report_if_err(&self, bres: bckres<()>) {
match bres { match bres {
Ok(()) => (), Ok(()) => (),
Err(ref e) => self.report((*e)) Err(ref e) => self.report((*e))
} }
} }
fn report(err: bckerr) { fn report(&self, err: bckerr) {
self.span_err( self.span_err(
err.cmt.span, err.cmt.span,
fmt!("illegal borrow: %s", fmt!("illegal borrow: %s",
@ -472,15 +482,15 @@ pub impl borrowck_ctxt {
self.note_and_explain_bckerr(err); self.note_and_explain_bckerr(err);
} }
fn span_err(s: span, +m: ~str) { fn span_err(&self, s: span, +m: ~str) {
self.tcx.sess.span_err(s, m); self.tcx.sess.span_err(s, m);
} }
fn span_note(s: span, +m: ~str) { fn span_note(&self, s: span, +m: ~str) {
self.tcx.sess.span_note(s, m); self.tcx.sess.span_note(s, m);
} }
fn add_to_mutbl_map(cmt: cmt) { fn add_to_mutbl_map(&self, cmt: cmt) {
match cmt.cat { match cmt.cat {
cat_local(id) | cat_arg(id) => { cat_local(id) | cat_arg(id) => {
self.mutbl_map.insert(id, ()); self.mutbl_map.insert(id, ());
@ -492,7 +502,7 @@ pub impl borrowck_ctxt {
} }
} }
fn bckerr_to_str(err: bckerr) -> ~str { fn bckerr_to_str(&self, err: bckerr) -> ~str {
match err.code { match err.code {
err_mutbl(req) => { err_mutbl(req) => {
fmt!("creating %s alias to %s", fmt!("creating %s alias to %s",
@ -520,7 +530,7 @@ pub impl borrowck_ctxt {
} }
} }
fn note_and_explain_bckerr(err: bckerr) { fn note_and_explain_bckerr(&self, err: bckerr) {
let code = err.code; let code = err.code;
match code { match code {
err_mutbl(*) | err_mut_uniq | err_mut_variant | err_mutbl(*) | err_mut_uniq | err_mut_variant |
@ -555,25 +565,25 @@ pub impl borrowck_ctxt {
} }
fn cmt_to_str(cmt: cmt) -> ~str { fn cmt_to_str(&self, cmt: cmt) -> ~str {
let mc = &mem_categorization_ctxt {tcx: self.tcx, let mc = &mem_categorization_ctxt {tcx: self.tcx,
method_map: self.method_map}; method_map: self.method_map};
mc.cmt_to_str(cmt) mc.cmt_to_str(cmt)
} }
fn cmt_to_repr(cmt: cmt) -> ~str { fn cmt_to_repr(&self, cmt: cmt) -> ~str {
let mc = &mem_categorization_ctxt {tcx: self.tcx, let mc = &mem_categorization_ctxt {tcx: self.tcx,
method_map: self.method_map}; method_map: self.method_map};
mc.cmt_to_repr(cmt) mc.cmt_to_repr(cmt)
} }
fn mut_to_str(mutbl: ast::mutability) -> ~str { fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
let mc = &mem_categorization_ctxt {tcx: self.tcx, let mc = &mem_categorization_ctxt {tcx: self.tcx,
method_map: self.method_map}; method_map: self.method_map};
mc.mut_to_str(mutbl) mc.mut_to_str(mutbl)
} }
fn loan_to_repr(loan: &Loan) -> ~str { fn loan_to_repr(&self, loan: &Loan) -> ~str {
fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)", fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl) loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
} }

View file

@ -15,7 +15,7 @@
use core::prelude::*; use core::prelude::*;
use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, borrowck_ctxt}; use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, BorrowckCtxt};
use middle::borrowck::{err_mut_uniq, err_mut_variant}; use middle::borrowck::{err_mut_uniq, err_mut_variant};
use middle::borrowck::{err_out_of_root_scope, err_out_of_scope}; use middle::borrowck::{err_out_of_root_scope, err_out_of_scope};
use middle::borrowck::{err_root_not_permitted, root_map_key}; use middle::borrowck::{err_root_not_permitted, root_map_key};
@ -30,40 +30,42 @@ use util::common::indenter;
use syntax::ast::{m_const, m_imm, m_mutbl}; use syntax::ast::{m_const, m_imm, m_mutbl};
use syntax::ast; use syntax::ast;
pub enum preserve_condition { pub enum PreserveCondition {
pc_ok, PcOk,
pc_if_pure(bckerr) PcIfPure(bckerr)
} }
impl preserve_condition { impl PreserveCondition {
// combines two preservation conditions such that if either of // combines two preservation conditions such that if either of
// them requires purity, the result requires purity // them requires purity, the result requires purity
fn combine(pc: preserve_condition) -> preserve_condition { fn combine(&self, pc: PreserveCondition) -> PreserveCondition {
match self { match *self {
pc_ok => {pc} PcOk => {pc}
pc_if_pure(_) => {self} PcIfPure(_) => {*self}
} }
} }
} }
impl borrowck_ctxt { impl BorrowckCtxt {
fn preserve(cmt: cmt, fn preserve(&self,
cmt: cmt,
scope_region: ty::Region, scope_region: ty::Region,
item_ub: ast::node_id, item_ub: ast::node_id,
root_ub: ast::node_id) root_ub: ast::node_id) -> bckres<PreserveCondition>
-> bckres<preserve_condition> { {
let ctxt = PreserveCtxt {
let ctxt = preserve_ctxt({bccx: self, bccx: self,
scope_region: scope_region, scope_region: scope_region,
item_ub: item_ub, item_ub: item_ub,
root_ub: root_ub, root_ub: root_ub,
root_managed_data: true}); root_managed_data: true
(&ctxt).preserve(cmt) };
ctxt.preserve(cmt)
} }
} }
enum preserve_ctxt = { struct PreserveCtxt {
bccx: borrowck_ctxt, bccx: &BorrowckCtxt,
// the region scope for which we must preserve the memory // the region scope for which we must preserve the memory
scope_region: ty::Region, scope_region: ty::Region,
@ -76,13 +78,12 @@ enum preserve_ctxt = {
// if false, do not attempt to root managed data // if false, do not attempt to root managed data
root_managed_data: bool root_managed_data: bool
}; }
impl PreserveCtxt {
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
priv impl &preserve_ctxt { fn preserve(&self, cmt: cmt) -> bckres<PreserveCondition> {
fn tcx() -> ty::ctxt { self.bccx.tcx }
fn preserve(cmt: cmt) -> bckres<preserve_condition> {
debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)", debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)",
self.bccx.cmt_to_repr(cmt), self.root_ub, self.bccx.cmt_to_repr(cmt), self.root_ub,
self.root_managed_data); self.root_managed_data);
@ -94,7 +95,7 @@ priv impl &preserve_ctxt {
self.compare_scope(cmt, ty::re_scope(self.item_ub)) self.compare_scope(cmt, ty::re_scope(self.item_ub))
} }
cat_special(sk_static_item) | cat_special(sk_method) => { cat_special(sk_static_item) | cat_special(sk_method) => {
Ok(pc_ok) Ok(PcOk)
} }
cat_rvalue => { cat_rvalue => {
// when we borrow an rvalue, we can keep it rooted but only // when we borrow an rvalue, we can keep it rooted but only
@ -181,7 +182,7 @@ priv impl &preserve_ctxt {
} }
cat_deref(_, _, unsafe_ptr) => { cat_deref(_, _, unsafe_ptr) => {
// Unsafe pointers are the user's problem // Unsafe pointers are the user's problem
Ok(pc_ok) Ok(PcOk)
} }
cat_deref(base, derefs, gc_ptr(*)) => { cat_deref(base, derefs, gc_ptr(*)) => {
// GC'd pointers of type @MT: if this pointer lives in // GC'd pointers of type @MT: if this pointer lives in
@ -193,13 +194,15 @@ priv impl &preserve_ctxt {
if cmt.cat.derefs_through_mutable_box() { if cmt.cat.derefs_through_mutable_box() {
self.attempt_root(cmt, base, derefs) self.attempt_root(cmt, base, derefs)
} else if base.mutbl == m_imm { } else if base.mutbl == m_imm {
let non_rooting_ctxt = let non_rooting_ctxt = PreserveCtxt {
preserve_ctxt({root_managed_data: false,.. **self}); root_managed_data: false,
match (&non_rooting_ctxt).preserve(base) { ..*self
Ok(pc_ok) => { };
Ok(pc_ok) match non_rooting_ctxt.preserve(base) {
Ok(PcOk) => {
Ok(PcOk)
} }
Ok(pc_if_pure(_)) => { Ok(PcIfPure(_)) => {
debug!("must root @T, otherwise purity req'd"); debug!("must root @T, otherwise purity req'd");
self.attempt_root(cmt, base, derefs) self.attempt_root(cmt, base, derefs)
} }
@ -267,10 +270,11 @@ priv impl &preserve_ctxt {
// node appears to draw the line between what will be rooted // node appears to draw the line between what will be rooted
// in the *arm* vs the *match*. // in the *arm* vs the *match*.
let match_rooting_ctxt = let match_rooting_ctxt = PreserveCtxt {
preserve_ctxt({scope_region: ty::re_scope(match_id), scope_region: ty::re_scope(match_id),
.. **self}); ..*self
(&match_rooting_ctxt).preserve(base) };
match_rooting_ctxt.preserve(base)
} }
} }
} }
@ -279,28 +283,29 @@ priv impl &preserve_ctxt {
/// `base`) be found in an immutable location (that is, `base` /// `base`) be found in an immutable location (that is, `base`
/// must be immutable). Also requires that `base` itself is /// must be immutable). Also requires that `base` itself is
/// preserved. /// preserved.
fn require_imm(cmt: cmt, fn require_imm(&self,
cmt: cmt,
cmt_base: cmt, cmt_base: cmt,
code: bckerr_code) -> bckres<preserve_condition> { code: bckerr_code) -> bckres<PreserveCondition> {
// Variant contents and unique pointers: must be immutably // Variant contents and unique pointers: must be immutably
// rooted to a preserved address. // rooted to a preserved address.
match self.preserve(cmt_base) { match self.preserve(cmt_base) {
// the base is preserved, but if we are not mutable then // the base is preserved, but if we are not mutable then
// purity is required // purity is required
Ok(pc_ok) => { Ok(PcOk) => {
match cmt_base.mutbl { match cmt_base.mutbl {
m_mutbl | m_const => { m_mutbl | m_const => {
Ok(pc_if_pure(bckerr { cmt: cmt, code: code })) Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
} }
m_imm => { m_imm => {
Ok(pc_ok) Ok(PcOk)
} }
} }
} }
// the base requires purity too, that's fine // the base requires purity too, that's fine
Ok(pc_if_pure(ref e)) => { Ok(PcIfPure(ref e)) => {
Ok(pc_if_pure((*e))) Ok(PcIfPure((*e)))
} }
// base is not stable, doesn't matter // base is not stable, doesn't matter
@ -312,10 +317,11 @@ priv impl &preserve_ctxt {
/// Checks that the scope for which the value must be preserved /// Checks that the scope for which the value must be preserved
/// is a subscope of `scope_ub`; if so, success. /// is a subscope of `scope_ub`; if so, success.
fn compare_scope(cmt: cmt, fn compare_scope(&self,
scope_ub: ty::Region) -> bckres<preserve_condition> { cmt: cmt,
scope_ub: ty::Region) -> bckres<PreserveCondition> {
if self.bccx.is_subregion_of(self.scope_region, scope_ub) { if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
Ok(pc_ok) Ok(PcOk)
} else { } else {
Err(bckerr { Err(bckerr {
cmt:cmt, cmt:cmt,
@ -333,10 +339,8 @@ priv impl &preserve_ctxt {
/// value live for longer than the current fn or else potentially /// value live for longer than the current fn or else potentially
/// require that an statically unbounded number of values be /// require that an statically unbounded number of values be
/// rooted (if a loop exists). /// rooted (if a loop exists).
fn attempt_root(cmt: cmt, fn attempt_root(&self, cmt: cmt, base: cmt,
base: cmt, derefs: uint) -> bckres<PreserveCondition> {
derefs: uint)
-> bckres<preserve_condition> {
if !self.root_managed_data { if !self.root_managed_data {
// normally, there is a root_ub; the only time that this // normally, there is a root_ub; the only time that this
// is none is when a boxed value is stored in an immutable // is none is when a boxed value is stored in an immutable
@ -387,7 +391,7 @@ priv impl &preserve_ctxt {
scope: scope_to_use, scope: scope_to_use,
freezes: cmt.cat.derefs_through_mutable_box() freezes: cmt.cat.derefs_through_mutable_box()
}); });
return Ok(pc_ok); return Ok(PcOk);
} else { } else {
debug!("Unable to root"); debug!("Unable to root");
return Err(bckerr { return Err(bckerr {

View file

@ -1,141 +0,0 @@
// Copyright 2012 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.
use core::prelude::*;
use middle::freevars;
use middle::ty;
use core::option;
use core::vec;
use std::map::HashMap;
use std::map;
use syntax::codemap::span;
use syntax::{ast, ast_util};
pub enum capture_mode {
cap_copy, // Copy the value into the closure.
cap_move, // Move the value into the closure.
cap_drop, // Drop value after creating closure.
cap_ref, // Reference directly from parent stack frame (block fn).
}
pub type capture_var = {
def: ast::def, // Variable being accessed free
span: span, // Location of access or cap item
cap_item: Option<ast::capture_item>, // Capture item, if any
mode: capture_mode // How variable is being accessed
};
pub type capture_map = map::HashMap<ast::def_id, capture_var>;
// checks the capture clause for a fn_expr() and issues warnings or
// errors for any irregularities which we identify.
pub fn check_capture_clause(tcx: ty::ctxt,
fn_expr_id: ast::node_id,
cap_clause: ast::capture_clause) {
let freevars = freevars::get_freevars(tcx, fn_expr_id);
let seen_defs = map::HashMap();
for (*cap_clause).each |cap_item| {
let cap_def = tcx.def_map.get(cap_item.id);
if !vec::any(*freevars, |fv| fv.def == cap_def ) {
tcx.sess.span_warn(
cap_item.span,
fmt!("captured variable `%s` not used in closure",
tcx.sess.str_of(cap_item.name)));
}
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
if !seen_defs.insert(cap_def_id, ()) {
tcx.sess.span_err(
cap_item.span,
fmt!("variable `%s` captured more than once",
tcx.sess.str_of(cap_item.name)));
}
}
}
pub fn compute_capture_vars(tcx: ty::ctxt,
fn_expr_id: ast::node_id,
fn_proto: ast::Proto,
cap_clause: ast::capture_clause)
-> ~[capture_var] {
let freevars = freevars::get_freevars(tcx, fn_expr_id);
let cap_map = map::HashMap();
// first add entries for anything explicitly named in the cap clause
for (*cap_clause).each |cap_item| {
debug!("Doing capture var: %s (%?)",
tcx.sess.str_of(cap_item.name), cap_item.id);
let cap_def = tcx.def_map.get(cap_item.id);
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
if cap_item.is_move {
// if we are moving the value in, but it's not actually used,
// must drop it.
if vec::any(*freevars, |fv| fv.def == cap_def ) {
cap_map.insert(cap_def_id, {def:cap_def,
span: cap_item.span,
cap_item: Some(*cap_item),
mode:cap_move});
} else {
cap_map.insert(cap_def_id, {def:cap_def,
span: cap_item.span,
cap_item: Some(*cap_item),
mode:cap_drop});
}
} else {
// if we are copying the value in, but it's not actually used,
// just ignore it.
if vec::any(*freevars, |fv| fv.def == cap_def ) {
cap_map.insert(cap_def_id, {def:cap_def,
span: cap_item.span,
cap_item: Some(*cap_item),
mode:cap_copy});
}
}
}
// now go through anything that is referenced but was not explicitly
// named and add that
let implicit_mode_is_by_ref = fn_proto == ast::ProtoBorrowed;
for vec::each(*freevars) |fvar| {
let fvar_def_id = ast_util::def_id_of_def(fvar.def).node;
match cap_map.find(fvar_def_id) {
option::Some(_) => { /* was explicitly named, do nothing */ }
option::None => {
// Move if this type implicitly moves; copy otherwise.
let mode;
if implicit_mode_is_by_ref {
mode = cap_ref;
} else {
let fvar_ty = ty::node_id_to_type(tcx, fvar_def_id);
if ty::type_implicitly_moves(tcx, fvar_ty) {
mode = cap_move;
} else {
mode = cap_copy;
}
};
cap_map.insert(fvar_def_id, {def:fvar.def,
span: fvar.span,
cap_item: None,
mode:mode});
}
}
}
let mut result = ~[];
for cap_map.each_value |cap_var| { result.push(cap_var); }
return result;
}

View file

@ -32,13 +32,13 @@ pub fn check_crate(tcx: ty::ctxt, crate: @crate) {
expr_loop(ref b, _) => { expr_loop(ref b, _) => {
(v.visit_block)((*b), {in_loop: true,.. cx}, v); (v.visit_block)((*b), {in_loop: true,.. cx}, v);
} }
expr_fn(_, _, _, _) => { expr_fn(_, _, _) => {
visit::visit_expr(e, {in_loop: false, can_ret: true}, v); visit::visit_expr(e, {in_loop: false, can_ret: true}, v);
} }
expr_fn_block(_, ref b, _) => { expr_fn_block(_, ref b) => {
(v.visit_block)((*b), {in_loop: false, can_ret: false}, v); (v.visit_block)((*b), {in_loop: false, can_ret: false}, v);
} }
expr_loop_body(@expr {node: expr_fn_block(_, ref b, _), _}) => { expr_loop_body(@expr {node: expr_fn_block(_, ref b), _}) => {
let proto = ty::ty_fn_proto(ty::expr_ty(tcx, e)); let proto = ty::ty_fn_proto(ty::expr_ty(tcx, e));
let blk = (proto == ProtoBorrowed); let blk = (proto == ProtoBorrowed);
(v.visit_block)((*b), {in_loop: true, can_ret: blk}, v); (v.visit_block)((*b), {in_loop: true, can_ret: blk}, v);

View file

@ -16,6 +16,7 @@ use middle::pat_util::*;
use middle::ty::*; use middle::ty::*;
use middle::ty; use middle::ty;
use middle::typeck::method_map; use middle::typeck::method_map;
use middle::moves;
use util::ppaux::ty_to_str; use util::ppaux::ty_to_str;
use core::cmp; use core::cmp;
@ -34,10 +35,16 @@ use syntax::visit;
pub struct MatchCheckCtxt { pub struct MatchCheckCtxt {
tcx: ty::ctxt, tcx: ty::ctxt,
method_map: method_map, method_map: method_map,
moves_map: moves::MovesMap
} }
pub fn check_crate(tcx: ty::ctxt, method_map: method_map, crate: @crate) { pub fn check_crate(tcx: ty::ctxt,
let cx = @MatchCheckCtxt { tcx: tcx, method_map: method_map }; method_map: method_map,
moves_map: moves::MovesMap,
crate: @crate) {
let cx = @MatchCheckCtxt {tcx: tcx,
method_map: method_map,
moves_map: moves_map};
visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor { visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor {
visit_expr: |a,b,c| check_expr(cx, a, b, c), visit_expr: |a,b,c| check_expr(cx, a, b, c),
visit_local: |a,b,c| check_local(cx, a, b, c), visit_local: |a,b,c| check_local(cx, a, b, c),
@ -53,13 +60,7 @@ pub fn expr_is_non_moving_lvalue(cx: @MatchCheckCtxt, expr: @expr) -> bool {
return false; return false;
} }
match cx.tcx.value_modes.find(expr.id) { !cx.moves_map.contains_key(expr.id)
Some(MoveValue) => return false,
Some(CopyValue) | Some(ReadValue) => return true,
None => {
cx.tcx.sess.span_bug(expr.span, ~"no entry in value mode map");
}
}
} }
pub fn check_expr(cx: @MatchCheckCtxt, ex: @expr, &&s: (), v: visit::vt<()>) { pub fn check_expr(cx: @MatchCheckCtxt, ex: @expr, &&s: (), v: visit::vt<()>) {
@ -113,7 +114,7 @@ pub fn check_arms(cx: @MatchCheckCtxt, arms: ~[arm]) {
for arms.each |arm| { for arms.each |arm| {
for arm.pats.each |pat| { for arm.pats.each |pat| {
let v = ~[*pat]; let v = ~[*pat];
match is_useful(cx, seen, v) { match is_useful(cx, copy seen, v) {
not_useful => { not_useful => {
cx.tcx.sess.span_err(pat.span, ~"unreachable pattern"); cx.tcx.sess.span_err(pat.span, ~"unreachable pattern");
} }
@ -197,7 +198,7 @@ pub enum ctor {
// Note: is_useful doesn't work on empty types, as the paper notes. // Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty. // So it assumes that v is non-empty.
pub fn is_useful(cx: @MatchCheckCtxt, +m: matrix, +v: ~[@pat]) -> useful { pub fn is_useful(cx: @MatchCheckCtxt, +m: matrix, +v: &[@pat]) -> useful {
if m.len() == 0u { return useful_; } if m.len() == 0u { return useful_; }
if m[0].len() == 0u { return not_useful; } if m[0].len() == 0u { return not_useful; }
let real_pat = match vec::find(m, |r| r[0].id != 0) { let real_pat = match vec::find(m, |r| r[0].id != 0) {
@ -272,12 +273,12 @@ pub fn is_useful(cx: @MatchCheckCtxt, +m: matrix, +v: ~[@pat]) -> useful {
pub fn is_useful_specialized(cx: @MatchCheckCtxt, pub fn is_useful_specialized(cx: @MatchCheckCtxt,
m: matrix, m: matrix,
+v: ~[@pat], +v: &[@pat],
+ctor: ctor, +ctor: ctor,
arity: uint, arity: uint,
lty: ty::t) lty: ty::t)
-> useful { -> useful {
let ms = vec::filter_map(m, |r| specialize(cx, copy *r, let ms = vec::filter_map(m, |r| specialize(cx, *r,
ctor, arity, lty)); ctor, arity, lty));
let could_be_useful = is_useful( let could_be_useful = is_useful(
cx, ms, specialize(cx, v, ctor, arity, lty).get()); cx, ms, specialize(cx, v, ctor, arity, lty).get());
@ -467,7 +468,7 @@ pub fn wild() -> @pat {
} }
pub fn specialize(cx: @MatchCheckCtxt, pub fn specialize(cx: @MatchCheckCtxt,
+r: ~[@pat], +r: &[@pat],
ctor_id: ctor, ctor_id: ctor,
arity: uint, arity: uint,
left_ty: ty::t) left_ty: ty::t)
@ -729,21 +730,13 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
for pats.each |pat| { for pats.each |pat| {
do pat_bindings(def_map, *pat) |bm, id, span, _path| { do pat_bindings(def_map, *pat) |bm, id, span, _path| {
match bm { match bm {
bind_by_copy => {}
bind_by_ref(_) => { bind_by_ref(_) => {
by_ref_span = Some(span); by_ref_span = Some(span);
} }
bind_by_move => {
any_by_move = true;
}
bind_by_value => {}
bind_infer => { bind_infer => {
match cx.tcx.value_modes.find(id) { if cx.moves_map.contains_key(id) {
Some(MoveValue) => any_by_move = true, any_by_move = true;
Some(CopyValue) | Some(ReadValue) => {}
None => {
cx.tcx.sess.span_bug(span, ~"no mode for pat \
binding");
}
} }
} }
} }
@ -781,18 +774,18 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
do walk_pat(*pat) |p| { do walk_pat(*pat) |p| {
if pat_is_binding(def_map, p) { if pat_is_binding(def_map, p) {
match p.node { match p.node {
pat_ident(bind_by_move, _, sub) => check_move(p, sub), pat_ident(_, _, sub) => {
pat_ident(bind_infer, _, sub) => { if cx.moves_map.contains_key(p.id) {
match tcx.value_modes.find(p.id) { check_move(p, sub);
Some(MoveValue) => check_move(p, sub),
Some(CopyValue) | Some(ReadValue) => {}
None => {
cx.tcx.sess.span_bug(
pat.span, ~"no mode for pat binding");
}
} }
} }
_ => {} _ => {
cx.tcx.sess.span_bug(
p.span,
fmt!("Binding pattern %d is \
not an identifier: %?",
p.id, p.node));
}
} }
} }
} }
@ -800,32 +793,23 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
// Now check to ensure that any move binding is not behind an @ or &. // Now check to ensure that any move binding is not behind an @ or &.
// This is always illegal. // This is always illegal.
let vt = visit::mk_vt(@visit::Visitor { let vt = visit::mk_vt(@visit::Visitor {
visit_pat: |pat, behind_bad_pointer, v| { visit_pat: |pat, behind_bad_pointer: bool, v| {
let error_out = || {
cx.tcx.sess.span_err(pat.span, ~"by-move pattern \
bindings may not occur \
behind @ or & bindings");
};
match pat.node { match pat.node {
pat_ident(binding_mode, _, sub) => { pat_ident(_, _, sub) => {
debug!("(check legality of move) checking pat \ debug!("(check legality of move) checking pat \
ident with behind_bad_pointer %?", ident with behind_bad_pointer %?",
behind_bad_pointer); behind_bad_pointer);
match binding_mode {
bind_by_move if behind_bad_pointer => error_out(), if behind_bad_pointer &&
bind_infer if behind_bad_pointer => { cx.moves_map.contains_key(pat.id)
match cx.tcx.value_modes.find(pat.id) { {
Some(MoveValue) => error_out(), cx.tcx.sess.span_err(
Some(CopyValue) | pat.span,
Some(ReadValue) => {} ~"by-move pattern \
None => { bindings may not occur \
cx.tcx.sess.span_bug(pat.span, behind @ or & bindings");
~"no mode for pat binding");
}
}
}
_ => {}
} }
match sub { match sub {
None => {} None => {}
Some(subpat) => { Some(subpat) => {
@ -833,9 +817,11 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
} }
} }
} }
pat_box(subpat) | pat_region(subpat) => { pat_box(subpat) | pat_region(subpat) => {
(v.visit_pat)(subpat, true, v); (v.visit_pat)(subpat, true, v);
} }
_ => visit::visit_pat(pat, behind_bad_pointer, v) _ => visit::visit_pat(pat, behind_bad_pointer, v)
} }
}, },

View file

@ -413,8 +413,8 @@ pub fn lit_to_const(lit: @lit) -> const_val {
} }
pub fn compare_const_vals(a: const_val, b: const_val) -> int { pub fn compare_const_vals(a: const_val, b: const_val) -> int {
match (a, b) { match (&a, &b) {
(const_int(a), const_int(b)) => { (&const_int(a), &const_int(b)) => {
if a == b { if a == b {
0 0
} else if a < b { } else if a < b {
@ -423,7 +423,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
1 1
} }
} }
(const_uint(a), const_uint(b)) => { (&const_uint(a), &const_uint(b)) => {
if a == b { if a == b {
0 0
} else if a < b { } else if a < b {
@ -432,7 +432,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
1 1
} }
} }
(const_float(a), const_float(b)) => { (&const_float(a), &const_float(b)) => {
if a == b { if a == b {
0 0
} else if a < b { } else if a < b {
@ -441,7 +441,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
1 1
} }
} }
(const_str(ref a), const_str(ref b)) => { (&const_str(ref a), &const_str(ref b)) => {
if (*a) == (*b) { if (*a) == (*b) {
0 0
} else if (*a) < (*b) { } else if (*a) < (*b) {
@ -450,7 +450,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
1 1
} }
} }
(const_bool(a), const_bool(b)) => { (&const_bool(a), &const_bool(b)) => {
if a == b { if a == b {
0 0
} else if a < b { } else if a < b {

View file

@ -48,7 +48,7 @@ fn collect_freevars(def_map: resolve::DefMap, blk: ast::blk)
let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) { let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
match expr.node { match expr.node {
ast::expr_fn(proto, _, _, _) => { ast::expr_fn(proto, _, _) => {
if proto != ast::ProtoBare { if proto != ast::ProtoBare {
visit::visit_expr(expr, depth + 1, v); visit::visit_expr(expr, depth + 1, v);
} }
@ -123,6 +123,7 @@ pub fn get_freevars(tcx: ty::ctxt, fid: ast::node_id) -> freevar_info {
Some(d) => return d Some(d) => return d
} }
} }
pub fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool { pub fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool {
return vec::len(*get_freevars(tcx, fid)) != 0u; return vec::len(*get_freevars(tcx, fid)) != 0u;
} }

View file

@ -15,7 +15,6 @@ use middle::freevars;
use middle::lint::{non_implicitly_copyable_typarams, implicit_copies}; use middle::lint::{non_implicitly_copyable_typarams, implicit_copies};
use middle::liveness; use middle::liveness;
use middle::pat_util; use middle::pat_util;
use middle::ty::{CopyValue, MoveValue, ReadValue};
use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const}; use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const};
use middle::ty; use middle::ty;
use middle::typeck; use middle::typeck;
@ -102,8 +101,6 @@ pub fn check_crate(tcx: ty::ctxt,
let visit = visit::mk_vt(@visit::Visitor { let visit = visit::mk_vt(@visit::Visitor {
visit_arm: check_arm, visit_arm: check_arm,
visit_expr: check_expr, visit_expr: check_expr,
visit_stmt: check_stmt,
visit_block: check_block,
visit_fn: check_fn, visit_fn: check_fn,
visit_ty: check_ty, visit_ty: check_ty,
visit_item: fn@(i: @item, cx: ctx, v: visit::vt<ctx>) { visit_item: fn@(i: @item, cx: ctx, v: visit::vt<ctx>) {
@ -115,75 +112,41 @@ pub fn check_crate(tcx: ty::ctxt,
tcx.sess.abort_if_errors(); tcx.sess.abort_if_errors();
} }
// bool flag is only used for checking closures, type check_fn = fn@(ctx, @freevar_entry);
// where it refers to whether a var is 'move' in the
// capture clause
pub type check_fn = fn@(ctx,
node_id,
Option<@freevar_entry>,
bool,
ty::t,
sp: span);
// Yields the appropriate function to check the kind of closed over // Yields the appropriate function to check the kind of closed over
// variables. `id` is the node_id for some expression that creates the // variables. `id` is the node_id for some expression that creates the
// closure. // closure.
pub fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) { fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
fn check_for_uniq(cx: ctx, id: node_id, fv: Option<@freevar_entry>, fn check_for_uniq(cx: ctx, fv: @freevar_entry) {
is_move: bool, var_t: ty::t, sp: span) {
// all captured data must be sendable, regardless of whether it is // all captured data must be sendable, regardless of whether it is
// moved in or copied in. Note that send implies owned. // moved in or copied in. Note that send implies owned.
if !check_send(cx, var_t, sp) { return; } let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id);
if !check_send(cx, var_t, fv.span) { return; }
// copied in data must be copyable, but moved in data can be anything
let is_implicit = fv.is_some();
if !is_move {
check_copy(cx, id, var_t, sp, is_implicit,
Some(("non-copyable value cannot be copied into a \
~fn closure",
"to copy values into a ~fn closure, use a \
capture clause: `fn~(copy x)` or `|copy x|`")));
}
// check that only immutable variables are implicitly copied in // check that only immutable variables are implicitly copied in
for fv.each |fv| { check_imm_free_var(cx, fv.def, fv.span);
check_imm_free_var(cx, fv.def, fv.span);
}
} }
fn check_for_box(cx: ctx, id: node_id, fv: Option<@freevar_entry>, fn check_for_box(cx: ctx, fv: @freevar_entry) {
is_move: bool, var_t: ty::t, sp: span) {
// all captured data must be owned // all captured data must be owned
if !check_durable(cx.tcx, var_t, sp) { return; } let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id);
if !check_durable(cx.tcx, var_t, fv.span) { return; }
// copied in data must be copyable, but moved in data can be anything
let is_implicit = fv.is_some();
if !is_move {
check_copy(cx, id, var_t, sp, is_implicit,
Some(("non-copyable value cannot be copied into a \
@fn closure",
"to copy values into a @fn closure, use a \
capture clause: `fn~(copy x)` or `|copy x|`")));
}
// check that only immutable variables are implicitly copied in // check that only immutable variables are implicitly copied in
for fv.each |fv| { check_imm_free_var(cx, fv.def, fv.span);
check_imm_free_var(cx, fv.def, fv.span);
}
} }
fn check_for_block(cx: ctx, _id: node_id, fv: Option<@freevar_entry>, fn check_for_block(_cx: ctx, _fv: @freevar_entry) {
_is_move: bool, _var_t: ty::t, sp: span) { // no restrictions
// only restriction: no capture clauses (we would have to take
// ownership of the moved/copied in data).
if fv.is_none() {
cx.tcx.sess.span_err(
sp,
~"cannot capture values explicitly with a block closure");
}
} }
fn check_for_bare(cx: ctx, _id: node_id, _fv: Option<@freevar_entry>, fn check_for_bare(cx: ctx, fv: @freevar_entry) {
_is_move: bool, _var_t: ty::t, sp: span) { cx.tcx.sess.span_err(
cx.tcx.sess.span_err(sp, ~"attempted dynamic environment capture"); fv.span,
~"attempted dynamic environment capture");
} }
let fty = ty::node_id_to_type(cx.tcx, id); let fty = ty::node_id_to_type(cx.tcx, id);
@ -197,68 +160,26 @@ pub fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
// Check that the free variables used in a shared/sendable closure conform // Check that the free variables used in a shared/sendable closure conform
// to the copy/move kind bounds. Then recursively check the function body. // to the copy/move kind bounds. Then recursively check the function body.
pub fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span, fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
fn_id: node_id, cx: ctx, v: visit::vt<ctx>) { fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {
// Find the check function that enforces the appropriate bounds for this
// kind of function: // Check kinds on free variables:
do with_appropriate_checker(cx, fn_id) |chk| { do with_appropriate_checker(cx, fn_id) |chk| {
// Begin by checking the variables in the capture clause, if any.
// Here we slightly abuse the map function to both check and report
// errors and produce a list of the def id's for all capture
// variables. This list is used below to avoid checking and reporting
// on a given variable twice.
let cap_clause = match fk {
visit::fk_anon(_, cc) | visit::fk_fn_block(cc) => cc,
visit::fk_item_fn(*) | visit::fk_method(*) |
visit::fk_dtor(*) => @~[]
};
let captured_vars = do (*cap_clause).map |cap_item| {
let cap_def = cx.tcx.def_map.get(cap_item.id);
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
chk(cx, fn_id, None, cap_item.is_move, ty, cap_item.span);
cap_def_id
};
// Iterate over any free variables that may not have appeared in the
// capture list. Ensure that they too are of the appropriate kind.
for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) |fv| { for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) |fv| {
let id = ast_util::def_id_of_def(fv.def).node; chk(cx, *fv);
// skip over free variables that appear in the cap clause
if captured_vars.contains(&id) { loop; }
let ty = ty::node_id_to_type(cx.tcx, id);
// is_move is true if this type implicitly moves and false
// otherwise.
let is_move = ty::type_implicitly_moves(cx.tcx, ty);
chk(cx, fn_id, Some(*fv), is_move, ty, fv.span);
} }
} }
visit::visit_fn(fk, decl, body, sp, fn_id, cx, v); visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
} }
pub fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) { fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) {
match b.node.expr {
Some(ex) => maybe_copy(cx, ex,
Some(("Tail expressions in blocks must be copyable",
try_adding))),
_ => ()
}
visit::visit_block(b, cx, v);
}
pub fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) {
for vec::each(a.pats) |p| { for vec::each(a.pats) |p| {
do pat_util::pat_bindings(cx.tcx.def_map, *p) |mode, id, span, _pth| { do pat_util::pat_bindings(cx.tcx.def_map, *p) |mode, id, span, _pth| {
if mode == bind_by_value { if mode == bind_by_copy {
let t = ty::node_id_to_type(cx.tcx, id); let t = ty::node_id_to_type(cx.tcx, id);
let reason = "consider binding with `ref` or `move` instead"; let reason = "consider binding with `ref` or `move` instead";
check_copy(cx, id, t, span, false, Some((reason,reason))); check_copy(cx, t, span, reason);
} }
} }
} }
@ -267,14 +188,14 @@ pub fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) {
pub fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) { pub fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
debug!("kind::check_expr(%s)", expr_to_str(e, cx.tcx.sess.intr())); debug!("kind::check_expr(%s)", expr_to_str(e, cx.tcx.sess.intr()));
let id_to_use = match e.node {
// Handle any kind bounds on type parameters
let type_parameter_id = match e.node {
expr_index(*)|expr_assign_op(*)| expr_index(*)|expr_assign_op(*)|
expr_unary(*)|expr_binary(*)|expr_method_call(*) => e.callee_id, expr_unary(*)|expr_binary(*)|expr_method_call(*) => e.callee_id,
_ => e.id _ => e.id
}; };
do option::iter(&cx.tcx.node_type_substs.find(type_parameter_id)) |ts| {
// Handle any kind bounds on type parameters
do option::iter(&cx.tcx.node_type_substs.find(id_to_use)) |ts| {
let bounds = match e.node { let bounds = match e.node {
expr_path(_) => { expr_path(_) => {
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id)); let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
@ -299,137 +220,76 @@ pub fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
*bounds, (*bounds).len()); *bounds, (*bounds).len());
} }
for vec::each2(*ts, *bounds) |ty, bound| { for vec::each2(*ts, *bounds) |ty, bound| {
check_bounds(cx, id_to_use, e.span, *ty, *bound) check_bounds(cx, type_parameter_id, e.span, *ty, *bound)
} }
} }
match /*bad*/copy e.node { match e.node {
expr_assign(_, ex) | expr_cast(source, _) => {
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) | check_cast_for_escaping_regions(cx, source, e);
expr_ret(Some(ex)) => { check_kind_bounds_of_cast(cx, source, e);
maybe_copy(cx, ex, Some(("returned values must be copyable", }
try_adding))); expr_copy(expr) => {
} // Note: This is the only place where we must check whether the
expr_cast(source, _) => { // argument is copyable. This is not because this is the only
maybe_copy(cx, source, Some(("casted values must be copyable", // kind of expression that may copy things, but rather because all
try_adding))); // other copies will have been converted to moves by by the
check_cast_for_escaping_regions(cx, source, e); // `moves` pass if the value is not copyable.
check_kind_bounds_of_cast(cx, source, e); check_copy(cx,
} ty::expr_ty(cx.tcx, expr),
expr_copy(expr) => check_copy_ex(cx, expr, false, expr.span,
Some(("explicit copy requires a copyable argument", ""))), "explicit copy requires a copyable argument");
// Vector add copies, but not "implicitly" }
expr_assign_op(_, _, ex) => check_copy_ex(cx, ex, false, expr_rec(ref fields, def) | expr_struct(_, ref fields, def) => {
Some(("assignment with operation requires \ match def {
a copyable argument", ""))), Some(ex) => {
expr_binary(add, ls, rs) => { // All noncopyable fields must be overridden
let reason = Some(("binary operators require copyable arguments", let t = ty::expr_ty(cx.tcx, ex);
"")); let ty_fields = match ty::get(t).sty {
check_copy_ex(cx, ls, false, reason); ty::ty_rec(ref f) => {
check_copy_ex(cx, rs, false, reason); copy *f
} }
expr_rec(ref fields, def) | expr_struct(_, ref fields, def) => { ty::ty_struct(did, ref substs) => {
for (*fields).each |field| { maybe_copy(cx, field.node.expr, ty::struct_fields(cx.tcx, did, substs)
Some(("record or struct fields require \ }
copyable arguments", ""))); } _ => {
match def { cx.tcx.sess.span_bug(
Some(ex) => { ex.span,
// All noncopyable fields must be overridden ~"bad base expr type in record")
let t = ty::expr_ty(cx.tcx, ex); }
let ty_fields = match /*bad*/copy ty::get(t).sty { };
ty::ty_rec(f) => f, for ty_fields.each |tf| {
ty::ty_struct(did, ref substs) => // If this field would not be copied, ok.
ty::struct_fields(cx.tcx, did, &(*substs)), if fields.any(|f| f.node.ident == tf.ident) { loop; }
_ => cx.tcx.sess.span_bug(ex.span,
~"bad base expr type in record") // If this field is copyable, ok.
}; let kind = ty::type_kind(cx.tcx, tf.mt.ty);
for ty_fields.each |tf| { if ty::kind_can_be_copied(kind) { loop; }
if !vec::any((*fields), |f| f.node.ident == tf.ident ) &&
!ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) { cx.tcx.sess.span_err(
cx.tcx.sess.span_err(e.span, e.span,
~"copying a noncopyable value"); fmt!("cannot copy field `%s` of base expression, \
which has a noncopyable type",
*cx.tcx.sess.intr().get(tf.ident)));
}
} }
} _ => {}
}
_ => {}
}
}
expr_tup(exprs) | expr_vec(exprs, _) => {
for exprs.each |expr| { maybe_copy(cx, *expr,
Some(("tuple or vec elements must be copyable", ""))); }
}
expr_call(f, args, _) => {
for ty::ty_fn_args(ty::expr_ty(cx.tcx, f)).eachi |i, arg_t| {
match ty::arg_mode(cx.tcx, *arg_t) {
by_copy => maybe_copy(cx, args[i],
Some(("function arguments must be copyable",
"try changing the function to take a reference \
instead"))),
by_ref | by_val | by_move => ()
} }
} }
} expr_repeat(element, count_expr, _) => {
expr_method_call(_, _, _, args, _) => { let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span);
for ty::ty_fn_args(ty::node_id_to_type(cx.tcx, e.callee_id)).eachi if count > 1 {
|i, arg_t| { let element_ty = ty::expr_ty(cx.tcx, element);
match ty::arg_mode(cx.tcx, *arg_t) { check_copy(cx, element_ty, element.span,
by_copy => maybe_copy(cx, args[i], "repeated element will be copied");
Some(("function arguments must be copyable",
"try changing the function to take a \
reference instead"))),
by_ref | by_val | by_move => ()
} }
} }
} _ => {}
expr_field(lhs, _, _) => {
// If this is a method call with a by-val argument, we need
// to check the copy
match cx.method_map.find(e.id) {
Some(ref mme) => {
match ty::arg_mode(cx.tcx, mme.self_arg) {
by_copy => maybe_copy(cx, lhs,
Some(("method call takes its self argument by copy",
""))),
by_ref | by_val | by_move => ()
}
}
_ => ()
}
}
expr_repeat(element, count_expr, _) => {
let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span);
if count == 1 {
maybe_copy(cx, element, Some(("trivial repeat takes its element \
by copy", "")));
} else {
let element_ty = ty::expr_ty(cx.tcx, element);
check_copy(cx, element.id, element_ty, element.span, true,
Some(("repeat takes its elements by copy", "")));
}
}
_ => { }
} }
visit::visit_expr(e, cx, v); visit::visit_expr(e, cx, v);
} }
pub fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) { fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) {
match stmt.node {
stmt_decl(@spanned {node: decl_local(ref locals), _}, _) => {
for locals.each |local| {
match local.node.init {
Some(expr) =>
maybe_copy(cx, expr, Some(("initializer statement \
takes its right-hand side by copy", ""))),
_ => {}
}
}
}
_ => {}
}
visit::visit_stmt(stmt, cx, v);
}
pub fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) {
match aty.node { match aty.node {
ty_path(_, id) => { ty_path(_, id) => {
do option::iter(&cx.tcx.node_type_substs.find(id)) |ts| { do option::iter(&cx.tcx.node_type_substs.find(id)) |ts| {
@ -471,11 +331,7 @@ pub fn check_bounds(cx: ctx, id: node_id, sp: span,
} }
} }
pub fn maybe_copy(cx: ctx, ex: @expr, why: Option<(&str,&str)>) { fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
check_copy_ex(cx, ex, true, why);
}
pub fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
match ex.node { match ex.node {
expr_path(_) => { expr_path(_) => {
match cx.tcx.def_map.get(ex.id) { match cx.tcx.def_map.get(ex.id) {
@ -489,75 +345,31 @@ pub fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
} }
} }
pub fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool, fn check_imm_free_var(cx: ctx, def: def, sp: span) {
why: Option<(&str,&str)>) { match def {
if ty::expr_is_lval(cx.tcx, cx.method_map, ex) && def_local(_, is_mutbl) => {
if is_mutbl {
// a reference to a constant like `none`... no need to warn cx.tcx.sess.span_err(
// about *this* even if the type is Option<~int> sp,
!is_nullary_variant(cx, ex) && ~"mutable variables cannot be implicitly captured");
// borrowed unique value isn't really a copy
!is_autorefd(cx, ex)
{
match cx.tcx.value_modes.find(ex.id) {
None => cx.tcx.sess.span_bug(ex.span, ~"no value mode for lval"),
Some(MoveValue) | Some(ReadValue) => {} // Won't be a copy.
Some(CopyValue) => {
debug!("(kind checking) is a copy value: `%s`",
expr_to_str(ex, cx.tcx.sess.intr()));
let ty = ty::expr_ty(cx.tcx, ex);
check_copy(cx, ex.id, ty, ex.span, implicit_copy, why);
} }
} }
} def_arg(*) => { /* ok */ }
def_upvar(_, def1, _, _) => { check_imm_free_var(cx, *def1, sp); }
fn is_autorefd(cx: ctx, ex: @expr) -> bool { def_binding(*) | def_self(*) => { /*ok*/ }
match cx.tcx.adjustments.find(ex.id) { _ => {
None => false, cx.tcx.sess.span_bug(
Some(ref adj) => adj.autoref.is_some() sp,
fmt!("unknown def for free variable: %?", def));
} }
} }
} }
pub fn check_imm_free_var(cx: ctx, def: def, sp: span) { fn check_copy(cx: ctx, ty: ty::t, sp: span, reason: &str) {
let msg = ~"mutable variables cannot be implicitly captured; \
use a capture clause";
match def {
def_local(_, is_mutbl) => {
if is_mutbl {
cx.tcx.sess.span_err(sp, msg);
}
}
def_arg(*) => { /* ok */ }
def_upvar(_, def1, _, _) => {
check_imm_free_var(cx, *def1, sp);
}
def_binding(*) | def_self(*) => { /*ok*/ }
_ => {
cx.tcx.sess.span_bug(
sp,
fmt!("unknown def for free variable: %?", def));
}
}
}
pub fn check_copy(cx: ctx, id: node_id, ty: ty::t, sp: span,
implicit_copy: bool, why: Option<(&str,&str)>) {
let k = ty::type_kind(cx.tcx, ty); let k = ty::type_kind(cx.tcx, ty);
if !ty::kind_can_be_copied(k) { if !ty::kind_can_be_copied(k) {
cx.tcx.sess.span_err(sp, ~"copying a noncopyable value"); cx.tcx.sess.span_err(sp, ~"copying a noncopyable value");
do why.map |reason| { cx.tcx.sess.span_note(sp, fmt!("%s", reason));
cx.tcx.sess.span_note(sp, fmt!("%s", reason.first()));
};
} else if implicit_copy && !ty::kind_can_be_implicitly_copied(k) {
cx.tcx.sess.span_lint(
implicit_copies, id, cx.current_item,
sp,
~"implicitly copying a non-implicitly-copyable value");
do why.map |reason| {
cx.tcx.sess.span_note(sp, fmt!("%s", reason.second()));
};
} }
} }

View file

@ -352,7 +352,7 @@ impl LanguageItemCollector {
return; // Didn't match. return; // Didn't match.
} }
match self.item_refs.find(value) { match self.item_refs.find(/*bad*/copy value) {
None => { None => {
// Didn't match. // Didn't match.
} }

View file

@ -344,7 +344,7 @@ impl ctxt {
for triples.each |pair| { for triples.each |pair| {
let (meta, level, lintname) = /*bad*/copy *pair; let (meta, level, lintname) = /*bad*/copy *pair;
match self.dict.find(lintname) { match self.dict.find(/*bad*/ copy lintname) {
None => { None => {
self.span_lint( self.span_lint(
new_ctxt.get_level(unrecognized_lint), new_ctxt.get_level(unrecognized_lint),
@ -518,9 +518,9 @@ fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr, fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr,
r: &ast::expr) -> bool { r: &ast::expr) -> bool {
let (lit, expr, swap) = match (l.node, r.node) { let (lit, expr, swap) = match (&l.node, &r.node) {
(ast::expr_lit(_), _) => (l, r, true), (&ast::expr_lit(_), _) => (l, r, true),
(_, ast::expr_lit(_)) => (r, l, false), (_, &ast::expr_lit(_)) => (r, l, false),
_ => return true _ => return true
}; };
// Normalize the binop so that the literal is always on the RHS in // Normalize the binop so that the literal is always on the RHS in

View file

@ -105,12 +105,11 @@
use core::prelude::*; use core::prelude::*;
use middle::capture::{cap_move, cap_drop, cap_copy, cap_ref};
use middle::capture;
use middle::pat_util; use middle::pat_util;
use middle::ty::MoveValue;
use middle::ty; use middle::ty;
use middle::typeck; use middle::typeck;
use middle::moves;
use util::ppaux::ty_to_str;
use core::cmp; use core::cmp;
use core::dvec::DVec; use core::dvec::DVec;
@ -203,6 +202,8 @@ fn live_node_kind_to_str(lnk: LiveNodeKind, cx: ty::ctxt) -> ~str {
pub fn check_crate(tcx: ty::ctxt, pub fn check_crate(tcx: ty::ctxt,
method_map: typeck::method_map, method_map: typeck::method_map,
variable_moves_map: moves::VariableMovesMap,
capture_map: moves::CaptureMap,
crate: @crate) -> last_use_map { crate: @crate) -> last_use_map {
let visitor = visit::mk_vt(@visit::Visitor { let visitor = visit::mk_vt(@visit::Visitor {
visit_fn: visit_fn, visit_fn: visit_fn,
@ -213,7 +214,8 @@ pub fn check_crate(tcx: ty::ctxt,
}); });
let last_use_map = HashMap(); let last_use_map = HashMap();
let initial_maps = @IrMaps(tcx, method_map, last_use_map); let initial_maps = @IrMaps(tcx, method_map, variable_moves_map,
capture_map, last_use_map);
visit::visit_crate(*crate, initial_maps, visitor); visit::visit_crate(*crate, initial_maps, visitor);
tcx.sess.abort_if_errors(); tcx.sess.abort_if_errors();
return last_use_map; return last_use_map;
@ -294,28 +296,35 @@ fn relevant_def(def: def) -> Option<node_id> {
struct IrMaps { struct IrMaps {
tcx: ty::ctxt, tcx: ty::ctxt,
method_map: typeck::method_map, method_map: typeck::method_map,
variable_moves_map: moves::VariableMovesMap,
capture_map: moves::CaptureMap,
last_use_map: last_use_map, last_use_map: last_use_map,
mut num_live_nodes: uint, mut num_live_nodes: uint,
mut num_vars: uint, mut num_vars: uint,
live_node_map: HashMap<node_id, LiveNode>, live_node_map: HashMap<node_id, LiveNode>,
variable_map: HashMap<node_id, Variable>, variable_map: HashMap<node_id, Variable>,
capture_map: HashMap<node_id, @~[CaptureInfo]>, capture_info_map: HashMap<node_id, @~[CaptureInfo]>,
mut var_kinds: ~[VarKind], mut var_kinds: ~[VarKind],
mut lnks: ~[LiveNodeKind], mut lnks: ~[LiveNodeKind],
} }
fn IrMaps(tcx: ty::ctxt, method_map: typeck::method_map, fn IrMaps(tcx: ty::ctxt,
method_map: typeck::method_map,
variable_moves_map: moves::VariableMovesMap,
capture_map: moves::CaptureMap,
last_use_map: last_use_map) -> IrMaps { last_use_map: last_use_map) -> IrMaps {
IrMaps { IrMaps {
tcx: tcx, tcx: tcx,
method_map: method_map, method_map: method_map,
variable_moves_map: variable_moves_map,
capture_map: capture_map,
last_use_map: last_use_map, last_use_map: last_use_map,
num_live_nodes: 0, num_live_nodes: 0,
num_vars: 0, num_vars: 0,
live_node_map: HashMap(), live_node_map: HashMap(),
variable_map: HashMap(), variable_map: HashMap(),
capture_map: HashMap(), capture_info_map: HashMap(),
var_kinds: ~[], var_kinds: ~[],
lnks: ~[] lnks: ~[]
} }
@ -377,11 +386,11 @@ impl IrMaps {
} }
fn set_captures(node_id: node_id, +cs: ~[CaptureInfo]) { fn set_captures(node_id: node_id, +cs: ~[CaptureInfo]) {
self.capture_map.insert(node_id, @cs); self.capture_info_map.insert(node_id, @cs);
} }
fn captures(expr: @expr) -> @~[CaptureInfo] { fn captures(expr: @expr) -> @~[CaptureInfo] {
match self.capture_map.find(expr.id) { match self.capture_info_map.find(expr.id) {
Some(caps) => caps, Some(caps) => caps,
None => { None => {
self.tcx.sess.span_bug(expr.span, ~"no registered caps"); self.tcx.sess.span_bug(expr.span, ~"no registered caps");
@ -397,7 +406,6 @@ impl IrMaps {
let vk = self.var_kinds[*var]; let vk = self.var_kinds[*var];
debug!("Node %d is a last use of variable %?", expr_id, vk); debug!("Node %d is a last use of variable %?", expr_id, vk);
match vk { match vk {
Arg(id, _, by_move) |
Arg(id, _, by_copy) | Arg(id, _, by_copy) |
Local(LocalInfo {id: id, kind: FromLetNoInitializer, _}) | Local(LocalInfo {id: id, kind: FromLetNoInitializer, _}) |
Local(LocalInfo {id: id, kind: FromLetWithInitializer, _}) | Local(LocalInfo {id: id, kind: FromLetWithInitializer, _}) |
@ -427,7 +435,10 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
let _i = ::util::common::indenter(); let _i = ::util::common::indenter();
// swap in a new set of IR maps for this function body: // swap in a new set of IR maps for this function body:
let fn_maps = @IrMaps(self.tcx, self.method_map, let fn_maps = @IrMaps(self.tcx,
self.method_map,
self.variable_moves_map,
self.capture_map,
self.last_use_map); self.last_use_map);
debug!("creating fn_maps: %x", ptr::addr_of(&(*fn_maps)) as uint); debug!("creating fn_maps: %x", ptr::addr_of(&(*fn_maps)) as uint);
@ -548,8 +559,8 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
} }
visit::visit_expr(expr, self, vt); visit::visit_expr(expr, self, vt);
} }
expr_fn(_, _, _, cap_clause) | expr_fn(_, _, _) |
expr_fn_block(_, _, cap_clause) => { expr_fn_block(_, _) => {
// Interesting control flow (for loops can contain labeled // Interesting control flow (for loops can contain labeled
// breaks or continues) // breaks or continues)
self.add_live_node_for_node(expr.id, ExprNode(expr.span)); self.add_live_node_for_node(expr.id, ExprNode(expr.span));
@ -558,17 +569,18 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
// being the location that the variable is used. This results // being the location that the variable is used. This results
// in better error messages than just pointing at the closure // in better error messages than just pointing at the closure
// construction site. // construction site.
let proto = ty::ty_fn_proto(ty::expr_ty(self.tcx, expr)); let cvs = self.capture_map.get(expr.id);
let cvs = capture::compute_capture_vars(self.tcx, expr.id, proto,
cap_clause);
let mut call_caps = ~[]; let mut call_caps = ~[];
for cvs.each |cv| { for cvs.each |cv| {
match relevant_def(cv.def) { match relevant_def(cv.def) {
Some(rv) => { Some(rv) => {
let cv_ln = self.add_live_node(FreeVarNode(cv.span)); let cv_ln = self.add_live_node(FreeVarNode(cv.span));
let is_move = match cv.mode { let is_move = match cv.mode {
cap_move | cap_drop => true, // var must be dead afterwards // var must be dead afterwards
cap_copy | cap_ref => false // var can still be used moves::CapMove => true,
// var can stil be used
moves::CapCopy | moves::CapRef => false
}; };
call_caps.push(CaptureInfo {ln: cv_ln, call_caps.push(CaptureInfo {ln: cv_ln,
is_move: is_move, is_move: is_move,
@ -600,7 +612,7 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | expr_loop_body(*) | expr_do_body(*) | expr_cast(*) |
expr_unary(*) | expr_fail(*) | expr_unary(*) | expr_fail(*) |
expr_break(_) | expr_again(_) | expr_lit(_) | expr_ret(*) | expr_break(_) | expr_again(_) | expr_lit(_) | expr_ret(*) |
expr_block(*) | expr_unary_move(*) | expr_assign(*) | expr_block(*) | expr_assign(*) |
expr_swap(*) | expr_assign_op(*) | expr_mac(*) | expr_struct(*) | expr_swap(*) | expr_assign_op(*) | expr_mac(*) | expr_struct(*) |
expr_repeat(*) | expr_paren(*) => { expr_repeat(*) | expr_paren(*) => {
visit::visit_expr(expr, self, vt); visit::visit_expr(expr, self, vt);
@ -700,7 +712,7 @@ impl Liveness {
} }
fn variable(node_id: node_id, span: span) -> Variable { fn variable(node_id: node_id, span: span) -> Variable {
(*self.ir).variable(node_id, span) self.ir.variable(node_id, span)
} }
fn variable_from_def_map(node_id: node_id, fn variable_from_def_map(node_id: node_id,
@ -760,7 +772,7 @@ impl Liveness {
assert ln.is_valid(); assert ln.is_valid();
let reader = self.users[self.idx(ln, var)].reader; let reader = self.users[self.idx(ln, var)].reader;
if reader.is_valid() {Some((*self.ir).lnk(reader))} else {None} if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
} }
/* /*
@ -782,7 +794,7 @@ impl Liveness {
assert ln.is_valid(); assert ln.is_valid();
let writer = self.users[self.idx(ln, var)].writer; let writer = self.users[self.idx(ln, var)].writer;
if writer.is_valid() {Some((*self.ir).lnk(writer))} else {None} if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
} }
fn assigned_on_exit(ln: LiveNode, var: Variable) fn assigned_on_exit(ln: LiveNode, var: Variable)
@ -980,21 +992,22 @@ impl Liveness {
// inputs passed by & mode should be considered live on exit: // inputs passed by & mode should be considered live on exit:
for decl.inputs.each |arg| { for decl.inputs.each |arg| {
match ty::resolved_mode(self.tcx, arg.mode) { match ty::resolved_mode(self.tcx, arg.mode) {
by_ref | by_val => { by_val | by_ref => {
// These are "non-owned" modes, so register a read at // By val and by ref do not own, so register a
// the end. This will prevent us from moving out of // read at the end. This will prevent us from
// such variables but also prevent us from registering // moving out of such variables but also prevent
// last uses and so forth. // us from registering last uses and so forth.
do pat_util::pat_bindings(self.tcx.def_map, arg.pat) do pat_util::pat_bindings(self.tcx.def_map, arg.pat)
|_bm, arg_id, _sp, _path| { |_bm, arg_id, _sp, _path|
let var = self.variable(arg_id, blk.span); {
self.acc(self.s.exit_ln, var, ACC_READ); let var = self.variable(arg_id, blk.span);
self.acc(self.s.exit_ln, var, ACC_READ);
}
}
by_copy => {
// By copy is an owned mode. If we don't use the
// variable, nobody will.
} }
}
by_move | by_copy => {
// These are owned modes. If we don't use the
// variable, nobody will.
}
} }
} }
@ -1092,7 +1105,7 @@ impl Liveness {
self.propagate_through_expr(e, succ) self.propagate_through_expr(e, succ)
} }
expr_fn(_, _, ref blk, _) | expr_fn_block(_, ref blk, _) => { expr_fn(_, _, ref blk) | expr_fn_block(_, ref blk) => {
debug!("%s is an expr_fn or expr_fn_block", debug!("%s is an expr_fn or expr_fn_block",
expr_to_str(expr, self.tcx.sess.intr())); expr_to_str(expr, self.tcx.sess.intr()));
@ -1105,8 +1118,8 @@ impl Liveness {
// the construction of a closure itself is not important, // the construction of a closure itself is not important,
// but we have to consider the closed over variables. // but we have to consider the closed over variables.
let caps = (*self.ir).captures(expr); let caps = self.ir.captures(expr);
do (*caps).foldr(succ) |cap, succ| { do caps.foldr(succ) |cap, succ| {
self.init_from_succ(cap.ln, succ); self.init_from_succ(cap.ln, succ);
let var = self.variable(cap.var_nid, expr.span); let var = self.variable(cap.var_nid, expr.span);
self.acc(cap.ln, var, ACC_READ | ACC_USE); self.acc(cap.ln, var, ACC_READ | ACC_USE);
@ -1315,7 +1328,6 @@ impl Liveness {
expr_assert(e) | expr_assert(e) |
expr_addr_of(_, e) | expr_addr_of(_, e) |
expr_copy(e) | expr_copy(e) |
expr_unary_move(e) |
expr_loop_body(e) | expr_loop_body(e) |
expr_do_body(e) | expr_do_body(e) |
expr_cast(e, _) | expr_cast(e, _) |
@ -1541,26 +1553,6 @@ fn check_arm(arm: arm, &&self: @Liveness, vt: vt<@Liveness>) {
visit::visit_arm(arm, self, vt); visit::visit_arm(arm, self, vt);
} }
fn check_call(args: &[@expr],
targs: &[ty::arg],
&&self: @Liveness) {
for vec::each2(args, targs) |arg_expr, arg_ty| {
match ty::resolved_mode(self.tcx, arg_ty.mode) {
by_val | by_copy | by_ref => {}
by_move => {
if ty::expr_is_lval(self.tcx, self.ir.method_map, *arg_expr) {
// Probably a bad error message (what's an rvalue?)
// but I can't think of anything better
self.tcx.sess.span_err(arg_expr.span,
fmt!("move mode argument must be an rvalue: try (move \
%s) instead",
expr_to_str(*arg_expr, self.tcx.sess.intr())));
}
}
}
}
}
fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) { fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
match /*bad*/copy expr.node { match /*bad*/copy expr.node {
expr_path(_) => { expr_path(_) => {
@ -1568,19 +1560,12 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
let ln = self.live_node(expr.id, expr.span); let ln = self.live_node(expr.id, expr.span);
self.consider_last_use(expr, ln, *var); self.consider_last_use(expr, ln, *var);
match self.tcx.value_modes.find(expr.id) { match self.ir.variable_moves_map.find(expr.id) {
Some(MoveValue) => { None => {}
Some(entire_expr) => {
debug!("(checking expr) is a move: `%s`", debug!("(checking expr) is a move: `%s`",
expr_to_str(expr, self.tcx.sess.intr())); expr_to_str(expr, self.tcx.sess.intr()));
self.check_move_from_var(expr.span, ln, *var); self.check_move_from_var(ln, *var, entire_expr);
}
Some(v) => {
debug!("(checking expr) not a move (%?): `%s`",
v,
expr_to_str(expr, self.tcx.sess.intr()));
}
None => {
fail ~"no mode for lval";
} }
} }
} }
@ -1589,12 +1574,12 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
} }
expr_fn(*) | expr_fn_block(*) => { expr_fn(*) | expr_fn_block(*) => {
let caps = (*self.ir).captures(expr); let caps = self.ir.captures(expr);
for (*caps).each |cap| { for caps.each |cap| {
let var = self.variable(cap.var_nid, expr.span); let var = self.variable(cap.var_nid, expr.span);
self.consider_last_use(expr, cap.ln, var); self.consider_last_use(expr, cap.ln, var);
if cap.is_move { if cap.is_move {
self.check_move_from_var(expr.span, cap.ln, var); self.check_move_from_var(cap.ln, var, expr);
} }
} }
@ -1608,32 +1593,14 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
visit::visit_expr(expr, self, vt); visit::visit_expr(expr, self, vt);
} }
expr_unary_move(r) => {
self.check_move_from_expr(r, vt);
visit::visit_expr(expr, self, vt);
}
expr_assign_op(_, l, _) => { expr_assign_op(_, l, _) => {
self.check_lvalue(l, vt); self.check_lvalue(l, vt);
visit::visit_expr(expr, self, vt); visit::visit_expr(expr, self, vt);
} }
expr_call(f, args, _) => {
let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f));
check_call(args, targs, self);
visit::visit_expr(expr, self, vt);
}
expr_method_call(_, _, _, args, _) => {
let targs = ty::ty_fn_args(ty::node_id_to_type(self.tcx,
expr.callee_id));
check_call(args, targs, self);
visit::visit_expr(expr, self, vt);
}
// no correctness conditions related to liveness // no correctness conditions related to liveness
expr_call(*) | expr_method_call(*) |
expr_if(*) | expr_match(*) | expr_if(*) | expr_match(*) |
expr_while(*) | expr_loop(*) | expr_while(*) | expr_loop(*) |
expr_index(*) | expr_field(*) | expr_vstore(*) | expr_index(*) | expr_field(*) | expr_vstore(*) |
@ -1659,7 +1626,8 @@ fn check_fn(_fk: visit::fn_kind, _decl: fn_decl,
enum ReadKind { enum ReadKind {
PossiblyUninitializedVariable, PossiblyUninitializedVariable,
PossiblyUninitializedField, PossiblyUninitializedField,
MovedValue MovedValue,
PartiallyMovedValue
} }
impl @Liveness { impl @Liveness {
@ -1683,17 +1651,28 @@ impl @Liveness {
} }
} }
/* fn check_move_from_var(ln: LiveNode,
Checks whether <var> is live on entry to any of the successors of <ln>. var: Variable,
If it is, report an error. move_expr: @expr)
*/ {
fn check_move_from_var(span: span, ln: LiveNode, var: Variable) { /*!
*
* Checks whether `var` is live on entry to any of the
* successors of `ln`. If it is, report an error.
* `move_expr` is the expression which caused the variable
* to be moved.
*
* Note that `move_expr` is not necessarily a reference to the
* variable. It might be an expression like `x.f` which could
* cause a move of the variable `x`, or a closure creation.
*/
debug!("check_move_from_var(%s, %s)", debug!("check_move_from_var(%s, %s)",
ln.to_str(), var.to_str()); ln.to_str(), var.to_str());
match self.live_on_exit(ln, var) { match self.live_on_exit(ln, var) {
None => {} None => {}
Some(lnk) => self.report_illegal_move(span, lnk, var) Some(lnk) => self.report_illegal_move(lnk, var, move_expr)
} }
} }
@ -1703,48 +1682,7 @@ impl @Liveness {
match self.live_on_exit(ln, var) { match self.live_on_exit(ln, var) {
Some(_) => {} Some(_) => {}
None => (*self.ir).add_last_use(expr.id, var) None => self.ir.add_last_use(expr.id, var)
}
}
fn check_move_from_expr(expr: @expr, vt: vt<@Liveness>) {
debug!("check_move_from_expr(node %d: %s)",
expr.id, expr_to_str(expr, self.tcx.sess.intr()));
if self.ir.method_map.contains_key(expr.id) {
// actually an rvalue, since this calls a method
return;
}
match expr.node {
expr_path(_) => {
match self.variable_from_path(expr) {
Some(var) => {
let ln = self.live_node(expr.id, expr.span);
self.check_move_from_var(expr.span, ln, var);
}
None => {}
}
}
expr_field(base, _, _) => {
// Moving from x.y is allowed if x is never used later.
// (Note that the borrowck guarantees that anything
// being moved from is uniquely tied to the stack frame)
self.check_move_from_expr(base, vt);
}
expr_index(base, _) => {
// Moving from x[y] is allowed if x is never used later.
// (Note that the borrowck guarantees that anything
// being moved from is uniquely tied to the stack frame)
self.check_move_from_expr(base, vt);
}
_ => {
// For other kinds of lvalues, no checks are required,
// and any embedded expressions are actually rvalues
}
} }
} }
@ -1808,36 +1746,84 @@ impl @Liveness {
} }
} }
fn report_illegal_move(move_span: span, fn report_illegal_move(lnk: LiveNodeKind,
lnk: LiveNodeKind, var: Variable,
var: Variable) { move_expr: @expr)
{
// the only time that it is possible to have a moved value // the only time that it is possible to have a moved variable
// used by ExitNode would be arguments or fields in a ctor. // used by ExitNode would be arguments or fields in a ctor.
// we give a slightly different error message in those cases. // we give a slightly different error message in those cases.
if lnk == ExitNode { if lnk == ExitNode {
// XXX this seems like it should be reported in the borrow checker
let vk = self.ir.var_kinds[*var]; let vk = self.ir.var_kinds[*var];
match vk { match vk {
Arg(_, name, _) => { Arg(_, name, _) => {
self.tcx.sess.span_err( self.tcx.sess.span_err(
move_span, move_expr.span,
fmt!("illegal move from argument `%s`, which is not \ fmt!("illegal move from argument `%s`, which is not \
copy or move mode", self.tcx.sess.str_of(name))); copy or move mode", self.tcx.sess.str_of(name)));
return; return;
} }
Local(*) | ImplicitRet => { Local(*) | ImplicitRet => {
self.tcx.sess.span_bug( self.tcx.sess.span_bug(
move_span, move_expr.span,
fmt!("illegal reader (%?) for `%?`", fmt!("illegal reader (%?) for `%?`",
lnk, vk)); lnk, vk));
} }
} }
} }
self.report_illegal_read(move_span, lnk, var, MovedValue); match move_expr.node {
self.tcx.sess.span_note( expr_fn(*) | expr_fn_block(*) => {
move_span, ~"move of value occurred here"); self.report_illegal_read(
move_expr.span, lnk, var, MovedValue);
let name = self.ir.variable_name(var);
self.tcx.sess.span_note(
move_expr.span,
fmt!("`%s` moved into closure environment here \
because its type is moved by default",
name));
}
expr_path(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, MovedValue);
self.report_move_location(
move_expr, var, "", "it");
}
expr_field(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr, var, "field of ", "the field");
}
expr_index(*) => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr, var, "element of ", "the element");
}
_ => {
self.report_illegal_read(
move_expr.span, lnk, var, PartiallyMovedValue);
self.report_move_location(
move_expr, var, "subcomponent of ", "the subcomponent");
}
};
}
fn report_move_location(move_expr: @expr,
var: Variable,
expr_descr: &str,
pronoun: &str)
{
let move_expr_ty = ty::expr_ty(self.tcx, move_expr);
let name = self.ir.variable_name(var);
self.tcx.sess.span_note(
move_expr.span,
fmt!("%s`%s` moved here because %s has type %s, \
which is moved by default (use `copy` to override)",
expr_descr, name, pronoun,
ty_to_str(self.tcx, move_expr_ty)));
} }
fn report_illegal_read(chk_span: span, fn report_illegal_read(chk_span: span,
@ -1845,13 +1831,13 @@ impl @Liveness {
var: Variable, var: Variable,
rk: ReadKind) { rk: ReadKind) {
let msg = match rk { let msg = match rk {
PossiblyUninitializedVariable => { PossiblyUninitializedVariable => "possibly uninitialized \
~"possibly uninitialized variable" variable",
} PossiblyUninitializedField => "possibly uninitialized field",
PossiblyUninitializedField => ~"possibly uninitialized field", MovedValue => "moved value",
MovedValue => ~"moved value" PartiallyMovedValue => "partially moved value"
}; };
let name = (*self.ir).variable_name(var); let name = self.ir.variable_name(var);
match lnk { match lnk {
FreeVarNode(span) => { FreeVarNode(span) => {
self.tcx.sess.span_err( self.tcx.sess.span_err(
@ -1863,8 +1849,7 @@ impl @Liveness {
span, span,
fmt!("use of %s: `%s`", msg, name)); fmt!("use of %s: `%s`", msg, name));
} }
ExitNode | ExitNode | VarDefNode(_) => {
VarDefNode(_) => {
self.tcx.sess.span_bug( self.tcx.sess.span_bug(
chk_span, chk_span,
fmt!("illegal reader: %?", lnk)); fmt!("illegal reader: %?", lnk));
@ -1873,7 +1858,7 @@ impl @Liveness {
} }
fn should_warn(var: Variable) -> Option<~str> { fn should_warn(var: Variable) -> Option<~str> {
let name = (*self.ir).variable_name(var); let name = self.ir.variable_name(var);
if name[0] == ('_' as u8) {None} else {Some(name)} if name[0] == ('_' as u8) {None} else {Some(name)}
} }

View file

@ -394,7 +394,7 @@ pub impl &mem_categorization_ctxt {
ast::expr_block(*) | ast::expr_loop(*) | ast::expr_match(*) | ast::expr_block(*) | ast::expr_loop(*) | ast::expr_match(*) |
ast::expr_lit(*) | ast::expr_break(*) | ast::expr_mac(*) | ast::expr_lit(*) | ast::expr_break(*) | ast::expr_mac(*) |
ast::expr_again(*) | ast::expr_rec(*) | ast::expr_struct(*) | ast::expr_again(*) | ast::expr_rec(*) | ast::expr_struct(*) |
ast::expr_unary_move(*) | ast::expr_repeat(*) => { ast::expr_repeat(*) => {
return self.cat_rvalue(expr, expr_ty); return self.cat_rvalue(expr, expr_ty);
} }
} }
@ -430,20 +430,16 @@ pub impl &mem_categorization_ctxt {
// lp: loan path, must be none for aliasable things // lp: loan path, must be none for aliasable things
let m = if mutbl {m_mutbl} else {m_imm}; let m = if mutbl {m_mutbl} else {m_imm};
let lp = match ty::resolved_mode(self.tcx, mode) { let lp = match ty::resolved_mode(self.tcx, mode) {
ast::by_move | ast::by_copy => { ast::by_copy => Some(@lp_arg(vid)),
Some(@lp_arg(vid)) ast::by_ref => None,
} ast::by_val => {
ast::by_ref => { // by-value is this hybrid mode where we have a
None // pointer but we do not own it. This is not
} // considered loanable because, for example, a by-ref
ast::by_val => { // and and by-val argument might both actually contain
// by-value is this hybrid mode where we have a // the same unique ptr.
// pointer but we do not own it. This is not None
// considered loanable because, for example, a by-ref }
// and and by-val argument might both actually contain
// the same unique ptr.
None
}
}; };
@cmt_ { @cmt_ {
id:id, id:id,

View file

@ -1,281 +0,0 @@
// Copyright 2012 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.
use core::prelude::*;
use middle::pat_util;
use middle::ty;
use middle::ty::{CopyValue, MoveValue, ReadValue, ValueMode, ctxt};
use middle::typeck::{method_map, method_map_entry};
use core::vec;
use std::map::HashMap;
use syntax::ast::{bind_infer, box, by_copy, by_move, by_ref, by_val, crate};
use syntax::ast::{deref, expr, expr_addr_of, expr_assign, expr_assign_op};
use syntax::ast::{expr_binary, expr_call, expr_copy, expr_field, expr_index};
use syntax::ast::{expr_match, expr_method_call, expr_paren, expr_path};
use syntax::ast::{expr_swap, expr_unary, neg, node_id, not, pat, pat_ident};
use syntax::ast::{expr_vstore, expr_vec, expr_rec, expr_tup, expr_lit};
use syntax::ast::{expr_cast, expr_if, expr_while, expr_loop, expr_fn};
use syntax::ast::{expr_fn_block, expr_loop_body, expr_do_body, expr_block};
use syntax::ast::{expr_unary_move, expr_fail, expr_break, expr_again};
use syntax::ast::{expr_ret, expr_log, expr_assert, expr_mac, expr_struct};
use syntax::ast::{expr_repeat};
use syntax::ast::{sty_uniq, sty_value, uniq};
use syntax::ast::{fn_decl, blk};
use syntax::visit;
use syntax::visit::{fn_kind, vt};
use syntax::print::pprust;
use syntax::codemap::span;
struct VisitContext {
tcx: ctxt,
method_map: HashMap<node_id,method_map_entry>,
mode: ValueMode,
}
fn compute_modes_for_fn(fk: fn_kind,
decl: fn_decl,
body: blk,
sp: span,
id: node_id,
&&cx: VisitContext,
v: vt<VisitContext>) {
let body_cx = VisitContext { mode: MoveValue, ..cx };
visit::visit_fn(fk, decl, body, sp, id, body_cx, v);
}
fn compute_modes_for_fn_args(callee_id: node_id,
args: &[@expr],
last_arg_is_block: bool,
&&cx: VisitContext,
v: vt<VisitContext>) {
let arg_tys = ty::ty_fn_args(ty::node_id_to_type(cx.tcx, callee_id));
let mut i = 0;
for vec::each2(args, arg_tys) |arg, arg_ty| {
if last_arg_is_block && i == args.len() - 1 {
let block_cx = VisitContext { mode: MoveValue, ..cx };
compute_modes_for_expr(*arg, block_cx, v);
} else {
match ty::resolved_mode(cx.tcx, arg_ty.mode) {
by_ref => {
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(*arg, arg_cx, v);
}
by_val | by_move | by_copy => {
compute_modes_for_expr(*arg, cx, v);
}
}
}
i += 1;
}
}
fn record_mode_for_expr(expr: @expr, &&cx: VisitContext) {
match cx.mode {
ReadValue | CopyValue => {
cx.tcx.value_modes.insert(expr.id, cx.mode);
}
MoveValue => {
// This is, contextually, a move, but if this expression
// is implicitly copyable it's cheaper to copy.
let e_ty = ty::expr_ty(cx.tcx, expr);
if ty::type_implicitly_moves(cx.tcx, e_ty) {
cx.tcx.value_modes.insert(expr.id, MoveValue);
} else {
cx.tcx.value_modes.insert(expr.id, CopyValue);
}
}
}
}
fn compute_modes_for_expr(expr: @expr,
&&cx: VisitContext,
v: vt<VisitContext>) {
debug!("compute_modes_for_expr(expr=%?/%s, mode=%?)",
expr.id, pprust::expr_to_str(expr, cx.tcx.sess.intr()),
cx.mode);
// Adjust the mode if there was an implicit reference here.
let cx = match cx.tcx.adjustments.find(expr.id) {
None => cx,
Some(adjustment) => {
if adjustment.autoref.is_some() {
VisitContext { mode: ReadValue, ..cx }
} else {
cx
}
}
};
match copy expr.node {
expr_call(callee, args, is_block) => {
let callee_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(callee, callee_cx, v);
compute_modes_for_fn_args(callee.id, args, is_block, cx, v);
}
expr_path(*) => {
record_mode_for_expr(expr, cx);
}
expr_copy(expr) => {
let callee_cx = VisitContext { mode: CopyValue, ..cx };
compute_modes_for_expr(expr, callee_cx, v);
}
expr_method_call(callee, _, _, args, is_block) => {
// The LHS of the dot may or may not result in a move, depending
// on the method map entry.
let callee_mode;
match cx.method_map.find(expr.id) {
Some(ref method_map_entry) => {
match method_map_entry.explicit_self {
sty_uniq(_) | sty_value => callee_mode = MoveValue,
_ => callee_mode = ReadValue
}
}
None => {
cx.tcx.sess.span_bug(expr.span, ~"no method map entry");
}
}
let callee_cx = VisitContext { mode: callee_mode, ..cx };
compute_modes_for_expr(callee, callee_cx, v);
compute_modes_for_fn_args(expr.callee_id, args, is_block, cx, v);
}
expr_binary(_, lhs, rhs) | expr_assign_op(_, lhs, rhs) => {
// The signatures of these take their arguments by-ref, so they
// don't copy or move.
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(lhs, arg_cx, v);
compute_modes_for_expr(rhs, arg_cx, v);
}
expr_addr_of(_, arg) => {
// Takes its argument by-ref, so it doesn't copy or move.
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(arg, arg_cx, v);
}
expr_unary(unop, arg) => {
match unop {
deref => {
// Derefs function as reads.
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(arg, arg_cx, v);
// This is an lvalue, so it needs a value mode recorded
// for it.
record_mode_for_expr(expr, cx);
}
box(_) | uniq(_) => {
let arg_cx = VisitContext { mode: MoveValue, ..cx };
compute_modes_for_expr(arg, arg_cx, v);
}
not | neg => {
// Takes its argument by ref.
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(arg, arg_cx, v);
}
}
}
expr_field(arg, _, _) => {
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(arg, arg_cx, v);
record_mode_for_expr(expr, cx);
}
expr_assign(lhs, rhs) => {
// The signatures of these take their arguments by-ref, so they
// don't copy or move.
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(lhs, arg_cx, v);
compute_modes_for_expr(rhs, cx, v);
}
expr_swap(lhs, rhs) => {
let arg_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(lhs, arg_cx, v);
compute_modes_for_expr(rhs, arg_cx, v);
}
expr_index(lhs, rhs) => {
let lhs_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(lhs, lhs_cx, v);
let rhs_cx = VisitContext { mode: MoveValue, ..cx };
compute_modes_for_expr(rhs, rhs_cx, v);
record_mode_for_expr(expr, cx);
}
expr_paren(arg) => {
compute_modes_for_expr(arg, cx, v);
record_mode_for_expr(expr, cx);
}
expr_match(head, ref arms) => {
// We must do this first so that `arms_have_by_move_bindings`
// below knows which bindings are moves.
for arms.each |arm| {
(v.visit_arm)(*arm, cx, v);
}
let by_move_bindings_present =
pat_util::arms_have_by_move_bindings(cx.tcx, *arms);
if by_move_bindings_present {
// Propagate the current mode flag downward.
visit::visit_expr(expr, cx, v);
} else {
// We aren't moving into any pattern, so this is just a read.
let head_cx = VisitContext { mode: ReadValue, ..cx };
compute_modes_for_expr(head, head_cx, v);
}
}
// Spell out every remaining expression so we don't forget to
// update this code if we add a new variant.
// (Maybe a macro to do this would be nice...)
expr_vstore(*) | expr_vec(*) | expr_rec(*) | expr_tup(*) |
expr_lit(*) | expr_cast(*) | expr_if(*) | expr_while(*) |
expr_loop(*) | expr_fn(*) | expr_fn_block(*) |
expr_loop_body(*) | expr_do_body(*) | expr_block(*) |
expr_unary_move(*) | expr_fail(*) | expr_break(*) |
expr_again(*) | expr_ret(*) | expr_log(*) | expr_assert(*) |
expr_mac(*) | expr_struct(*) | expr_repeat(*) => {
visit::visit_expr(expr, cx, v)
}
}
}
fn compute_modes_for_pat(pat: @pat,
&&cx: VisitContext,
v: vt<VisitContext>) {
match pat.node {
pat_ident(bind_infer, _, _)
if pat_util::pat_is_binding(cx.tcx.def_map, pat) => {
if ty::type_implicitly_moves(cx.tcx, ty::pat_ty(cx.tcx, pat)) {
cx.tcx.value_modes.insert(pat.id, MoveValue);
} else {
cx.tcx.value_modes.insert(pat.id, CopyValue);
}
}
_ => {}
}
visit::visit_pat(pat, cx, v);
}
pub fn compute_modes(tcx: ctxt, method_map: method_map, crate: @crate) {
let visitor = visit::mk_vt(@visit::Visitor {
visit_fn: compute_modes_for_fn,
visit_expr: compute_modes_for_expr,
visit_pat: compute_modes_for_pat,
.. *visit::default_visitor()
});
let callee_cx = VisitContext {
tcx: tcx,
method_map: method_map,
mode: MoveValue
};
visit::visit_crate(*crate, callee_cx, visitor);
}

View file

@ -0,0 +1,815 @@
// Copyright 2012 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.
/*!
# Moves Computation
The goal of this file is to compute which
expressions/patterns/captures correspond to *moves*. This is
generally a function of the context in which the expression appears as
well as the expression's type.
## Examples
We will use the following fragment of code to explain the various
considerations. Note that in this code `x` is used after it has been
moved here. This is not relevant to this pass, though the information
we compute would later be used to detect this error (see the section
Enforcement of Moves, below).
struct Foo { a: int, b: ~int }
let x: Foo = ...;
let w = (x {Read}).a; // Read
let y = (x {Move}).b; // Move
let z = copy (x {Read}).b; // Read
Let's look at these examples one by one. In the first case, `w`, the
expression being assigned is `x.a`, which has `int` type. In that
case, the value is read, and the container (`x`) is also read.
In the second case, `y`, `x.b` is being assigned which has type
`~int`. Because this type moves by default, that will be a move
reference. Whenever we move from a compound expression like `x.b` (or
`x[b]` or `*x` or `{x)[b].c`, etc), this invalidates all containing
expressions since we do not currently permit "incomplete" variables
where part of them has been moved and part has not. In this case,
this means that the reference to `x` is also a move. We'll see later,
though, that these kind of "partial moves", where part of the
expression has been moved, are classified and stored somewhat
differently.
The final example (`z`) is `copy x.b`: in this case, although the
expression being assigned has type `~int`, there are no moves
involved.
### Patterns
For each binding in a match or let pattern, we also compute a read
or move designation. A move binding means that the value will be
moved from the value being matched. As a result, the expression
being matched (aka, the 'discriminant') is either moved or read
depending on whethe the bindings move the value they bind to out of
the discriminant.
For examples, consider this match expression:
match x {Move} {
Foo { a: a {Read}, b: b {Move} } => {...}
}
Here, the binding `b` is value (not ref) mode, and `b` has type
`~int`, and therefore the discriminant expression `x` would be
incomplete so it also considered moved.
In the following two examples, in contrast, the mode of `b` is either
`copy` or `ref` and hence the overall result is a read:
match x {Read} {
Foo { a: a {Read}, b: copy b {Read} } => {...}
}
match x {Read} {
Foo { a: a {Read}, b: ref b {Read} } => {...}
}
Similar reasoning can be applied to `let` expressions:
let Foo { a: a {Read}, b: b {Move} } = x {Move};
let Foo { a: a {Read}, b: copy b {Read} } = x {Read};
let Foo { a: a {Read}, b: ref b {Read} } = x {Read};
## Output
The pass results in the struct `MoveMaps` which contains two sets,
`moves_map` and `variable_moves_map`, and one map, `capture_map`.
`moves_map` is a set containing the id of every *outermost
expression* or *binding* that is moved. Note that `moves_map` only
contains the *outermost expressions* that are moved. Therefore, if
you have a use of `x.b`, as in the example `y` above, the
expression `x.b` would be in the `moves_map` but not `x`. The
reason for this is that, for most purposes, it's only the outermost
expression that is needed. The borrow checker and trans, for
example, only care about the outermost expressions that are moved.
It is more efficient therefore just to store those entries.
In the case of the liveness pass, however, we need to know which
*variable references* are moved (see the Enforcement of Moves
section below for more details). That is, for the `x.b`
expression, liveness only cares about the `x`. For this purpose,
we have a second map, `variable_moves_map`, that contains the ids
of all variable references which is moved.
The `capture_map` maps from the node_id of a closure expression to an
array of `CaptureVar` structs detailing which variables are captured
and how (by ref, by copy, by move).
## Enforcement of Moves
The enforcement of moves is somewhat complicated because it is divided
amongst the liveness and borrowck modules. In general, the borrow
checker is responsible for guaranteeing that *only owned data is
moved*. The liveness checker, in contrast, is responsible for
checking that *no variable is used after it is moved*.
To see the difference, let's look at a few examples. Here is a
program fragment where the error would be caught by liveness:
struct Foo { a: int, b: ~int }
let x: Foo = ...;
let y = x.b; // (1)
let z = x; // (2) //~ ERROR use of moved value `x`
Here the liveness checker will see the assignment to `y` moves
invalidates the variable `x` because it moves the expression `x.b`.
An error is resported because `x` is not dead at the point where it is
invalidated.
In more concrete terms, the `moves_map` generated from this example
would contain both the expression `x.b` (1) and the expression `x`
(2). Note that it would not contain `x` (1), because `moves_map` only
contains the outermost expressions that are moved. However,
`moves_map` is not used by liveness. It uses the
`variable_moves_map`, which would contain both references to `x`: (1)
and (2). Therefore, after computing which variables are live where,
liveness will see that the reference (1) to `x` is both present in
`variable_moves_map` and that `x` is live and report an error.
Now let's look at another illegal example, but one where liveness would
not catch the error:
struct Foo { a: int, b: ~int }
let x: @Foo = ...;
let y = x.b; //~ ERROR move from managed (@) box
This is an interesting example because the only change I've made is
to make `x` have type `@Foo` and not `Foo`. Thanks to auto-deref,
the expression `x.b` still works, but now it is short for `{x).b`,
and hence the move is actually moving out of the contents of a
managed box, which is illegal. However, liveness knows nothing of
this. It only tracks what variables are used where. The moves
pass (that is, this pass) is also ignorant of such details. From
the perspective of the moves pass, the `let y = x.b` line above
will be categorized as follows:
let y = {(x{Move}) {Move}).b; {Move}
Therefore, the reference to `x` will be present in
`variable_moves_map`, but liveness will not report an error because
there is no subsequent use.
This is where the borrow checker comes in. When the borrow checker
runs, it will see that `x.b` is present in the `moves_map`. It will
use the `mem_categorization` module to determine where the result of
this expression resides in memory and see that it is owned by managed
data, and report an error.
In principle, liveness could use the `mem_categorization` module
itself and check that moves always originate from owned data
(historically, of course, this was not the case; `mem_categorization`
used to be private to the borrow checker). However, there is another
kind of error which liveness could not possibly detect. Sometimes a
move is an error due to an outstanding loan, and it is borrow
checker's job to compute those loans. That is, consider *this*
example:
struct Foo { a: int, b: ~int }
let x: Foo = ...;
let y = &x.b; //~ NOTE loan issued here
let z = x.b; //~ ERROR move with outstanding loan
In this case, `y` is a pointer into `x`, so when `z` tries to move out
of `x`, we get an error. There is no way that liveness could compute
this information without redoing the efforts of the borrow checker.
### Closures
Liveness is somewhat complicated by having to deal with stack
closures. More information to come!
## Distributive property
Copies are "distributive" over parenthesization, but blocks are
considered rvalues. What this means is that, for example, neither
`a.clone()` nor `(a).clone()` will move `a` (presuming that `a` has a
linear type and `clone()` takes its self by reference), but
`{a}.clone()` will move `a`, as would `(if cond {a} else {b}).clone()`
and so on.
*/
use core::prelude::*;
use middle::pat_util::{pat_bindings};
use middle::freevars;
use middle::ty;
use middle::typeck::{method_map, method_map_entry};
use middle::typeck::check::{DerefArgs, DoDerefArgs, DontDerefArgs};
use util::ppaux;
use util::common::indenter;
use core::vec;
use std::map::HashMap;
use syntax::ast::*;
use syntax::ast_util;
use syntax::visit;
use syntax::visit::{fn_kind, fk_item_fn, fk_method, fk_dtor,
fk_anon, fk_fn_block, vt};
use syntax::print::pprust;
use syntax::codemap::span;
#[auto_encode]
#[auto_decode]
pub enum CaptureMode {
CapCopy, // Copy the value into the closure.
CapMove, // Move the value into the closure.
CapRef, // Reference directly from parent stack frame (used by `&fn()`).
}
#[auto_encode]
#[auto_decode]
pub struct CaptureVar {
def: def, // Variable being accessed free
span: span, // Location of an access to this variable
mode: CaptureMode // How variable is being accessed
}
pub type CaptureMap = HashMap<node_id, @[CaptureVar]>;
pub type MovesMap = HashMap<node_id, ()>;
/**
* For each variable which will be moved, links to the
* expression */
pub type VariableMovesMap = HashMap<node_id, @expr>;
/** See the section Output on the module comment for explanation. */
pub struct MoveMaps {
moves_map: MovesMap,
variable_moves_map: VariableMovesMap,
capture_map: CaptureMap
}
struct VisitContext {
tcx: ty::ctxt,
method_map: HashMap<node_id,method_map_entry>,
move_maps: MoveMaps
}
enum UseMode {
MoveInWhole, // Move the entire value.
MoveInPart(@expr), // Some subcomponent will be moved
Read // Read no matter what the type.
}
pub fn compute_moves(tcx: ty::ctxt,
method_map: method_map,
crate: @crate) -> MoveMaps
{
let visitor = visit::mk_vt(@visit::Visitor {
visit_expr: compute_modes_for_expr,
.. *visit::default_visitor()
});
let visit_cx = VisitContext {
tcx: tcx,
method_map: method_map,
move_maps: MoveMaps {
moves_map: HashMap(),
variable_moves_map: HashMap(),
capture_map: HashMap()
}
};
visit::visit_crate(*crate, visit_cx, visitor);
return visit_cx.move_maps;
}
// ______________________________________________________________________
// Expressions
fn compute_modes_for_expr(expr: @expr,
&&cx: VisitContext,
v: vt<VisitContext>)
{
cx.consume_expr(expr, v);
}
impl UseMode {
fn component_mode(&self, expr: @expr) -> UseMode {
/*!
*
* Assuming that `self` is the mode for an expression E,
* returns the appropriate mode to use for a subexpression of E.
*/
match *self {
Read | MoveInPart(_) => *self,
MoveInWhole => MoveInPart(expr)
}
}
}
impl VisitContext {
fn consume_exprs(&self,
exprs: &[@expr],
visitor: vt<VisitContext>)
{
for exprs.each |expr| {
self.consume_expr(*expr, visitor);
}
}
fn consume_expr(&self,
expr: @expr,
visitor: vt<VisitContext>)
{
/*!
*
* Indicates that the value of `expr` will be consumed,
* meaning either copied or moved depending on its type.
*/
debug!("consume_expr(expr=%?/%s)",
expr.id,
pprust::expr_to_str(expr, self.tcx.sess.intr()));
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
let mode = self.consume_mode_for_ty(expr_ty);
self.use_expr(expr, mode, visitor);
}
fn consume_block(&self,
blk: &blk,
visitor: vt<VisitContext>)
{
/*!
*
* Indicates that the value of `blk` will be consumed,
* meaning either copied or moved depending on its type.
*/
debug!("consume_block(blk.id=%?)", blk.node.id);
for blk.node.stmts.each |stmt| {
(visitor.visit_stmt)(*stmt, *self, visitor);
}
for blk.node.expr.each |tail_expr| {
self.consume_expr(*tail_expr, visitor);
}
}
fn consume_mode_for_ty(&self, ty: ty::t) -> UseMode {
/*!
*
* Selects the appropriate `UseMode` to consume a value with
* the type `ty`. This will be `MoveEntireMode` if `ty` is
* not implicitly copyable.
*/
let result = if ty::type_implicitly_moves(self.tcx, ty) {
MoveInWhole
} else {
Read
};
debug!("consume_mode_for_ty(ty=%s) = %?",
ppaux::ty_to_str(self.tcx, ty), result);
return result;
}
fn use_expr(&self,
expr: @expr,
expr_mode: UseMode,
visitor: vt<VisitContext>)
{
/*!
*
* Indicates that `expr` is used with a given mode. This will
* in turn trigger calls to the subcomponents of `expr`.
*/
debug!("use_expr(expr=%?/%s, mode=%?)",
expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()),
expr_mode);
match expr_mode {
MoveInWhole => { self.move_maps.moves_map.insert(expr.id, ()); }
MoveInPart(_) | Read => {}
}
// `expr_mode` refers to the post-adjustment value. If one of
// those adjustments is to take a reference, then it's only
// reading the underlying expression, not moving it.
let comp_mode = match self.tcx.adjustments.find(expr.id) {
Some(adj) if adj.autoref.is_some() => Read,
_ => expr_mode.component_mode(expr)
};
debug!("comp_mode = %?", comp_mode);
match expr.node {
expr_path(*) => {
match comp_mode {
MoveInPart(entire_expr) => {
self.move_maps.variable_moves_map.insert(
expr.id, entire_expr);
}
Read => {}
MoveInWhole => {
self.tcx.sess.span_bug(
expr.span,
fmt!("Component mode can never be MoveInWhole"));
}
}
}
expr_unary(deref, base) => { // *base
if !self.use_overloaded_operator(
expr, DontDerefArgs, base, [], visitor)
{
// Moving out of *base moves out of base.
self.use_expr(base, comp_mode, visitor);
}
}
expr_field(base, _, _) => { // base.f
// Moving out of base.f moves out of base.
self.use_expr(base, comp_mode, visitor);
}
expr_index(lhs, rhs) => { // lhs[rhs]
if !self.use_overloaded_operator(
expr, DontDerefArgs, lhs, [rhs], visitor)
{
self.use_expr(lhs, comp_mode, visitor);
self.consume_expr(rhs, visitor);
}
}
expr_call(callee, ref args, _) => { // callee(args)
self.use_expr(callee, Read, visitor);
self.use_fn_args(callee.id, *args, visitor);
}
expr_method_call(callee, _, _, ref args, _) => { // callee.m(args)
// Implicit self is equivalent to & mode, but every
// other kind should be + mode.
self.use_receiver(expr.id, expr.span, callee, visitor);
self.use_fn_args(expr.callee_id, *args, visitor);
}
expr_rec(ref fields, opt_with) |
expr_struct(_, ref fields, opt_with) => {
for fields.each |field| {
self.consume_expr(field.node.expr, visitor);
}
for opt_with.each |with_expr| {
self.consume_expr(*with_expr, visitor);
}
}
expr_tup(ref exprs) => {
self.consume_exprs(*exprs, visitor);
}
expr_if(cond_expr, ref then_blk, opt_else_expr) => {
self.consume_expr(cond_expr, visitor);
self.consume_block(then_blk, visitor);
for opt_else_expr.each |else_expr| {
self.consume_expr(*else_expr, visitor);
}
}
expr_match(discr, ref arms) => {
// We must do this first so that `arms_have_by_move_bindings`
// below knows which bindings are moves.
for arms.each |arm| {
self.consume_arm(arm, visitor);
}
let by_move_bindings_present =
self.arms_have_by_move_bindings(
self.move_maps.moves_map, *arms);
if by_move_bindings_present {
// If one of the arms moves a value out of the
// discriminant, then the discriminant itself is
// moved.
self.consume_expr(discr, visitor);
} else {
// Otherwise, the discriminant is merely read.
self.use_expr(discr, Read, visitor);
}
}
expr_copy(base) => {
self.use_expr(base, Read, visitor);
}
expr_paren(base) => {
// Note: base is not considered a *component* here, so
// use `expr_mode` not `comp_mode`.
self.use_expr(base, expr_mode, visitor);
}
expr_vec(ref exprs, _) => {
self.consume_exprs(*exprs, visitor);
}
expr_addr_of(_, base) => { // &base
self.use_expr(base, Read, visitor);
}
expr_break(*) |
expr_again(*) |
expr_lit(*) => {}
expr_loop(ref blk, _) => {
self.consume_block(blk, visitor);
}
expr_log(_, a_expr, b_expr) => {
self.consume_expr(a_expr, visitor);
self.use_expr(b_expr, Read, visitor);
}
expr_assert(cond_expr) => {
self.consume_expr(cond_expr, visitor);
}
expr_while(cond_expr, ref blk) => {
self.consume_expr(cond_expr, visitor);
self.consume_block(blk, visitor);
}
expr_unary(_, lhs) => {
if !self.use_overloaded_operator(
expr, DontDerefArgs, lhs, [], visitor)
{
self.consume_expr(lhs, visitor);
}
}
expr_binary(_, lhs, rhs) => {
if !self.use_overloaded_operator(
expr, DoDerefArgs, lhs, [rhs], visitor)
{
self.consume_expr(lhs, visitor);
self.consume_expr(rhs, visitor);
}
}
expr_block(ref blk) => {
self.consume_block(blk, visitor);
}
expr_fail(ref opt_expr) |
expr_ret(ref opt_expr) => {
for opt_expr.each |expr| {
self.consume_expr(*expr, visitor);
}
}
expr_assign(lhs, rhs) => {
self.use_expr(lhs, Read, visitor);
self.consume_expr(rhs, visitor);
}
expr_cast(base, _) => {
self.consume_expr(base, visitor);
}
expr_assign_op(_, lhs, rhs) => {
// FIXME(#4712) --- Overloaded operators?
//
// if !self.use_overloaded_operator(
// expr, DoDerefArgs, lhs, [rhs], visitor)
// {
self.consume_expr(lhs, visitor);
self.consume_expr(rhs, visitor);
// }
}
expr_repeat(base, count, _) => {
self.consume_expr(base, visitor);
self.consume_expr(count, visitor);
}
expr_swap(lhs, rhs) => {
self.use_expr(lhs, Read, visitor);
self.use_expr(rhs, Read, visitor);
}
expr_loop_body(base) |
expr_do_body(base) => {
self.use_expr(base, comp_mode, visitor);
}
expr_fn(_, _, ref body) |
expr_fn_block(_, ref body) => {
let cap_vars = self.compute_captures(expr.id);
self.move_maps.capture_map.insert(expr.id, cap_vars);
self.consume_block(body, visitor);
}
expr_vstore(base, _) => {
self.use_expr(base, comp_mode, visitor);
}
expr_mac(*) => {
self.tcx.sess.span_bug(
expr.span,
~"macro expression remains after expansion");
}
}
}
fn use_overloaded_operator(&self,
expr: @expr,
deref_args: DerefArgs,
receiver_expr: @expr,
arg_exprs: &[@expr],
visitor: vt<VisitContext>) -> bool
{
if !self.method_map.contains_key(expr.id) {
return false;
}
self.use_receiver(expr.id, expr.span, receiver_expr, visitor);
// The deref_args stuff should eventually be converted into
// adjustments. Moreover, it should eventually be applied
// consistently to all overloaded operators. But that's not
// how it is today.
match deref_args {
DoDerefArgs => {
// we are always passing in a borrowed pointer,
// so it's always read mode:
for arg_exprs.each |arg_expr| {
self.use_expr(*arg_expr, Read, visitor);
}
}
DontDerefArgs => {
self.use_fn_args(expr.callee_id, arg_exprs, visitor);
}
}
return true;
}
fn consume_arm(&self,
arm: &arm,
visitor: vt<VisitContext>)
{
for arm.pats.each |pat| {
self.use_pat(*pat);
}
for arm.guard.each |guard| {
self.consume_expr(*guard, visitor);
}
self.consume_block(&arm.body, visitor);
}
fn use_pat(&self,
pat: @pat)
{
/*!
*
* Decides whether each binding in a pattern moves the value
* into itself or not based on its type and annotation.
*/
do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, _path| {
let mode = match bm {
bind_by_copy => Read,
bind_by_ref(_) => Read,
bind_infer => {
let pat_ty = ty::node_id_to_type(self.tcx, id);
self.consume_mode_for_ty(pat_ty)
}
};
match mode {
MoveInWhole => { self.move_maps.moves_map.insert(id, ()); }
MoveInPart(_) | Read => {}
}
}
}
fn use_receiver(&self,
expr_id: node_id,
span: span,
receiver_expr: @expr,
visitor: vt<VisitContext>)
{
let callee_mode = match self.method_map.find(expr_id) {
Some(ref method_map_entry) => {
match method_map_entry.explicit_self {
sty_by_ref => by_ref,
_ => by_copy
}
}
None => {
self.tcx.sess.span_bug(
span,
~"no method map entry");
}
};
self.use_fn_arg(callee_mode, receiver_expr, visitor);
}
fn use_fn_args(&self,
callee_id: node_id,
arg_exprs: &[@expr],
visitor: vt<VisitContext>)
{
/*!
*
* Uses the argument expressions according to the function modes.
*/
let arg_tys =
ty::ty_fn_args(ty::node_id_to_type(self.tcx, callee_id));
for vec::each2(arg_exprs, arg_tys) |arg_expr, arg_ty| {
let arg_mode = ty::resolved_mode(self.tcx, arg_ty.mode);
self.use_fn_arg(arg_mode, *arg_expr, visitor);
}
}
fn use_fn_arg(&self,
arg_mode: rmode,
arg_expr: @expr,
visitor: vt<VisitContext>)
{
/*!
*
* Uses the argument according to the given argument mode.
*/
match arg_mode {
by_val | by_ref => self.use_expr(arg_expr, Read, visitor),
by_copy => self.consume_expr(arg_expr, visitor)
}
}
fn arms_have_by_move_bindings(&self,
moves_map: MovesMap,
+arms: &[arm]) -> bool
{
for arms.each |arm| {
for arm.pats.each |pat| {
let mut found = false;
do pat_bindings(self.tcx.def_map, *pat) |_, node_id, _, _| {
if moves_map.contains_key(node_id) {
found = true;
}
}
if found { return true; }
}
}
return false;
}
fn compute_captures(&self, fn_expr_id: node_id) -> @[CaptureVar] {
debug!("compute_capture_vars(fn_expr_id=%?)", fn_expr_id);
let _indenter = indenter();
let fn_ty = ty::node_id_to_type(self.tcx, fn_expr_id);
let proto = ty::ty_fn_proto(fn_ty);
let freevars = freevars::get_freevars(self.tcx, fn_expr_id);
if proto == ProtoBorrowed {
// &fn() captures everything by ref
at_vec::from_fn(freevars.len(), |i| {
let fvar = &freevars[i];
CaptureVar {def: fvar.def, span: fvar.span, mode: CapRef}
})
} else {
// @fn() and ~fn() capture by copy or by move depending on type
at_vec::from_fn(freevars.len(), |i| {
let fvar = &freevars[i];
let fvar_def_id = ast_util::def_id_of_def(fvar.def).node;
let fvar_ty = ty::node_id_to_type(self.tcx, fvar_def_id);
debug!("fvar_def_id=%? fvar_ty=%s",
fvar_def_id, ppaux::ty_to_str(self.tcx, fvar_ty));
let mode = if ty::type_implicitly_moves(self.tcx, fvar_ty) {
CapMove
} else {
CapCopy
};
CaptureVar {def: fvar.def, span: fvar.span, mode:mode}
})
}
}
}

View file

@ -11,7 +11,6 @@
use core::prelude::*; use core::prelude::*;
use middle::resolve; use middle::resolve;
use middle::ty::{CopyValue, MoveValue, ReadValue};
use middle::ty; use middle::ty;
use syntax::ast::*; use syntax::ast::*;
@ -94,30 +93,3 @@ pub fn pat_binding_ids(dm: resolve::DefMap, pat: @pat) -> ~[node_id] {
return found; return found;
} }
pub fn arms_have_by_move_bindings(tcx: ty::ctxt, +arms: &[arm]) -> bool {
for arms.each |arm| {
for arm.pats.each |pat| {
let mut found = false;
do pat_bindings(tcx.def_map, *pat)
|binding_mode, node_id, span, _path| {
match binding_mode {
bind_by_move => found = true,
bind_infer => {
match tcx.value_modes.find(node_id) {
Some(MoveValue) => found = true,
Some(CopyValue) | Some(ReadValue) => {}
None => {
tcx.sess.span_bug(span, ~"pat binding not in \
value mode map");
}
}
}
bind_by_ref(*) | bind_by_value => {}
}
}
if found { return true; }
}
}
return false;
}

View file

@ -290,15 +290,6 @@ pub fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
cx.sess.intr())); cx.sess.intr()));
new_cx.parent = Some(expr.id); new_cx.parent = Some(expr.id);
} }
ast::expr_fn(_, _, _, cap_clause) |
ast::expr_fn_block(_, _, cap_clause) => {
// although the capture items are not expressions per se, they
// do get "evaluated" in some sense as copies or moves of the
// relevant variables so we parent them like an expression
for (*cap_clause).each |cap_item| {
record_parent(new_cx, cap_item.id);
}
}
ast::expr_while(cond, _) => { ast::expr_while(cond, _) => {
new_cx.root_exprs.insert(cond.id, ()); new_cx.root_exprs.insert(cond.id, ());
} }

View file

@ -24,8 +24,8 @@ use core::cmp;
use core::str; use core::str;
use core::vec; use core::vec;
use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm}; use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk, capture_clause}; use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk};
use syntax::ast::{bind_by_value, bind_infer, bind_by_ref, bind_by_move}; use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy};
use syntax::ast::{crate, crate_num, decl_item, def, def_arg, def_binding}; use syntax::ast::{crate, crate_num, decl_item, def, def_arg, def_binding};
use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label}; use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label};
use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self}; use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self};
@ -175,11 +175,6 @@ pub enum SelfBinding {
HasSelfBinding(node_id, bool /* is implicit */) HasSelfBinding(node_id, bool /* is implicit */)
} }
pub enum CaptureClause {
NoCaptureClause,
HasCaptureClause(capture_clause)
}
pub type ResolveVisitor = vt<()>; pub type ResolveVisitor = vt<()>;
#[deriving_eq] #[deriving_eq]
@ -3727,7 +3722,6 @@ pub impl Resolver {
OpaqueFunctionRibKind), OpaqueFunctionRibKind),
(*block), (*block),
NoSelfBinding, NoSelfBinding,
NoCaptureClause,
visitor); visitor);
} }
@ -3803,33 +3797,7 @@ pub impl Resolver {
type_parameters: TypeParameters, type_parameters: TypeParameters,
block: blk, block: blk,
self_binding: SelfBinding, self_binding: SelfBinding,
capture_clause: CaptureClause,
visitor: ResolveVisitor) { visitor: ResolveVisitor) {
// Check each element of the capture clause.
match capture_clause {
NoCaptureClause => {
// Nothing to do.
}
HasCaptureClause(capture_clause) => {
// Resolve each captured item.
for (*capture_clause).each |capture_item| {
match self.resolve_identifier(capture_item.name,
ValueNS,
true,
capture_item.span) {
None => {
self.session.span_err(capture_item.span,
~"unresolved name in \
capture clause");
}
Some(def) => {
self.record_def(capture_item.id, def);
}
}
}
}
}
// Create a value rib for the function. // Create a value rib for the function.
let function_value_rib = @Rib(rib_kind); let function_value_rib = @Rib(rib_kind);
(*self.value_ribs).push(function_value_rib); (*self.value_ribs).push(function_value_rib);
@ -3945,7 +3913,6 @@ pub impl Resolver {
HasSelfBinding HasSelfBinding
((*destructor).node.self_id, ((*destructor).node.self_id,
true), true),
NoCaptureClause,
visitor); visitor);
} }
} }
@ -3976,7 +3943,6 @@ pub impl Resolver {
type_parameters, type_parameters,
method.body, method.body,
self_binding, self_binding,
NoCaptureClause,
visitor); visitor);
} }
@ -4047,7 +4013,6 @@ pub impl Resolver {
NormalRibKind), NormalRibKind),
method.body, method.body,
HasSelfBinding(method.self_id), HasSelfBinding(method.self_id),
NoCaptureClause,
visitor); visitor);
*/ */
} }
@ -4855,14 +4820,13 @@ pub impl Resolver {
visit_expr(expr, (), visitor); visit_expr(expr, (), visitor);
} }
expr_fn(_, ref fn_decl, ref block, capture_clause) | expr_fn(_, ref fn_decl, ref block) |
expr_fn_block(ref fn_decl, ref block, capture_clause) => { expr_fn_block(ref fn_decl, ref block) => {
self.resolve_function(FunctionRibKind(expr.id, block.node.id), self.resolve_function(FunctionRibKind(expr.id, block.node.id),
Some(@/*bad*/copy *fn_decl), Some(@/*bad*/copy *fn_decl),
NoTypeParameters, NoTypeParameters,
(*block), (*block),
NoSelfBinding, NoSelfBinding,
HasCaptureClause(capture_clause),
visitor); visitor);
} }
@ -5139,18 +5103,12 @@ pub impl Resolver {
descr: &str) { descr: &str) {
match pat_binding_mode { match pat_binding_mode {
bind_infer => {} bind_infer => {}
bind_by_value => { bind_by_copy => {
self.session.span_err( self.session.span_err(
pat.span, pat.span,
fmt!("cannot use `copy` binding mode with %s", fmt!("cannot use `copy` binding mode with %s",
descr)); descr));
} }
bind_by_move => {
self.session.span_err(
pat.span,
fmt!("cannot use `move` binding mode with %s",
descr));
}
bind_by_ref(*) => { bind_by_ref(*) => {
self.session.span_err( self.session.span_err(
pat.span, pat.span,

View file

@ -160,7 +160,6 @@ use middle::trans::datum::*;
use middle::trans::expr::Dest; use middle::trans::expr::Dest;
use middle::trans::expr; use middle::trans::expr;
use middle::trans::glue; use middle::trans::glue;
use middle::ty::{CopyValue, MoveValue, ReadValue};
use util::common::indenter; use util::common::indenter;
use core::dvec::DVec; use core::dvec::DVec;
@ -935,7 +934,7 @@ pub fn root_pats_as_necessary(bcx: block,
// for details (look for the case covering cat_discr). // for details (look for the case covering cat_discr).
let datum = Datum {val: val, ty: node_id_type(bcx, pat_id), let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
mode: ByRef, source: FromLvalue}; mode: ByRef, source: ZeroMem};
bcx = datum.root(bcx, root_info); bcx = datum.root(bcx, root_info);
// If we kept going, we'd only re-root the same value, so // If we kept going, we'd only re-root the same value, so
// return now. // return now.
@ -1091,7 +1090,7 @@ pub fn store_non_ref_bindings(bcx: block,
TrByValue(is_move, lldest) => { TrByValue(is_move, lldest) => {
let llval = Load(bcx, binding_info.llmatch); // get a T* let llval = Load(bcx, binding_info.llmatch); // get a T*
let datum = Datum {val: llval, ty: binding_info.ty, let datum = Datum {val: llval, ty: binding_info.ty,
mode: ByRef, source: FromLvalue}; mode: ByRef, source: ZeroMem};
bcx = { bcx = {
if is_move { if is_move {
datum.move_to(bcx, INIT, lldest) datum.move_to(bcx, INIT, lldest)
@ -1575,30 +1574,19 @@ pub fn trans_match_inner(scope_cx: block,
// Note that we use the names because each binding will have many ids // Note that we use the names because each binding will have many ids
// from the various alternatives. // from the various alternatives.
let bindings_map = HashMap(); let bindings_map = HashMap();
do pat_bindings(tcx.def_map, arm.pats[0]) |bm, p_id, s, path| { do pat_bindings(tcx.def_map, arm.pats[0]) |bm, p_id, _s, path| {
let ident = path_to_ident(path); let ident = path_to_ident(path);
let variable_ty = node_id_type(bcx, p_id); let variable_ty = node_id_type(bcx, p_id);
let llvariable_ty = type_of::type_of(bcx.ccx(), variable_ty); let llvariable_ty = type_of::type_of(bcx.ccx(), variable_ty);
let llmatch, trmode; let llmatch, trmode;
match bm { match bm {
ast::bind_by_value | ast::bind_by_move => { ast::bind_by_copy | ast::bind_infer => {
// in this case, the type of the variable will be T, // in this case, the final type of the variable will be T,
// but we need to store a *T // but during matching we need to store a *T as explained
let is_move = (bm == ast::bind_by_move); // above
llmatch = alloca(bcx, T_ptr(llvariable_ty)); let is_move =
trmode = TrByValue(is_move, alloca(bcx, llvariable_ty)); scope_cx.ccx().maps.moves_map.contains_key(p_id);
}
ast::bind_infer => {
// in this case also, the type of the variable will be T,
// but we need to store a *T
let is_move = match tcx.value_modes.find(p_id) {
None => {
tcx.sess.span_bug(s, ~"no value mode");
}
Some(MoveValue) => true,
Some(CopyValue) | Some(ReadValue) => false
};
llmatch = alloca(bcx, T_ptr(llvariable_ty)); llmatch = alloca(bcx, T_ptr(llvariable_ty));
trmode = TrByValue(is_move, alloca(bcx, llvariable_ty)); trmode = TrByValue(is_move, alloca(bcx, llvariable_ty));
} }
@ -1657,7 +1645,8 @@ pub fn trans_match_inner(scope_cx: block,
arm_cxs.push(bcx); arm_cxs.push(bcx);
} }
return controlflow::join_blocks(scope_cx, dvec::unwrap(move arm_cxs)); bcx = controlflow::join_blocks(scope_cx, dvec::unwrap(move arm_cxs));
return bcx;
fn mk_fail(bcx: block, sp: span, +msg: ~str, fn mk_fail(bcx: block, sp: span, +msg: ~str,
finished: @mut Option<BasicBlockRef>) -> BasicBlockRef { finished: @mut Option<BasicBlockRef>) -> BasicBlockRef {
@ -1697,7 +1686,7 @@ pub fn bind_irrefutable_pat(bcx: block,
if make_copy { if make_copy {
let binding_ty = node_id_type(bcx, pat.id); let binding_ty = node_id_type(bcx, pat.id);
let datum = Datum {val: val, ty: binding_ty, let datum = Datum {val: val, ty: binding_ty,
mode: ByRef, source: FromRvalue}; mode: ByRef, source: RevokeClean};
let scratch = scratch_datum(bcx, binding_ty, false); let scratch = scratch_datum(bcx, binding_ty, false);
datum.copy_to_datum(bcx, INIT, scratch); datum.copy_to_datum(bcx, INIT, scratch);
match binding_mode { match binding_mode {

View file

@ -1676,7 +1676,7 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
ast::by_ref => { ast::by_ref => {
llarg = raw_llarg; llarg = raw_llarg;
} }
ast::by_move | ast::by_copy => { ast::by_copy => {
// only by value if immediate: // only by value if immediate:
if datum::appropriate_mode(arg_ty.ty).is_by_value() { if datum::appropriate_mode(arg_ty.ty).is_by_value() {
let alloc = alloc_ty(bcx, arg_ty.ty); let alloc = alloc_ty(bcx, arg_ty.ty);
@ -2198,16 +2198,8 @@ pub fn create_main_wrapper(ccx: @crate_ctxt, _sp: span, main_llfn: ValueRef) {
create_entry_fn(ccx, llfn); create_entry_fn(ccx, llfn);
fn create_main(ccx: @crate_ctxt, main_llfn: ValueRef) -> ValueRef { fn create_main(ccx: @crate_ctxt, main_llfn: ValueRef) -> ValueRef {
let unit_ty = ty::mk_estr(ccx.tcx, ty::vstore_uniq);
let vecarg_ty: ty::arg =
arg {
mode: ast::expl(ast::by_val),
ty: ty::mk_evec(ccx.tcx,
ty::mt {ty: unit_ty, mutbl: ast::m_imm},
ty::vstore_uniq)
};
let nt = ty::mk_nil(ccx.tcx); let nt = ty::mk_nil(ccx.tcx);
let llfty = type_of_fn(ccx, ~[vecarg_ty], nt); let llfty = type_of_fn(ccx, ~[], nt);
let llfdecl = decl_fn(ccx.llmod, ~"_rust_main", let llfdecl = decl_fn(ccx.llmod, ~"_rust_main",
lib::llvm::CCallConv, llfty); lib::llvm::CCallConv, llfty);
@ -2953,7 +2945,7 @@ pub fn trans_crate(sess: session::Session,
tcx: ty::ctxt, tcx: ty::ctxt,
output: &Path, output: &Path,
emap2: resolve::ExportMap2, emap2: resolve::ExportMap2,
maps: astencode::maps) -> (ModuleRef, link_meta) { maps: astencode::Maps) -> (ModuleRef, link_meta) {
let symbol_hasher = @hash::default_state(); let symbol_hasher = @hash::default_state();
let link_meta = let link_meta =

View file

@ -79,7 +79,7 @@ pub fn count_insn(cx: block, category: &str) {
s += ~"/"; s += ~"/";
s += category; s += category;
let n = match h.find(s) { let n = match h.find(/*bad*/ copy s) {
Some(n) => n, Some(n) => n,
_ => 0u _ => 0u
}; };

View file

@ -334,7 +334,7 @@ pub fn trans_method_call(in_cx: block,
pub fn trans_rtcall_or_lang_call(bcx: block, pub fn trans_rtcall_or_lang_call(bcx: block,
did: ast::def_id, did: ast::def_id,
args: ~[ValueRef], args: &[ValueRef],
dest: expr::Dest) dest: expr::Dest)
-> block { -> block {
let fty = if did.crate == ast::local_crate { let fty = if did.crate == ast::local_crate {
@ -351,7 +351,7 @@ pub fn trans_rtcall_or_lang_call(bcx: block,
pub fn trans_rtcall_or_lang_call_with_type_params(bcx: block, pub fn trans_rtcall_or_lang_call_with_type_params(bcx: block,
did: ast::def_id, did: ast::def_id,
args: ~[ValueRef], args: &[ValueRef],
type_params: ~[ty::t], type_params: ~[ty::t],
dest: expr::Dest) dest: expr::Dest)
-> block { -> block {
@ -418,11 +418,11 @@ pub fn trans_call_inner(
dest: expr::Dest, dest: expr::Dest,
autoref_arg: AutorefArg) -> block { autoref_arg: AutorefArg) -> block {
do base::with_scope(in_cx, call_info, ~"call") |cx| { do base::with_scope(in_cx, call_info, ~"call") |cx| {
let ret_in_loop = match /*bad*/copy args { let ret_in_loop = match args {
ArgExprs(args) => { ArgExprs(args) => {
args.len() > 0u && match vec::last(args).node { args.len() > 0u && match vec::last(args).node {
ast::expr_loop_body(@ast::expr { ast::expr_loop_body(@ast::expr {
node: ast::expr_fn_block(_, ref body, _), node: ast::expr_fn_block(_, ref body),
_ _
}) => body_contains_ret((*body)), }) => body_contains_ret((*body)),
_ => false _ => false
@ -464,7 +464,7 @@ pub fn trans_call_inner(
} }
}; };
let args_res = trans_args(bcx, llenv, /*bad*/copy args, fn_expr_ty, let args_res = trans_args(bcx, llenv, args, fn_expr_ty,
dest, ret_flag, autoref_arg); dest, ret_flag, autoref_arg);
bcx = args_res.bcx; bcx = args_res.bcx;
let mut llargs = /*bad*/copy args_res.args; let mut llargs = /*bad*/copy args_res.args;
@ -520,9 +520,10 @@ pub fn trans_call_inner(
} }
} }
pub enum CallArgs { pub enum CallArgs {
ArgExprs(~[@ast::expr]), ArgExprs(&[@ast::expr]),
ArgVals(~[ValueRef]) ArgVals(&[ValueRef])
} }
pub fn trans_args(cx: block, pub fn trans_args(cx: block,
@ -625,7 +626,7 @@ pub fn trans_arg_expr(bcx: block,
ast::expr_loop_body( ast::expr_loop_body(
// XXX: Bad copy. // XXX: Bad copy.
blk@@ast::expr { blk@@ast::expr {
node: ast::expr_fn_block(copy decl, ref body, cap), node: ast::expr_fn_block(copy decl, ref body),
_ _
}) => }) =>
{ {
@ -635,12 +636,12 @@ pub fn trans_arg_expr(bcx: block,
let proto = ty::ty_fn_proto(arg_ty); let proto = ty::ty_fn_proto(arg_ty);
let bcx = closure::trans_expr_fn( let bcx = closure::trans_expr_fn(
bcx, proto, decl, /*bad*/copy *body, arg_expr.id, bcx, proto, decl, /*bad*/copy *body, arg_expr.id,
blk.id, cap, Some(ret_flag), expr::SaveIn(scratch)); blk.id, Some(ret_flag), expr::SaveIn(scratch));
DatumBlock {bcx: bcx, DatumBlock {bcx: bcx,
datum: Datum {val: scratch, datum: Datum {val: scratch,
ty: scratch_ty, ty: scratch_ty,
mode: ByRef, mode: ByRef,
source: FromRvalue}} source: RevokeClean}}
} }
_ => { _ => {
bcx.sess().impossible_case( bcx.sess().impossible_case(
@ -670,34 +671,35 @@ pub fn trans_arg_expr(bcx: block,
} else { } else {
// FIXME(#3548) use the adjustments table // FIXME(#3548) use the adjustments table
match autoref_arg { match autoref_arg {
DoAutorefArg => { val = arg_datum.to_ref_llval(bcx); } DoAutorefArg => {
assert !bcx.ccx().maps.moves_map.contains_key(arg_expr.id);
val = arg_datum.to_ref_llval(bcx);
}
DontAutorefArg => { DontAutorefArg => {
match arg_mode { match arg_mode {
ast::by_ref => { ast::by_ref => {
// This assertion should really be valid, but because
// the explicit self code currently passes by-ref, it
// does not hold.
//
//assert !bcx.ccx().maps.moves_map.contains_key(
// arg_expr.id);
val = arg_datum.to_ref_llval(bcx); val = arg_datum.to_ref_llval(bcx);
} }
ast::by_val => { ast::by_val => {
// NB: avoid running the take glue. // NB: avoid running the take glue.
assert !bcx.ccx().maps.moves_map.contains_key(
arg_expr.id);
val = arg_datum.to_value_llval(bcx); val = arg_datum.to_value_llval(bcx);
} }
ast::by_copy | ast::by_move => { ast::by_copy => {
let scratch = scratch_datum(bcx, arg_datum.ty, false); let scratch = scratch_datum(bcx, arg_datum.ty, false);
if arg_mode == ast::by_move { arg_datum.store_to_datum(bcx, arg_expr.id,
// NDM---Doesn't seem like this should be INIT, scratch);
// necessary
if !arg_datum.store_will_move() {
bcx.sess().span_bug(
arg_expr.span,
fmt!("move mode but datum will not \
store: %s",
arg_datum.to_str(bcx.ccx())));
}
}
arg_datum.store_to_datum(bcx, INIT, scratch);
// Technically, ownership of val passes to the callee. // Technically, ownership of val passes to the callee.
// However, we must cleanup should we fail before the // However, we must cleanup should we fail before the

View file

@ -15,12 +15,12 @@ use back::link::{mangle_internal_name_by_path_and_seq};
use back::link::{mangle_internal_name_by_path}; use back::link::{mangle_internal_name_by_path};
use lib::llvm::llvm; use lib::llvm::llvm;
use lib::llvm::{ValueRef, TypeRef}; use lib::llvm::{ValueRef, TypeRef};
use middle::capture; use middle::moves;
use middle::trans::base::*; use middle::trans::base::*;
use middle::trans::build::*; use middle::trans::build::*;
use middle::trans::callee; use middle::trans::callee;
use middle::trans::common::*; use middle::trans::common::*;
use middle::trans::datum::{Datum, INIT, ByRef, ByValue, FromLvalue}; use middle::trans::datum::{Datum, INIT, ByRef, ByValue, ZeroMem};
use middle::trans::expr; use middle::trans::expr;
use middle::trans::glue; use middle::trans::glue;
use middle::trans::machine; use middle::trans::machine;
@ -106,7 +106,7 @@ use syntax::print::pprust::expr_to_str;
pub enum EnvAction { pub enum EnvAction {
/// Copy the value from this llvm ValueRef into the environment. /// Copy the value from this llvm ValueRef into the environment.
EnvStore, EnvCopy,
/// Move the value from this llvm ValueRef into the environment. /// Move the value from this llvm ValueRef into the environment.
EnvMove, EnvMove,
@ -123,7 +123,7 @@ pub struct EnvValue {
pub impl EnvAction { pub impl EnvAction {
fn to_str() -> ~str { fn to_str() -> ~str {
match self { match self {
EnvStore => ~"EnvStore", EnvCopy => ~"EnvCopy",
EnvMove => ~"EnvMove", EnvMove => ~"EnvMove",
EnvRef => ~"EnvRef" EnvRef => ~"EnvRef"
} }
@ -151,7 +151,7 @@ pub fn mk_closure_tys(tcx: ty::ctxt,
// converted to ptrs. // converted to ptrs.
let bound_tys = bound_values.map(|bv| { let bound_tys = bound_values.map(|bv| {
match bv.action { match bv.action {
EnvStore | EnvMove => bv.datum.ty, EnvCopy | EnvMove => bv.datum.ty,
EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty) EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
} }
}); });
@ -242,8 +242,8 @@ pub fn store_environment(bcx: block,
let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]); let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
match bv.action { match bv.action {
EnvStore => { EnvCopy => {
bcx = bv.datum.store_to(bcx, INIT, bound_data); bcx = bv.datum.copy_to(bcx, INIT, bound_data);
} }
EnvMove => { EnvMove => {
bcx = bv.datum.move_to(bcx, INIT, bound_data); bcx = bv.datum.move_to(bcx, INIT, bound_data);
@ -264,7 +264,7 @@ pub fn store_environment(bcx: block,
// Given a context and a list of upvars, build a closure. This just // Given a context and a list of upvars, build a closure. This just
// collects the upvars and packages them up for store_environment. // collects the upvars and packages them up for store_environment.
pub fn build_closure(bcx0: block, pub fn build_closure(bcx0: block,
cap_vars: ~[capture::capture_var], cap_vars: &[moves::CaptureVar],
proto: ast::Proto, proto: ast::Proto,
include_ret_handle: Option<ValueRef>) -> closure_result { include_ret_handle: Option<ValueRef>) -> closure_result {
let _icx = bcx0.insn_ctxt("closure::build_closure"); let _icx = bcx0.insn_ctxt("closure::build_closure");
@ -274,27 +274,23 @@ pub fn build_closure(bcx0: block,
// Package up the captured upvars // Package up the captured upvars
let mut env_vals = ~[]; let mut env_vals = ~[];
for vec::each(cap_vars) |cap_var| { for cap_vars.each |cap_var| {
debug!("Building closure: captured variable %?", *cap_var); debug!("Building closure: captured variable %?", *cap_var);
let datum = expr::trans_local_var(bcx, cap_var.def, None); let datum = expr::trans_local_var(bcx, cap_var.def);
match cap_var.mode { match cap_var.mode {
capture::cap_ref => { moves::CapRef => {
assert proto == ast::ProtoBorrowed; assert proto == ast::ProtoBorrowed;
env_vals.push(EnvValue {action: EnvRef, env_vals.push(EnvValue {action: EnvRef,
datum: datum}); datum: datum});
} }
capture::cap_copy => { moves::CapCopy => {
env_vals.push(EnvValue {action: EnvStore, env_vals.push(EnvValue {action: EnvCopy,
datum: datum}); datum: datum});
} }
capture::cap_move => { moves::CapMove => {
env_vals.push(EnvValue {action: EnvMove, env_vals.push(EnvValue {action: EnvMove,
datum: datum}); datum: datum});
} }
capture::cap_drop => {
bcx = datum.drop_val(bcx);
datum.cancel_clean(bcx);
}
} }
} }
@ -303,7 +299,7 @@ pub fn build_closure(bcx0: block,
do option::iter(&include_ret_handle) |flagptr| { do option::iter(&include_ret_handle) |flagptr| {
// Flag indicating we have returned (a by-ref bool): // Flag indicating we have returned (a by-ref bool):
let flag_datum = Datum {val: *flagptr, ty: ty::mk_bool(tcx), let flag_datum = Datum {val: *flagptr, ty: ty::mk_bool(tcx),
mode: ByRef, source: FromLvalue}; mode: ByRef, source: ZeroMem};
env_vals.push(EnvValue {action: EnvRef, env_vals.push(EnvValue {action: EnvRef,
datum: flag_datum}); datum: flag_datum});
@ -315,7 +311,7 @@ pub fn build_closure(bcx0: block,
}; };
let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil())); let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil()));
let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx), let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx),
mode: ByRef, source: FromLvalue}; mode: ByRef, source: ZeroMem};
env_vals.push(EnvValue {action: EnvRef, env_vals.push(EnvValue {action: EnvRef,
datum: ret_datum}); datum: ret_datum});
} }
@ -328,7 +324,7 @@ pub fn build_closure(bcx0: block,
// with the upvars and type descriptors. // with the upvars and type descriptors.
pub fn load_environment(fcx: fn_ctxt, pub fn load_environment(fcx: fn_ctxt,
cdata_ty: ty::t, cdata_ty: ty::t,
cap_vars: ~[capture::capture_var], cap_vars: &[moves::CaptureVar],
load_ret_handle: bool, load_ret_handle: bool,
proto: ast::Proto) { proto: ast::Proto) {
let _icx = fcx.insn_ctxt("closure::load_environment"); let _icx = fcx.insn_ctxt("closure::load_environment");
@ -354,20 +350,15 @@ pub fn load_environment(fcx: fn_ctxt,
// Populate the upvars from the environment. // Populate the upvars from the environment.
let mut i = 0u; let mut i = 0u;
for vec::each(cap_vars) |cap_var| { for cap_vars.each |cap_var| {
match cap_var.mode { let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
capture::cap_drop => { /* ignore */ } match proto {
_ => { ast::ProtoBorrowed => { upvarptr = Load(bcx, upvarptr); }
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]); ast::ProtoBox | ast::ProtoUniq | ast::ProtoBare => {}
match proto {
ast::ProtoBorrowed => { upvarptr = Load(bcx, upvarptr); }
ast::ProtoBox | ast::ProtoUniq | ast::ProtoBare => {}
}
let def_id = ast_util::def_id_of_def(cap_var.def);
fcx.llupvars.insert(def_id.node, upvarptr);
i += 1u;
}
} }
let def_id = ast_util::def_id_of_def(cap_var.def);
fcx.llupvars.insert(def_id.node, upvarptr);
i += 1u;
} }
if load_ret_handle { if load_ret_handle {
let flagptr = Load(bcx, GEPi(bcx, llcdata, [0u, i])); let flagptr = Load(bcx, GEPi(bcx, llcdata, [0u, i]));
@ -383,9 +374,9 @@ pub fn trans_expr_fn(bcx: block,
+body: ast::blk, +body: ast::blk,
outer_id: ast::node_id, outer_id: ast::node_id,
user_id: ast::node_id, user_id: ast::node_id,
cap_clause: ast::capture_clause,
is_loop_body: Option<Option<ValueRef>>, is_loop_body: Option<Option<ValueRef>>,
dest: expr::Dest) -> block { dest: expr::Dest) -> block
{
/*! /*!
* *
* Translates the body of a closure expression. * Translates the body of a closure expression.
@ -426,29 +417,24 @@ pub fn trans_expr_fn(bcx: block,
~"expr_fn"); ~"expr_fn");
let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty); let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
let trans_closure_env: &fn(ast::Proto) -> Result = |proto| {
let cap_vars = capture::compute_capture_vars(ccx.tcx, user_id, proto,
cap_clause);
let ret_handle = match is_loop_body { Some(x) => x, None => None };
// XXX: Bad copy.
let {llbox, cdata_ty, bcx} = build_closure(bcx, copy cap_vars, proto,
ret_handle);
trans_closure(ccx, /*bad*/copy sub_path, decl, /*bad*/copy body,
llfn, no_self, /*bad*/copy bcx.fcx.param_substs,
user_id, None, |fcx| {
load_environment(fcx, cdata_ty, copy cap_vars,
ret_handle.is_some(), proto);
}, |bcx| {
if is_loop_body.is_some() {
Store(bcx, C_bool(true), bcx.fcx.llretptr);
}
});
rslt(bcx, llbox)
};
let Result {bcx: bcx, val: closure} = match proto { let Result {bcx: bcx, val: closure} = match proto {
ast::ProtoBorrowed | ast::ProtoBox | ast::ProtoUniq => { ast::ProtoBorrowed | ast::ProtoBox | ast::ProtoUniq => {
trans_closure_env(proto) let cap_vars = ccx.maps.capture_map.get(user_id);
let ret_handle = match is_loop_body {Some(x) => x,
None => None};
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, proto,
ret_handle);
trans_closure(ccx, sub_path, decl,
body, llfn, no_self,
/*bad*/ copy bcx.fcx.param_substs, user_id, None,
|fcx| load_environment(fcx, cdata_ty, cap_vars,
ret_handle.is_some(), proto),
|bcx| {
if is_loop_body.is_some() {
Store(bcx, C_bool(true), bcx.fcx.llretptr);
}
});
rslt(bcx, llbox)
} }
ast::ProtoBare => { ast::ProtoBare => {
trans_closure(ccx, sub_path, decl, body, llfn, no_self, None, trans_closure(ccx, sub_path, decl, body, llfn, no_self, None,

View file

@ -205,7 +205,7 @@ pub struct crate_ctxt {
type_short_names: HashMap<ty::t, ~str>, type_short_names: HashMap<ty::t, ~str>,
all_llvm_symbols: Set<~str>, all_llvm_symbols: Set<~str>,
tcx: ty::ctxt, tcx: ty::ctxt,
maps: astencode::maps, maps: astencode::Maps,
stats: stats, stats: stats,
upcalls: @upcall::upcalls, upcalls: @upcall::upcalls,
tydesc_type: TypeRef, tydesc_type: TypeRef,
@ -1134,7 +1134,7 @@ pub fn C_u8(i: uint) -> ValueRef {
// our boxed-and-length-annotated strings. // our boxed-and-length-annotated strings.
pub fn C_cstr(cx: @crate_ctxt, +s: ~str) -> ValueRef { pub fn C_cstr(cx: @crate_ctxt, +s: ~str) -> ValueRef {
unsafe { unsafe {
match cx.const_cstr_cache.find(s) { match cx.const_cstr_cache.find(/*bad*/copy s) {
Some(llval) => return llval, Some(llval) => return llval,
None => () None => ()
} }

View file

@ -41,45 +41,35 @@
* convenient for interfacing with the various code floating around * convenient for interfacing with the various code floating around
* that predates datums. * that predates datums.
* *
* # Datum sources * # Datum cleanup styles
* *
* Each datum carries with it an idea of its "source". This indicates * Each datum carries with it an idea of how its value will be cleaned
* the kind of expression from which the datum originated. The source * up. This is important after a move, because we need to know how to
* affects what happens when the datum is stored or moved. * cancel the cleanup (since the value has been moved and therefore does
* not need to be freed). There are two options:
* *
* There are three options: * 1. `RevokeClean`: To cancel the cleanup, we invoke `revoke_clean()`.
* This is used for temporary rvalues.
* *
* 1. `FromRvalue`: This value originates from some temporary rvalue. * 2. `ZeroMem`: To cancel the cleanup, we zero out the memory where
* This is therefore the owning reference to the datum. If the * the value resides. This is used for lvalues.
* datum is stored, then, it will be *moved* into its new home.
* Furthermore, we will not zero out the datum but rather use
* `revoke_clean()` to cancel any cleanup.
* *
* 2. `FromLvalue`: This value originates from an lvalue. If the datum * # Copying, moving, and storing
* is stored, it will be *copied* into its new home. If the datum
* is moved, it will be zeroed out.
* *
* 3. `FromLastUseLvalue`: The same as FromLvalue, except that it * There are three methods for moving the value into a new
* originates from the *last use* of an lvalue. If the datum is * location:
* stored, then, it will be moved (and zeroed out).
* *
* # Storing, copying, and moving * - `copy_to()` will copy the value into a new location, meaning that
* the value is first mem-copied and then the new location is "taken"
* via the take glue, in effect creating a deep clone.
* *
* There are three kinds of methods for moving the value into a new * - `move_to()` will copy the value, meaning that the value is mem-copied
* location. *Storing* a datum is probably the one you want to reach * into its new home and then the cleanup on the this datum is revoked.
* for first: it is used when you will no longer use the datum and * This is a "shallow" clone. After `move_to()`, the current datum
* would like to place it somewhere. It may translate to a copy or a * is invalid and should no longer be used.
* move, depending on the source of the datum. After a store, the
* datum may or may not be usable anymore, so you must assume it is
* not.
* *
* Sometimes, though, you want to use an explicit copy or move. A * - `store_to()` either performs a copy or a move by consulting the
* copy copies the data from the datum into a new location and * moves_map computed by `middle::moves`.
* executes the take glue on that location, thus leaving the datum
* valid for further use. Moving, in contrast, copies the data into
* the new location and then cancels any cleanups on the current datum
* (as appropriate for the source). No glue code is executed. After
* a move, the datum is no longer usable.
* *
* # Scratch datum * # Scratch datum
* *
@ -133,8 +123,8 @@ pub struct Datum {
/// How did this value originate? This is particularly important /// How did this value originate? This is particularly important
/// if the value is MOVED or prematurely DROPPED, because it /// if the value is MOVED or prematurely DROPPED, because it
/// describes how to cancel the cleanup that was scheduled before. /// describes how to cancel the cleanup that was scheduled before.
/// See the def'n of the `DatumSource` type. /// See the def'n of the `DatumCleanup` type.
source: DatumSource source: DatumCleanup
} }
pub struct DatumBlock { pub struct DatumBlock {
@ -173,32 +163,16 @@ pub impl DatumMode: to_bytes::IterBytes {
} }
} }
/// See `Datum Sources` section at the head of this module. /// See `Datum cleanup styles` section at the head of this module.
pub enum DatumSource { #[deriving_eq]
FromRvalue, pub enum DatumCleanup {
FromLvalue, RevokeClean,
FromLastUseLvalue, ZeroMem
}
pub impl DatumSource {
fn is_rvalue() -> bool {
match self {
FromRvalue => true,
FromLvalue | FromLastUseLvalue => false
}
}
fn is_any_lvalue() -> bool {
match self {
FromRvalue => false,
FromLvalue | FromLastUseLvalue => true
}
}
} }
pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum {
return Datum {val: val, ty: ty, return Datum {val: val, ty: ty,
mode: ByValue, source: FromRvalue}; mode: ByValue, source: RevokeClean};
} }
pub fn immediate_rvalue_bcx(bcx: block, pub fn immediate_rvalue_bcx(bcx: block,
@ -221,7 +195,7 @@ pub fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum {
let llty = type_of::type_of(bcx.ccx(), ty); let llty = type_of::type_of(bcx.ccx(), ty);
let scratch = alloca_maybe_zeroed(bcx, llty, zero); let scratch = alloca_maybe_zeroed(bcx, llty, zero);
Datum { val: scratch, ty: ty, mode: ByRef, source: FromRvalue } Datum { val: scratch, ty: ty, mode: ByRef, source: RevokeClean }
} }
pub fn appropriate_mode(ty: ty::t) -> DatumMode { pub fn appropriate_mode(ty: ty::t) -> DatumMode {
@ -241,42 +215,39 @@ pub fn appropriate_mode(ty: ty::t) -> DatumMode {
} }
pub impl Datum { pub impl Datum {
fn store_will_move() -> bool { fn store_to(bcx: block, id: ast::node_id,
match self.source { action: CopyAction, dst: ValueRef) -> block {
FromRvalue | FromLastUseLvalue => true,
FromLvalue => false
}
}
fn store_to(bcx: block, action: CopyAction, dst: ValueRef) -> block {
/*! /*!
* *
* Stores this value into its final home. This moves if * Stores this value into its final home. This moves if
* possible, but copies otherwise. */ * `id` is located in the move table, but copies otherwise.
*/
if self.store_will_move() { if bcx.ccx().maps.moves_map.contains_key(id) {
self.move_to(bcx, action, dst) self.move_to(bcx, action, dst)
} else { } else {
self.copy_to(bcx, action, dst) self.copy_to(bcx, action, dst)
} }
} }
fn store_to_dest(bcx: block, dest: expr::Dest) -> block { fn store_to_dest(bcx: block, id: ast::node_id,
dest: expr::Dest) -> block {
match dest { match dest {
expr::Ignore => { expr::Ignore => {
return bcx; return bcx;
} }
expr::SaveIn(addr) => { expr::SaveIn(addr) => {
return self.store_to(bcx, INIT, addr); return self.store_to(bcx, id, INIT, addr);
} }
} }
} }
fn store_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block { fn store_to_datum(bcx: block, id: ast::node_id,
action: CopyAction, datum: Datum) -> block {
debug!("store_to_datum(self=%s, action=%?, datum=%s)", debug!("store_to_datum(self=%s, action=%?, datum=%s)",
self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx())); self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
assert datum.mode.is_by_ref(); assert datum.mode.is_by_ref();
self.store_to(bcx, action, datum.val) self.store_to(bcx, id, action, datum.val)
} }
fn move_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block { fn move_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
@ -396,7 +367,7 @@ pub impl Datum {
* Schedules this datum for cleanup in `bcx`. The datum * Schedules this datum for cleanup in `bcx`. The datum
* must be an rvalue. */ * must be an rvalue. */
assert self.source.is_rvalue(); assert self.source == RevokeClean;
match self.mode { match self.mode {
ByValue => { ByValue => {
add_clean_temp_immediate(bcx, self.val, self.ty); add_clean_temp_immediate(bcx, self.val, self.ty);
@ -410,10 +381,10 @@ pub impl Datum {
fn cancel_clean(bcx: block) { fn cancel_clean(bcx: block) {
if ty::type_needs_drop(bcx.tcx(), self.ty) { if ty::type_needs_drop(bcx.tcx(), self.ty) {
match self.source { match self.source {
FromRvalue => { RevokeClean => {
revoke_clean(bcx, self.val); revoke_clean(bcx, self.val);
} }
FromLvalue | FromLastUseLvalue => { ZeroMem => {
// Lvalues which potentially need to be dropped // Lvalues which potentially need to be dropped
// must be passed by ref, so that we can zero them // must be passed by ref, so that we can zero them
// out. // out.
@ -444,7 +415,7 @@ pub impl Datum {
ByValue => self, ByValue => self,
ByRef => { ByRef => {
Datum {val: self.to_value_llval(bcx), mode: ByValue, Datum {val: self.to_value_llval(bcx), mode: ByValue,
ty: self.ty, source: FromRvalue} ty: self.ty, source: RevokeClean}
} }
} }
} }
@ -476,7 +447,7 @@ pub impl Datum {
ByRef => self, ByRef => self,
ByValue => { ByValue => {
Datum {val: self.to_ref_llval(bcx), mode: ByRef, Datum {val: self.to_ref_llval(bcx), mode: ByRef,
ty: self.ty, source: FromRvalue} ty: self.ty, source: RevokeClean}
} }
} }
} }
@ -527,7 +498,7 @@ pub impl Datum {
fn GEPi(bcx: block, fn GEPi(bcx: block,
ixs: &[uint], ixs: &[uint],
ty: ty::t, ty: ty::t,
source: DatumSource) source: DatumCleanup)
-> Datum { -> Datum {
let base_val = self.to_ref_llval(bcx); let base_val = self.to_ref_llval(bcx);
Datum { Datum {
@ -618,7 +589,7 @@ pub impl Datum {
let ptr = self.to_value_llval(bcx); let ptr = self.to_value_llval(bcx);
let body = opaque_box_body(bcx, content_ty, ptr); let body = opaque_box_body(bcx, content_ty, ptr);
Datum {val: body, ty: content_ty, mode: ByRef, source: FromLvalue} Datum {val: body, ty: content_ty, mode: ByRef, source: ZeroMem}
} }
fn to_rptr(bcx: block) -> Datum { fn to_rptr(bcx: block) -> Datum {
@ -636,7 +607,7 @@ pub impl Datum {
let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static, let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static,
self.ty); self.ty);
Datum {val: llval, ty: rptr_ty, Datum {val: llval, ty: rptr_ty,
mode: ByValue, source: FromRvalue} mode: ByValue, source: RevokeClean}
} }
fn try_deref( fn try_deref(
@ -701,7 +672,7 @@ pub impl Datum {
val: PointerCast(bcx, self.val, llty), val: PointerCast(bcx, self.val, llty),
ty: ty, ty: ty,
mode: ByRef, mode: ByRef,
source: FromLvalue source: ZeroMem
}), }),
bcx bcx
) )
@ -740,7 +711,7 @@ pub impl Datum {
val: GEPi(bcx, self.val, [0, 0, 0]), val: GEPi(bcx, self.val, [0, 0, 0]),
ty: ty, ty: ty,
mode: ByRef, mode: ByRef,
source: FromLvalue source: ZeroMem
}), }),
bcx bcx
) )
@ -768,7 +739,7 @@ pub impl Datum {
val: lv.to_value_llval(bcx), val: lv.to_value_llval(bcx),
ty: ty, ty: ty,
mode: ByRef, mode: ByRef,
source: FromLvalue // *p is an lvalue source: ZeroMem // *p is an lvalue
} }
} }
} }
@ -841,8 +812,9 @@ pub impl DatumBlock {
self.datum.drop_val(self.bcx) self.datum.drop_val(self.bcx)
} }
fn store_to(action: CopyAction, dst: ValueRef) -> block { fn store_to(id: ast::node_id, action: CopyAction,
self.datum.store_to(self.bcx, action, dst) dst: ValueRef) -> block {
self.datum.store_to(self.bcx, id, action, dst)
} }
fn copy_to(action: CopyAction, dst: ValueRef) -> block { fn copy_to(action: CopyAction, dst: ValueRef) -> block {

View file

@ -789,10 +789,10 @@ pub fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> {
} }
ast_map::node_expr(expr) => { ast_map::node_expr(expr) => {
match /*bad*/copy expr.node { match /*bad*/copy expr.node {
ast::expr_fn(_, decl, _, _) => { ast::expr_fn(_, decl, _) => {
((dbg_cx.names)(~"fn"), decl.output, expr.id) ((dbg_cx.names)(~"fn"), decl.output, expr.id)
} }
ast::expr_fn_block(decl, _, _) => { ast::expr_fn_block(decl, _) => {
((dbg_cx.names)(~"fn"), decl.output, expr.id) ((dbg_cx.names)(~"fn"), decl.output, expr.id)
} }
_ => fcx.ccx.sess.span_bug(expr.span, _ => fcx.ccx.sess.span_bug(expr.span,
@ -818,7 +818,7 @@ pub fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> {
} }
let loc = cx.sess.codemap.lookup_char_pos(sp.lo); let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
let file_node = create_file(cx, loc.file.name).node; let file_node = create_file(cx, copy loc.file.name).node;
let ty_node = if cx.sess.opts.extra_debuginfo { let ty_node = if cx.sess.opts.extra_debuginfo {
match ret_ty.node { match ret_ty.node {
ast::ty_nil => llnull(), ast::ty_nil => llnull(),

View file

@ -12,45 +12,53 @@
# Translation of expressions. # Translation of expressions.
## User's guide ## Recommended entry point
If you wish to translate an expression, there are two basic modes: If you wish to translate an expression, the preferred way to do
so is to use:
1. `trans_into(block, expr, Dest) -> block` expr::trans_into(block, expr, Dest) -> block
2. `trans_to_datum(block, expr) -> DatumBlock`
`trans_into()` is the preferred form to use whenever possible. It This will generate code that evaluates `expr`, storing the result into
evaluates the expression and stores its result into `Dest`, which `Dest`, which must either be the special flag ignore (throw the result
must either be the special flag ignore (throw the result away) or away) or be a pointer to memory of the same type/size as the
be a pointer to memory of the same type/size as the expression. expression. It returns the resulting basic block. This form will
handle all automatic adjustments and moves for you.
Sometimes, though, you just want to evaluate the expression into ## Translation to a datum
some memory location so you can go and inspect it (e.g., a `match`
expression). In that case, `trans_to_datum()` is your friend. It
will evaluate the expression and return a `Datum` describing where
the result is to be found. This function tries to return its
result in the most efficient way possible, without introducing
extra copies or sacrificing information. Therefore, for lvalue
expressions, you always get a by-ref `Datum` in return that points
at the memory for this lvalue (almost, see [1]). For rvalue
expressions, we will return a by-value `Datum` whenever possible,
but it is often necessary to allocate a stack slot, store the
result of the rvalue in there, and then return a pointer to the
slot (see the discussion later on about the different kinds of
rvalues).
## More specific functions In some cases, `trans_into()` is too narrow of an interface.
Generally this occurs either when you know that the result value is
going to be a scalar, or when you need to evaluate the expression into
some memory location so you can go and inspect it (e.g., assignments,
`match` expressions, the `&` operator).
The two functions above are the most general and can handle any In such cases, you want the following function:
situation, but there are a few other functions that are useful
in specific scenarios:
- `trans_lvalue()` is exactly like `trans_to_datum()` but it only trans_to_datum(block, expr) -> DatumBlock
works on lvalues. This is mostly used as an assertion for those
places where only an lvalue is expected. It also guarantees that This function generates code to evaluate the expression and return a
you will get a by-ref Datum back (almost, see [1]). `Datum` describing where the result is to be found. This function
- `trans_local_var()` can be used to trans a ref to a local variable tries to return its result in the most efficient way possible, without
that is not an expression. introducing extra copies or sacrificing information. Therefore, for
lvalue expressions, you always get a by-ref `Datum` in return that
points at the memory for this lvalue (almost, see [1]). For rvalue
expressions, we will return a by-value `Datum` whenever possible, but
it is often necessary to allocate a stack slot, store the result of
the rvalue in there, and then return a pointer to the slot (see the
discussion later on about the different kinds of rvalues).
NB: The `trans_to_datum()` function does perform adjustments, but
since it returns a pointer to the value "in place" it does not handle
any moves that may be relevant. If you are transing an expression
whose result should be moved, you should either use the Datum methods
`move_to()` (for unconditional moves) or `store_to()` (for moves
conditioned on the type of the expression) at some point.
## Translating local variables
`trans_local_var()` can be used to trans a ref to a local variable
that is not an expression. This is needed for captures.
## Ownership and cleanups ## Ownership and cleanups
@ -127,7 +135,6 @@ use middle::trans::datum::*;
use middle::trans::machine; use middle::trans::machine;
use middle::trans::meth; use middle::trans::meth;
use middle::trans::tvec; use middle::trans::tvec;
use middle::ty::MoveValue;
use middle::ty::struct_mutable_fields; use middle::ty::struct_mutable_fields;
use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn}; use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn};
use util::common::indenter; use util::common::indenter;
@ -258,21 +265,70 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
} }
pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block { pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
return match bcx.tcx().adjustments.find(expr.id) { if bcx.tcx().adjustments.contains_key(expr.id) {
None => trans_into_unadjusted(bcx, expr, dest), // use trans_to_datum, which is mildly less efficient but
Some(_) => { // which will perform the adjustments:
// use trans_to_datum, which is mildly less efficient but let datumblock = trans_to_datum(bcx, expr);
// which will perform the adjustments: return match dest {
let datumblock = trans_to_datum(bcx, expr); Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
};
}
let ty = expr_ty(bcx, expr);
debug!("trans_into_unadjusted(expr=%s, dest=%s)",
bcx.expr_to_str(expr),
dest.to_str(bcx.ccx()));
let _indenter = indenter();
debuginfo::update_source_pos(bcx, expr.span);
let dest = {
if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
Ignore
} else {
dest
}
};
let kind = bcx.expr_kind(expr);
debug!("expr kind = %?", kind);
return match kind {
ty::LvalueExpr => {
let datumblock = trans_lvalue_unadjusted(bcx, expr);
match dest { match dest {
Ignore => datumblock.bcx, Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(INIT, lldest) SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
} }
} }
} ty::RvalueDatumExpr => {
let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
match dest {
Ignore => datumblock.drop_val(),
// NB: We always do `move_to()` regardless of the
// moves_map because we're processing an rvalue
SaveIn(lldest) => datumblock.move_to(INIT, lldest)
}
}
ty::RvalueDpsExpr => {
trans_rvalue_dps_unadjusted(bcx, expr, dest)
}
ty::RvalueStmtExpr => {
trans_rvalue_stmt_unadjusted(bcx, expr)
}
};
} }
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock { fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
/*!
*
* Translates an lvalue expression, always yielding a by-ref
* datum. Generally speaking you should call trans_to_datum()
* instead, but sometimes we call trans_lvalue() directly as a
* means of asserting that a particular expression is an lvalue. */
return match bcx.tcx().adjustments.find(expr.id) { return match bcx.tcx().adjustments.find(expr.id) {
None => trans_lvalue_unadjusted(bcx, expr), None => trans_lvalue_unadjusted(bcx, expr),
Some(_) => { Some(_) => {
@ -350,50 +406,6 @@ fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
} }
} }
fn trans_into_unadjusted(bcx: block, expr: @ast::expr, dest: Dest) -> block {
let ty = expr_ty(bcx, expr);
debug!("trans_into_unadjusted(expr=%s, dest=%s)",
bcx.expr_to_str(expr),
dest.to_str(bcx.ccx()));
let _indenter = indenter();
debuginfo::update_source_pos(bcx, expr.span);
let dest = {
if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
Ignore
} else {
dest
}
};
let kind = bcx.expr_kind(expr);
debug!("expr kind = %?", kind);
match kind {
ty::LvalueExpr => {
let datumblock = trans_lvalue_unadjusted(bcx, expr);
match dest {
Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
}
}
ty::RvalueDatumExpr => {
let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
match dest {
Ignore => datumblock.drop_val(),
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
}
}
ty::RvalueDpsExpr => {
return trans_rvalue_dps_unadjusted(bcx, expr, dest);
}
ty::RvalueStmtExpr => {
return trans_rvalue_stmt_unadjusted(bcx, expr);
}
}
}
fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock { fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted"); let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
@ -472,9 +484,12 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
return controlflow::trans_loop(bcx, (*body), opt_label); return controlflow::trans_loop(bcx, (*body), opt_label);
} }
ast::expr_assign(dst, src) => { ast::expr_assign(dst, src) => {
let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src)); let src_datum = unpack_datum!(
let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst)); bcx, trans_to_datum(bcx, src));
return src_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum); let dst_datum = unpack_datum!(
bcx, trans_lvalue(bcx, dst));
return src_datum.store_to_datum(
bcx, src.id, DROP_EXISTING, dst_datum);
} }
ast::expr_swap(dst, src) => { ast::expr_swap(dst, src) => {
let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst)); let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
@ -509,8 +524,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr))); trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
// XXX: This copy is really bad. match expr.node {
match /*bad*/copy expr.node {
ast::expr_paren(e) => { ast::expr_paren(e) => {
return trans_rvalue_dps_unadjusted(bcx, e, dest); return trans_rvalue_dps_unadjusted(bcx, e, dest);
} }
@ -519,14 +533,14 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
bcx.def(expr.id), dest); bcx.def(expr.id), dest);
} }
ast::expr_if(cond, ref thn, els) => { ast::expr_if(cond, ref thn, els) => {
return controlflow::trans_if(bcx, cond, (*thn), els, dest); return controlflow::trans_if(bcx, cond, *thn, els, dest);
} }
ast::expr_match(discr, ref arms) => { ast::expr_match(discr, ref arms) => {
return _match::trans_match(bcx, expr, discr, /*bad*/copy *arms, return _match::trans_match(bcx, expr, discr, /*bad*/copy *arms,
dest); dest);
} }
ast::expr_block(ref blk) => { ast::expr_block(ref blk) => {
return do base::with_scope(bcx, (*blk).info(), return do base::with_scope(bcx, blk.info(),
~"block-expr body") |bcx| { ~"block-expr body") |bcx| {
controlflow::trans_block(bcx, (*blk), dest) controlflow::trans_block(bcx, (*blk), dest)
}; };
@ -535,8 +549,8 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
ast::expr_struct(_, ref fields, base) => { ast::expr_struct(_, ref fields, base) => {
return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest); return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest);
} }
ast::expr_tup(args) => { ast::expr_tup(ref args) => {
return trans_tup(bcx, args, dest); return trans_tup(bcx, *args, dest);
} }
ast::expr_lit(@ast::spanned {node: ast::lit_str(s), _}) => { ast::expr_lit(@ast::spanned {node: ast::lit_str(s), _}) => {
return tvec::trans_lit_str(bcx, expr, s, dest); return tvec::trans_lit_str(bcx, expr, s, dest);
@ -552,15 +566,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
return tvec::trans_fixed_vstore(bcx, expr, expr, dest); return tvec::trans_fixed_vstore(bcx, expr, expr, dest);
} }
// XXX: Bad copy. // XXX: Bad copy.
ast::expr_fn(proto, copy decl, ref body, cap_clause) => { ast::expr_fn(proto, copy decl, ref body) => {
// Don't use this function for anything real. Use the one in // Don't use this function for anything real. Use the one in
// astconv instead. // astconv instead.
return closure::trans_expr_fn(bcx, proto, decl, return closure::trans_expr_fn(bcx, proto, decl,
/*bad*/copy *body, /*bad*/copy *body,
expr.id, expr.id, expr.id, expr.id,
cap_clause, None, dest); None, dest);
} }
ast::expr_fn_block(ref decl, ref body, cap_clause) => { ast::expr_fn_block(ref decl, ref body) => {
let expr_ty = expr_ty(bcx, expr); let expr_ty = expr_ty(bcx, expr);
match ty::get(expr_ty).sty { match ty::get(expr_ty).sty {
ty::ty_fn(ref fn_ty) => { ty::ty_fn(ref fn_ty) => {
@ -570,7 +584,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
return closure::trans_expr_fn( return closure::trans_expr_fn(
bcx, fn_ty.meta.proto, /*bad*/copy *decl, bcx, fn_ty.meta.proto, /*bad*/copy *decl,
/*bad*/copy *body, expr.id, expr.id, /*bad*/copy *body, expr.id, expr.id,
cap_clause, None, dest); None, dest);
} }
_ => { _ => {
bcx.sess().impossible_case( bcx.sess().impossible_case(
@ -582,7 +596,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
match ty::get(expr_ty(bcx, expr)).sty { match ty::get(expr_ty(bcx, expr)).sty {
ty::ty_fn(ref fn_ty) => { ty::ty_fn(ref fn_ty) => {
match blk.node { match blk.node {
ast::expr_fn_block(copy decl, ref body, cap) => { ast::expr_fn_block(copy decl, ref body) => {
return closure::trans_expr_fn( return closure::trans_expr_fn(
bcx, bcx,
fn_ty.meta.proto, fn_ty.meta.proto,
@ -590,7 +604,6 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
/*bad*/copy *body, /*bad*/copy *body,
expr.id, expr.id,
blk.id, blk.id,
cap,
Some(None), Some(None),
dest); dest);
} }
@ -613,26 +626,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
ast::expr_copy(a) => { ast::expr_copy(a) => {
return trans_into(bcx, a, dest); return trans_into(bcx, a, dest);
} }
ast::expr_unary_move(a) => { ast::expr_call(f, ref args, _) => {
if bcx.expr_is_lval(a) {
let datum = unpack_datum!(bcx, trans_to_datum(bcx, a));
return match dest {
Ignore => drop_and_cancel_clean(bcx, datum),
SaveIn(addr) => datum.move_to(bcx, INIT, addr)
};
} else {
return trans_into(bcx, a, dest);
}
}
ast::expr_call(f, args, _) => {
return callee::trans_call( return callee::trans_call(
bcx, expr, f, callee::ArgExprs(args), expr.id, dest); bcx, expr, f, callee::ArgExprs(*args), expr.id, dest);
} }
ast::expr_method_call(rcvr, _, _, args, _) => { ast::expr_method_call(rcvr, _, _, ref args, _) => {
return callee::trans_method_call(bcx, return callee::trans_method_call(bcx,
expr, expr,
rcvr, rcvr,
callee::ArgExprs(args), callee::ArgExprs(*args),
dest); dest);
} }
ast::expr_binary(_, lhs, rhs) => { ast::expr_binary(_, lhs, rhs) => {
@ -727,9 +729,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
/*! /*!
* *
* Translates an lvalue expression, always yielding a by-ref * Translates an lvalue expression, always yielding a by-ref
* datum. Generally speaking you should call trans_to_datum() * datum. Does not apply any adjustments. */
* instead, but sometimes we call trans_lvalue() directly as a
* means of asserting that a particular expression is an lvalue. */
let _icx = bcx.insn_ctxt("trans_lval"); let _icx = bcx.insn_ctxt("trans_lval");
let mut bcx = bcx; let mut bcx = bcx;
@ -752,6 +752,17 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
return DatumBlock {bcx: bcx, datum: unrooted_datum}; return DatumBlock {bcx: bcx, datum: unrooted_datum};
fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock { fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock {
/*!
*
* Translates `expr`. Note that this version generally
* yields an unrooted, unmoved version. Rooting and possible
* moves are dealt with above in trans_lvalue_unadjusted().
*
* One exception is if `expr` refers to a local variable,
* in which case the source may already be FromMovedLvalue
* if appropriate.
*/
let mut bcx = bcx; let mut bcx = bcx;
match expr.node { match expr.node {
@ -762,7 +773,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
return trans_def_lvalue(bcx, expr, bcx.def(expr.id)); return trans_def_lvalue(bcx, expr, bcx.def(expr.id));
} }
ast::expr_field(base, ident, _) => { ast::expr_field(base, ident, _) => {
return trans_rec_field(bcx, base, ident, expr.id); return trans_rec_field(bcx, base, ident);
} }
ast::expr_index(base, idx) => { ast::expr_index(base, idx) => {
return trans_index(bcx, expr, base, idx); return trans_index(bcx, expr, base, idx);
@ -779,52 +790,152 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
} }
} }
} }
}
fn trans_def_lvalue(bcx: block, fn trans_rec_field(bcx: block,
ref_expr: @ast::expr, base: @ast::expr,
def: ast::def) field: ast::ident) -> DatumBlock {
-> DatumBlock { /*!
let _icx = bcx.insn_ctxt("trans_def_lvalue"); *
let ccx = bcx.ccx(); * Translates `base.field`. Note that this version always
match def { * yields an unrooted, unmoved version. Rooting and possible
ast::def_const(did) => { * moves are dealt with above in trans_lvalue_unadjusted().
let const_ty = expr_ty(bcx, ref_expr); */
let val = if did.crate == ast::local_crate {
// The LLVM global has the type of its initializer, let mut bcx = bcx;
// which may not be equal to the enum's type for let _icx = bcx.insn_ctxt("trans_rec_field");
// non-C-like enums.
PointerCast(bcx, base::get_item_val(ccx, did.node), let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
T_ptr(type_of(bcx.ccx(), const_ty))) do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| {
} else { let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
base::trans_external_path(ccx, did, const_ty)
};
DatumBlock { DatumBlock {
bcx: bcx, datum: base_datum.GEPi(bcx,
datum: Datum {val: val, [0u, 0u, ix],
ty: const_ty, field_tys[ix].mt.ty,
mode: ByRef, ZeroMem),
source: FromLvalue} bcx: bcx
} }
} }
_ => { }
DatumBlock {
bcx: bcx, fn trans_index(bcx: block,
datum: trans_local_var(bcx, def, Some(ref_expr.id)) index_expr: @ast::expr,
base: @ast::expr,
idx: @ast::expr) -> DatumBlock {
/*!
*
* Translates `base[idx]`. Note that this version always
* yields an unrooted, unmoved version. Rooting and possible
* moves are dealt with above in trans_lvalue_unadjusted().
*/
let _icx = bcx.insn_ctxt("trans_index");
let ccx = bcx.ccx();
let base_ty = expr_ty(bcx, base);
let mut bcx = bcx;
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
// Translate index expression and cast to a suitable LLVM integer.
// Rust is less strict than LLVM in this regard.
let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val));
let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type);
let ix_val = {
if ix_size < int_size {
if ty::type_is_signed(expr_ty(bcx, idx)) {
SExt(bcx, ix_val, ccx.int_type)
} else { ZExt(bcx, ix_val, ccx.int_type) }
} else if ix_size > int_size {
Trunc(bcx, ix_val, ccx.int_type)
} else {
ix_val
}
};
let vt = tvec::vec_types(bcx, base_datum.ty);
base::maybe_name_value(bcx.ccx(), vt.llunit_size, ~"unit_sz");
let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix");
let mut (base, len) = base_datum.get_base_and_len(bcx);
if ty::type_is_str(base_ty) {
// acccount for null terminator in the case of string
len = Sub(bcx, len, C_uint(bcx.ccx(), 1u));
}
debug!("trans_index: base %s", val_str(bcx.ccx().tn, base));
debug!("trans_index: len %s", val_str(bcx.ccx().tn, len));
let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len);
let bcx = do with_cond(bcx, bounds_check) |bcx| {
let unscaled_len = UDiv(bcx, len, vt.llunit_size);
controlflow::trans_fail_bounds_check(bcx, index_expr.span,
ix_val, unscaled_len)
};
let elt = InBoundsGEP(bcx, base, ~[ix_val]);
let elt = PointerCast(bcx, elt, T_ptr(vt.llunit_ty));
return DatumBlock {
bcx: bcx,
datum: Datum {val: elt,
ty: vt.unit_ty,
mode: ByRef,
source: ZeroMem}
};
}
fn trans_def_lvalue(bcx: block,
ref_expr: @ast::expr,
def: ast::def)
-> DatumBlock
{
/*!
*
* Translates a reference to a path. Note that this version
* generally yields an unrooted, unmoved version. Rooting and
* possible moves are dealt with above in
* trans_lvalue_unadjusted(), with the caveat that local variables
* may already be in move mode.
*/
let _icx = bcx.insn_ctxt("trans_def_lvalue");
let ccx = bcx.ccx();
match def {
ast::def_const(did) => {
let const_ty = expr_ty(bcx, ref_expr);
let val = if did.crate == ast::local_crate {
// The LLVM global has the type of its initializer,
// which may not be equal to the enum's type for
// non-C-like enums.
PointerCast(bcx, base::get_item_val(ccx, did.node),
T_ptr(type_of(bcx.ccx(), const_ty)))
} else {
base::trans_external_path(ccx, did, const_ty)
};
DatumBlock {
bcx: bcx,
datum: Datum {val: val,
ty: const_ty,
mode: ByRef,
source: ZeroMem}
}
}
_ => {
DatumBlock {
bcx: bcx,
datum: trans_local_var(bcx, def)
}
} }
} }
} }
} }
pub fn trans_local_var(bcx: block, pub fn trans_local_var(bcx: block, def: ast::def) -> Datum {
def: ast::def,
expr_id_opt: Option<ast::node_id>)
-> Datum {
let _icx = bcx.insn_ctxt("trans_local_var"); let _icx = bcx.insn_ctxt("trans_local_var");
return match def { return match def {
ast::def_upvar(nid, _, _, _) => { ast::def_upvar(nid, _, _, _) => {
// Can't move upvars, so this is never a FromLvalueLastUse. // Can't move upvars, so this is never a ZeroMemLastUse.
let local_ty = node_id_type(bcx, nid); let local_ty = node_id_type(bcx, nid);
match bcx.fcx.llupvars.find(nid) { match bcx.fcx.llupvars.find(nid) {
Some(val) => { Some(val) => {
@ -832,7 +943,7 @@ pub fn trans_local_var(bcx: block,
val: val, val: val,
ty: local_ty, ty: local_ty,
mode: ByRef, mode: ByRef,
source: FromLvalue source: ZeroMem
} }
} }
None => { None => {
@ -842,10 +953,10 @@ pub fn trans_local_var(bcx: block,
} }
} }
ast::def_arg(nid, _, _) => { ast::def_arg(nid, _, _) => {
take_local(bcx, bcx.fcx.llargs, nid, expr_id_opt) take_local(bcx, bcx.fcx.llargs, nid)
} }
ast::def_local(nid, _) | ast::def_binding(nid, _) => { ast::def_local(nid, _) | ast::def_binding(nid, _) => {
take_local(bcx, bcx.fcx.lllocals, nid, expr_id_opt) take_local(bcx, bcx.fcx.lllocals, nid)
} }
ast::def_self(nid, _) => { ast::def_self(nid, _) => {
let self_info: ValSelfData = match bcx.fcx.llself { let self_info: ValSelfData = match bcx.fcx.llself {
@ -867,7 +978,7 @@ pub fn trans_local_var(bcx: block,
val: casted_val, val: casted_val,
ty: self_info.t, ty: self_info.t,
mode: ByRef, mode: ByRef,
source: source_from_opt_lvalue_type(bcx.tcx(), expr_id_opt) source: ZeroMem
} }
} }
_ => { _ => {
@ -878,8 +989,7 @@ pub fn trans_local_var(bcx: block,
fn take_local(bcx: block, fn take_local(bcx: block,
table: HashMap<ast::node_id, local_val>, table: HashMap<ast::node_id, local_val>,
nid: ast::node_id, nid: ast::node_id) -> Datum {
expr_id_opt: Option<ast::node_id>) -> Datum {
let (v, mode) = match table.find(nid) { let (v, mode) = match table.find(nid) {
Some(local_mem(v)) => (v, ByRef), Some(local_mem(v)) => (v, ByRef),
Some(local_imm(v)) => (v, ByValue), Some(local_imm(v)) => (v, ByValue),
@ -897,7 +1007,7 @@ pub fn trans_local_var(bcx: block,
val: v, val: v,
ty: ty, ty: ty,
mode: mode, mode: mode,
source: source_from_opt_lvalue_type(bcx.tcx(), expr_id_opt) source: ZeroMem
} }
} }
} }
@ -981,102 +1091,6 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
} }
} }
fn trans_rec_field(bcx: block,
base: @ast::expr,
field: ast::ident,
expr_id: ast::node_id) -> DatumBlock {
let mut bcx = bcx;
let _icx = bcx.insn_ctxt("trans_rec_field");
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| {
let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
DatumBlock {
datum: base_datum.GEPi(bcx,
[0u, 0u, ix],
field_tys[ix].mt.ty,
source_from_opt_lvalue_type(
bcx.tcx(), Some(expr_id))),
bcx: bcx
}
}
}
fn source_from_opt_lvalue_type(tcx: ty::ctxt,
expr_id_opt: Option<ast::node_id>)
-> DatumSource {
match expr_id_opt {
None => FromLvalue,
Some(expr_id) => {
match tcx.value_modes.find(expr_id) {
Some(MoveValue) => FromLastUseLvalue,
Some(_) | None => FromLvalue,
}
}
}
}
fn trans_index(bcx: block,
index_expr: @ast::expr,
base: @ast::expr,
idx: @ast::expr) -> DatumBlock {
let _icx = bcx.insn_ctxt("trans_index");
let ccx = bcx.ccx();
let base_ty = expr_ty(bcx, base);
let mut bcx = bcx;
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
// Translate index expression and cast to a suitable LLVM integer.
// Rust is less strict than LLVM in this regard.
let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val));
let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type);
let ix_val = {
if ix_size < int_size {
if ty::type_is_signed(expr_ty(bcx, idx)) {
SExt(bcx, ix_val, ccx.int_type)
} else { ZExt(bcx, ix_val, ccx.int_type) }
} else if ix_size > int_size {
Trunc(bcx, ix_val, ccx.int_type)
} else {
ix_val
}
};
let vt = tvec::vec_types(bcx, base_datum.ty);
base::maybe_name_value(bcx.ccx(), vt.llunit_size, ~"unit_sz");
let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix");
let mut (base, len) = base_datum.get_base_and_len(bcx);
if ty::type_is_str(base_ty) {
// acccount for null terminator in the case of string
len = Sub(bcx, len, C_uint(bcx.ccx(), 1u));
}
debug!("trans_index: base %s", val_str(bcx.ccx().tn, base));
debug!("trans_index: len %s", val_str(bcx.ccx().tn, len));
let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len);
let bcx = do with_cond(bcx, bounds_check) |bcx| {
let unscaled_len = UDiv(bcx, len, vt.llunit_size);
controlflow::trans_fail_bounds_check(bcx, index_expr.span,
ix_val, unscaled_len)
};
let elt = InBoundsGEP(bcx, base, ~[ix_val]);
let elt = PointerCast(bcx, elt, T_ptr(vt.llunit_ty));
return DatumBlock {
bcx: bcx,
datum: Datum {val: elt,
ty: vt.unit_ty,
mode: ByRef,
source: source_from_opt_lvalue_type(
bcx.tcx(), Some(index_expr.id))}
};
}
fn trans_rec_or_struct(bcx: block, fn trans_rec_or_struct(bcx: block,
fields: &[ast::field], fields: &[ast::field],
base: Option<@ast::expr>, base: Option<@ast::expr>,
@ -1158,7 +1172,7 @@ fn trans_rec_or_struct(bcx: block,
let base_datum = unpack_datum!( let base_datum = unpack_datum!(
bcx, trans_to_datum(bcx, *base_expr)); bcx, trans_to_datum(bcx, *base_expr));
// Copy over inherited fields // Copy/move over inherited fields
for field_tys.eachi |i, field_ty| { for field_tys.eachi |i, field_ty| {
if !fields.any(|f| f.node.ident == field_ty.ident) { if !fields.any(|f| f.node.ident == field_ty.ident) {
let dest = GEPi(bcx, addr, struct_field(i)); let dest = GEPi(bcx, addr, struct_field(i));
@ -1166,8 +1180,8 @@ fn trans_rec_or_struct(bcx: block,
base_datum.GEPi(bcx, base_datum.GEPi(bcx,
struct_field(i), struct_field(i),
field_ty.mt.ty, field_ty.mt.ty,
FromLvalue); ZeroMem);
bcx = base_field.store_to(bcx, INIT, dest); bcx = base_field.store_to(bcx, base_expr.id, INIT, dest);
} }
} }
} }
@ -1187,7 +1201,7 @@ fn trans_rec_or_struct(bcx: block,
} }
} }
fn trans_tup(bcx: block, elts: ~[@ast::expr], dest: Dest) -> block { fn trans_tup(bcx: block, elts: &[@ast::expr], dest: Dest) -> block {
let _icx = bcx.insn_ctxt("trans_tup"); let _icx = bcx.insn_ctxt("trans_tup");
let mut bcx = bcx; let mut bcx = bcx;
let addr = match dest { let addr = match dest {
@ -1642,7 +1656,7 @@ fn trans_assign_op(bcx: block,
trans_eager_binop( trans_eager_binop(
bcx, expr, dst_datum.ty, op, bcx, expr, dst_datum.ty, op,
&dst_datum, &src_datum)); &dst_datum, &src_datum));
return result_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum); return result_datum.copy_to_datum(bcx, DROP_EXISTING, dst_datum);
} }
fn shorten(+x: ~str) -> ~str { fn shorten(+x: ~str) -> ~str {

View file

@ -69,7 +69,7 @@ fn c_arg_and_ret_lltys(ccx: @crate_ctxt,
ty::ty_fn(ref fn_ty) => { ty::ty_fn(ref fn_ty) => {
let llargtys = type_of_explicit_args( let llargtys = type_of_explicit_args(
ccx, ccx,
/*bad*/copy fn_ty.sig.inputs); fn_ty.sig.inputs);
let llretty = type_of::type_of(ccx, fn_ty.sig.output); let llretty = type_of::type_of(ccx, fn_ty.sig.output);
(llargtys, llretty, fn_ty.sig.output) (llargtys, llretty, fn_ty.sig.output)
} }
@ -441,7 +441,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
// //
// - the datum will be by ref if the value is non-immediate; // - the datum will be by ref if the value is non-immediate;
// //
// - the datum has a FromRvalue source because, that way, // - the datum has a RevokeClean source because, that way,
// the `move_to()` method does not feel compelled to // the `move_to()` method does not feel compelled to
// zero out the memory where the datum resides. Zeroing // zero out the memory where the datum resides. Zeroing
// is not necessary since, for intrinsics, there is no // is not necessary since, for intrinsics, there is no
@ -449,7 +449,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
let tp_ty = substs.tys[0]; let tp_ty = substs.tys[0];
let mode = appropriate_mode(tp_ty); let mode = appropriate_mode(tp_ty);
let src = Datum {val: get_param(decl, first_real_arg + 1u), let src = Datum {val: get_param(decl, first_real_arg + 1u),
ty: tp_ty, mode: mode, source: FromRvalue}; ty: tp_ty, mode: mode, source: RevokeClean};
bcx = src.move_to(bcx, DROP_EXISTING, bcx = src.move_to(bcx, DROP_EXISTING,
get_param(decl, first_real_arg)); get_param(decl, first_real_arg));
} }
@ -458,7 +458,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
let tp_ty = substs.tys[0]; let tp_ty = substs.tys[0];
let mode = appropriate_mode(tp_ty); let mode = appropriate_mode(tp_ty);
let src = Datum {val: get_param(decl, first_real_arg + 1u), let src = Datum {val: get_param(decl, first_real_arg + 1u),
ty: tp_ty, mode: mode, source: FromRvalue}; ty: tp_ty, mode: mode, source: RevokeClean};
bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg)); bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
} }
~"min_align_of" => { ~"min_align_of" => {
@ -546,16 +546,17 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
onceness: ast::Many, onceness: ast::Many,
region: ty::re_bound(ty::br_anon(0)), region: ty::re_bound(ty::br_anon(0)),
bounds: @~[]}, bounds: @~[]},
sig: FnSig {inputs: ~[arg {mode: ast::expl(ast::by_val), sig: FnSig {inputs: ~[arg {mode: ast::expl(ast::by_copy),
ty: star_u8}], ty: star_u8}],
output: ty::mk_nil(bcx.tcx())} output: ty::mk_nil(bcx.tcx())}
}); });
let datum = Datum {val: get_param(decl, first_real_arg), let datum = Datum {val: get_param(decl, first_real_arg),
mode: ByRef, ty: fty, source: FromLvalue}; mode: ByRef, ty: fty, source: ZeroMem};
let arg_vals = ~[frameaddress_val];
bcx = trans_call_inner( bcx = trans_call_inner(
bcx, None, fty, ty::mk_nil(bcx.tcx()), bcx, None, fty, ty::mk_nil(bcx.tcx()),
|bcx| Callee {bcx: bcx, data: Closure(datum)}, |bcx| Callee {bcx: bcx, data: Closure(datum)},
ArgVals(~[frameaddress_val]), Ignore, DontAutorefArg); ArgVals(arg_vals), Ignore, DontAutorefArg);
} }
~"morestack_addr" => { ~"morestack_addr" => {
// XXX This is a hack to grab the address of this particular // XXX This is a hack to grab the address of this particular

View file

@ -232,7 +232,6 @@ pub impl reflector {
ast::expl(e) => match e { ast::expl(e) => match e {
ast::by_ref => 1u, ast::by_ref => 1u,
ast::by_val => 2u, ast::by_val => 2u,
ast::by_move => 4u,
ast::by_copy => 5u ast::by_copy => 5u
} }
}; };

View file

@ -24,7 +24,7 @@ pub fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef {
let llty = type_of(ccx, arg.ty); let llty = type_of(ccx, arg.ty);
match ty::resolved_mode(ccx.tcx, arg.mode) { match ty::resolved_mode(ccx.tcx, arg.mode) {
ast::by_val => llty, ast::by_val => llty,
ast::by_copy | ast::by_move => { ast::by_copy => {
if ty::type_is_immediate(arg.ty) { if ty::type_is_immediate(arg.ty) {
llty llty
} else { } else {
@ -35,12 +35,12 @@ pub fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef {
} }
} }
pub fn type_of_explicit_args(ccx: @crate_ctxt, inputs: ~[ty::arg]) pub fn type_of_explicit_args(ccx: @crate_ctxt,
-> ~[TypeRef] { inputs: &[ty::arg]) -> ~[TypeRef] {
inputs.map(|arg| type_of_explicit_arg(ccx, *arg)) inputs.map(|arg| type_of_explicit_arg(ccx, *arg))
} }
pub fn type_of_fn(cx: @crate_ctxt, inputs: ~[ty::arg], pub fn type_of_fn(cx: @crate_ctxt, inputs: &[ty::arg],
output: ty::t) -> TypeRef { output: ty::t) -> TypeRef {
unsafe { unsafe {
let mut atys: ~[TypeRef] = ~[]; let mut atys: ~[TypeRef] = ~[];

View file

@ -72,7 +72,7 @@ pub fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
ty::ty_fn(ref fn_ty) => { ty::ty_fn(ref fn_ty) => {
for vec::each(fn_ty.sig.inputs) |arg| { for vec::each(fn_ty.sig.inputs) |arg| {
match ty::resolved_mode(ccx.tcx, arg.mode) { match ty::resolved_mode(ccx.tcx, arg.mode) {
by_val | by_move | by_copy => { by_val | by_copy => {
type_needs(cx, use_repr, arg.ty); type_needs(cx, use_repr, arg.ty);
} }
by_ref => {} by_ref => {}
@ -255,7 +255,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) {
expr_rec(_, _) | expr_struct(*) | expr_tup(_) | expr_rec(_, _) | expr_struct(*) | expr_tup(_) |
expr_unary(box(_), _) | expr_unary(uniq(_), _) | expr_unary(box(_), _) | expr_unary(uniq(_), _) |
expr_binary(add, _, _) | expr_binary(add, _, _) |
expr_copy(_) | expr_unary_move(_) | expr_repeat(*) => { expr_copy(_) | expr_repeat(*) => {
node_type_needs(cx, use_repr, e.id); node_type_needs(cx, use_repr, e.id);
} }
expr_cast(base, _) => { expr_cast(base, _) => {
@ -316,7 +316,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) {
ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id)) ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id))
) |a| { ) |a| {
match a.mode { match a.mode {
expl(by_move) | expl(by_copy) | expl(by_val) => { expl(by_copy) | expl(by_val) => {
type_needs(cx, use_repr, a.ty); type_needs(cx, use_repr, a.ty);
} }
_ => () _ => ()
@ -330,7 +330,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) {
for ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, for ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx,
e.callee_id)).each |a| { e.callee_id)).each |a| {
match a.mode { match a.mode {
expl(by_move) | expl(by_copy) | expl(by_val) => { expl(by_copy) | expl(by_val) => {
type_needs(cx, use_repr, a.ty); type_needs(cx, use_repr, a.ty);
} }
_ => () _ => ()

View file

@ -99,15 +99,6 @@ pub struct field_ty {
mutability: ast::struct_mutability, mutability: ast::struct_mutability,
} }
/// How an lvalue is to be used.
#[auto_encode]
#[auto_decode]
pub enum ValueMode {
ReadValue, // Non-destructively read the value; do not copy or move.
CopyValue, // Copy the value.
MoveValue, // Move the value.
}
// Contains information needed to resolve types and (in the future) look up // Contains information needed to resolve types and (in the future) look up
// the types of AST nodes. // the types of AST nodes.
#[deriving_eq] #[deriving_eq]
@ -295,9 +286,6 @@ struct ctxt_ {
// A method will be in this list if and only if it is a destructor. // A method will be in this list if and only if it is a destructor.
destructors: HashMap<ast::def_id, ()>, destructors: HashMap<ast::def_id, ()>,
// Records the value mode (read, copy, or move) for every value.
value_modes: HashMap<ast::node_id, ValueMode>,
// Maps a trait onto a mapping from self-ty to impl // Maps a trait onto a mapping from self-ty to impl
trait_impls: HashMap<ast::def_id, HashMap<t, @Impl>> trait_impls: HashMap<ast::def_id, HashMap<t, @Impl>>
} }
@ -875,7 +863,6 @@ pub fn mk_ctxt(s: session::Session,
supertraits: HashMap(), supertraits: HashMap(),
destructor_for_type: HashMap(), destructor_for_type: HashMap(),
destructors: HashMap(), destructors: HashMap(),
value_modes: HashMap(),
trait_impls: HashMap() trait_impls: HashMap()
} }
} }
@ -2170,7 +2157,14 @@ pub fn type_kind_ext(cx: ctxt, ty: t, allow_ty_var: bool) -> Kind {
} }
ty_param(p) => { ty_param(p) => {
param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node)) // We only ever ask for the kind of types that are defined in the
// current crate; therefore, the only type parameters that could be
// in scope are those defined in the current crate. If this
// assertion failures, it is likely because of a failure in the
// cross-crate inlining code to translate a def-id.
assert p.def_id.crate == ast::local_crate;
param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node))
} }
// self is a special type parameter that can only appear in traits; it // self is a special type parameter that can only appear in traits; it
@ -2925,7 +2919,12 @@ pub fn pat_ty(cx: ctxt, pat: @ast::pat) -> t {
// Returns the type of an expression as a monotype. // Returns the type of an expression as a monotype.
// //
// NB: This type doesn't provide type parameter substitutions; e.g. if you // NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
// some cases, we insert `AutoAdjustment` annotations such as auto-deref or
// auto-ref. The type returned by this function does not consider such
// adjustments. See `expr_ty_adjusted()` instead.
//
// NB (2): This type doesn't provide type parameter substitutions; e.g. if you
// ask for the type of "id" in "id(3)", it will return "fn(&int) -> int" // ask for the type of "id" in "id(3)", it will return "fn(&int) -> int"
// instead of "fn(t) -> T with T = int". If this isn't what you want, see // instead of "fn(t) -> T with T = int". If this isn't what you want, see
// expr_ty_params_and_ty() below. // expr_ty_params_and_ty() below.
@ -2933,6 +2932,112 @@ pub fn expr_ty(cx: ctxt, expr: @ast::expr) -> t {
return node_id_to_type(cx, expr.id); return node_id_to_type(cx, expr.id);
} }
pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
/*!
*
* Returns the type of `expr`, considering any `AutoAdjustment`
* entry recorded for that expression.
*
* It would almost certainly be better to store the adjusted ty in with
* the `AutoAdjustment`, but I opted not to do this because it would
* require serializing and deserializing the type and, although that's not
* hard to do, I just hate that code so much I didn't want to touch it
* unless it was to fix it properly, which seemed a distraction from the
* task at hand! -nmatsakis
*/
let unadjusted_ty = expr_ty(cx, expr);
return match cx.adjustments.find(expr.id) {
None => unadjusted_ty,
Some(adj) => {
let mut adjusted_ty = unadjusted_ty;
for uint::range(0, adj.autoderefs) |i| {
match ty::deref(cx, adjusted_ty, true) {
Some(mt) => { adjusted_ty = mt.ty; }
None => {
cx.sess.span_bug(
expr.span,
fmt!("The %uth autoderef failed: %s",
i, ty_to_str(cx,
adjusted_ty)));
}
}
}
match adj.autoref {
None => adjusted_ty,
Some(ref autoref) => {
match autoref.kind {
AutoPtr => {
mk_rptr(cx, autoref.region,
mt {ty: adjusted_ty,
mutbl: autoref.mutbl})
}
AutoBorrowVec => {
borrow_vec(cx, expr, autoref, adjusted_ty)
}
AutoBorrowVecRef => {
adjusted_ty = borrow_vec(cx, expr, autoref,
adjusted_ty);
mk_rptr(cx, autoref.region,
mt {ty: adjusted_ty, mutbl: ast::m_imm})
}
AutoBorrowFn => {
borrow_fn(cx, expr, autoref, adjusted_ty)
}
}
}
}
}
};
fn borrow_vec(cx: ctxt, expr: @ast::expr,
autoref: &AutoRef, ty: ty::t) -> ty::t {
match get(ty).sty {
ty_evec(mt, _) => {
ty::mk_evec(cx, mt {ty: mt.ty, mutbl: autoref.mutbl},
vstore_slice(autoref.region))
}
ty_estr(_) => {
ty::mk_estr(cx, vstore_slice(autoref.region))
}
ref s => {
cx.sess.span_bug(
expr.span,
fmt!("borrow-vec associated with bad sty: %?",
s));
}
}
}
fn borrow_fn(cx: ctxt, expr: @ast::expr,
autoref: &AutoRef, ty: ty::t) -> ty::t {
match get(ty).sty {
ty_fn(ref fty) => {
ty::mk_fn(cx, FnTyBase {meta: FnMeta {proto: ProtoBorrowed,
region: autoref.region,
..copy fty.meta},
sig: copy fty.sig})
}
ref s => {
cx.sess.span_bug(
expr.span,
fmt!("borrow-fn associated with bad sty: %?",
s));
}
}
}
}
pub fn expr_ty_params_and_ty(cx: ctxt, pub fn expr_ty_params_and_ty(cx: ctxt,
expr: @ast::expr) expr: @ast::expr)
-> {params: ~[t], ty: t} { -> {params: ~[t], ty: t} {
@ -3060,7 +3165,6 @@ pub fn expr_kind(tcx: ctxt,
ast::expr_do_body(*) | ast::expr_do_body(*) |
ast::expr_block(*) | ast::expr_block(*) |
ast::expr_copy(*) | ast::expr_copy(*) |
ast::expr_unary_move(*) |
ast::expr_repeat(*) | ast::expr_repeat(*) |
ast::expr_lit(@ast::spanned {node: lit_str(_), _}) | ast::expr_lit(@ast::spanned {node: lit_str(_), _}) |
ast::expr_vstore(_, ast::expr_vstore_slice) | ast::expr_vstore(_, ast::expr_vstore_slice) |

View file

@ -381,7 +381,7 @@ pub fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
demand::eqtype(fcx, pat.span, region_ty, typ); demand::eqtype(fcx, pat.span, region_ty, typ);
} }
// otherwise the type of x is the expected type T // otherwise the type of x is the expected type T
ast::bind_by_value | ast::bind_by_move | ast::bind_infer => { ast::bind_by_copy | ast::bind_infer => {
demand::eqtype(fcx, pat.span, expected, typ); demand::eqtype(fcx, pat.span, expected, typ);
} }
} }

View file

@ -78,7 +78,6 @@ type parameter).
use core::prelude::*; use core::prelude::*;
use middle::capture;
use middle::const_eval; use middle::const_eval;
use middle::pat_util::pat_id_map; use middle::pat_util::pat_id_map;
use middle::pat_util; use middle::pat_util;
@ -1454,7 +1453,7 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
match ast_util::binop_to_method_name(op) { match ast_util::binop_to_method_name(op) {
Some(ref name) => { Some(ref name) => {
match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t, match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t,
fcx.tcx().sess.ident_of((*name)), fcx.tcx().sess.ident_of(copy *name),
~[rhs], DoDerefArgs) { ~[rhs], DoDerefArgs) {
Some(pair) => return pair, Some(pair) => return pair,
_ => () _ => ()
@ -1488,9 +1487,11 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str, fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
ex: @ast::expr, ex: @ast::expr,
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t { rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
match lookup_op_method(fcx, ex, rhs_expr, rhs_t, match lookup_op_method(
fcx.tcx().sess.ident_of(mname), ~[], fcx, ex, rhs_expr, rhs_t,
DontDerefArgs) { fcx.tcx().sess.ident_of(/*bad*/ copy mname), ~[],
DontDerefArgs)
{
Some((ret_ty, _)) => ret_ty, Some((ret_ty, _)) => ret_ty,
_ => { _ => {
fcx.type_error_message(ex.span, |actual| { fcx.type_error_message(ex.span, |actual| {
@ -2132,7 +2133,7 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
bot = check_expr_has_type(fcx, e, ty::mk_bool(tcx)); bot = check_expr_has_type(fcx, e, ty::mk_bool(tcx));
fcx.write_nil(id); fcx.write_nil(id);
} }
ast::expr_copy(a) | ast::expr_unary_move(a) => { ast::expr_copy(a) => {
bot = check_expr_with_opt_hint(fcx, a, expected); bot = check_expr_with_opt_hint(fcx, a, expected);
fcx.write_ty(id, fcx.expr_ty(a)); fcx.write_ty(id, fcx.expr_ty(a));
} }
@ -2163,15 +2164,13 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
ast::expr_match(discrim, ref arms) => { ast::expr_match(discrim, ref arms) => {
bot = _match::check_match(fcx, expr, discrim, (/*bad*/copy *arms)); bot = _match::check_match(fcx, expr, discrim, (/*bad*/copy *arms));
} }
ast::expr_fn(proto, ref decl, ref body, cap_clause) => { ast::expr_fn(proto, ref decl, ref body) => {
check_expr_fn(fcx, expr, Some(proto), check_expr_fn(fcx, expr, Some(proto),
decl, (*body), Vanilla, expected); decl, (*body), Vanilla, expected);
capture::check_capture_clause(tcx, expr.id, cap_clause);
} }
ast::expr_fn_block(ref decl, ref body, cap_clause) => { ast::expr_fn_block(ref decl, ref body) => {
check_expr_fn(fcx, expr, None, check_expr_fn(fcx, expr, None,
decl, (*body), Vanilla, expected); decl, (*body), Vanilla, expected);
capture::check_capture_clause(tcx, expr.id, cap_clause);
} }
ast::expr_loop_body(b) => { ast::expr_loop_body(b) => {
// a loop body is the special argument to a `for` loop. We know that // a loop body is the special argument to a `for` loop. We know that
@ -2234,7 +2233,7 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
} }
}; };
match b.node { match b.node {
ast::expr_fn_block(ref decl, ref body, cap_clause) => { ast::expr_fn_block(ref decl, ref body) => {
// If an error occurred, we pretend this isn't a for // If an error occurred, we pretend this isn't a for
// loop, so as to assign types to all nodes while also // loop, so as to assign types to all nodes while also
// propagating ty_err throughout so as to suppress // propagating ty_err throughout so as to suppress
@ -2246,7 +2245,6 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
check_expr_fn(fcx, b, None, check_expr_fn(fcx, b, None,
decl, *body, fn_kind, Some(inner_ty)); decl, *body, fn_kind, Some(inner_ty));
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
capture::check_capture_clause(tcx, b.id, cap_clause);
} }
// argh // argh
_ => fail ~"expr_fn_block" _ => fail ~"expr_fn_block"
@ -2283,11 +2281,10 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
} }
}; };
match b.node { match b.node {
ast::expr_fn_block(ref decl, ref body, cap_clause) => { ast::expr_fn_block(ref decl, ref body) => {
check_expr_fn(fcx, b, None, check_expr_fn(fcx, b, None,
decl, *body, DoBlock, Some(inner_ty)); decl, *body, DoBlock, Some(inner_ty));
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
capture::check_capture_clause(tcx, b.id, cap_clause);
} }
// argh // argh
_ => fail ~"expected fn ty" _ => fail ~"expected fn ty"
@ -3052,7 +3049,7 @@ pub fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
~"size_of" | ~"size_of" |
~"pref_align_of" | ~"min_align_of" => (1u, ~[], ty::mk_uint(ccx.tcx)), ~"pref_align_of" | ~"min_align_of" => (1u, ~[], ty::mk_uint(ccx.tcx)),
~"init" => (1u, ~[], param(ccx, 0u)), ~"init" => (1u, ~[], param(ccx, 0u)),
~"forget" => (1u, ~[arg(ast::by_move, param(ccx, 0u))], ~"forget" => (1u, ~[arg(ast::by_copy, param(ccx, 0u))],
ty::mk_nil(tcx)), ty::mk_nil(tcx)),
~"reinterpret_cast" => (2u, ~[arg(ast::by_ref, param(ccx, 0u))], ~"reinterpret_cast" => (2u, ~[arg(ast::by_ref, param(ccx, 0u))],
param(ccx, 1u)), param(ccx, 1u)),
@ -3062,7 +3059,7 @@ pub fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
(1u, ~[arg(ast::by_copy, (1u, ~[arg(ast::by_copy,
ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)), ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)),
param(ccx, 0u))), param(ccx, 0u))),
arg(ast::by_move, param(ccx, 0u))], arg(ast::by_copy, param(ccx, 0u))],
ty::mk_nil(tcx)) ty::mk_nil(tcx))
} }
~"needs_drop" => (1u, ~[], ty::mk_bool(tcx)), ~"needs_drop" => (1u, ~[], ty::mk_bool(tcx)),

View file

@ -710,7 +710,6 @@ pub mod guarantor {
ast::expr_do_body(*) | ast::expr_do_body(*) |
ast::expr_block(*) | ast::expr_block(*) |
ast::expr_copy(*) | ast::expr_copy(*) |
ast::expr_unary_move(*) |
ast::expr_repeat(*) | ast::expr_repeat(*) |
ast::expr_vec(*) => { ast::expr_vec(*) => {
assert !ty::expr_is_lval( assert !ty::expr_is_lval(

View file

@ -606,10 +606,10 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
ty::ty_uniq(mt) => { ty::ty_uniq(mt) => {
// Ensure that the trait vstore and the pointer // Ensure that the trait vstore and the pointer
// type match. // type match.
match (ty::get(ty).sty, vstore) { match (&ty::get(ty).sty, vstore) {
(ty::ty_box(_), ty::vstore_box) | (&ty::ty_box(_), ty::vstore_box) |
(ty::ty_uniq(_), ty::vstore_uniq) | (&ty::ty_uniq(_), ty::vstore_uniq) |
(ty::ty_rptr(*), ty::vstore_slice(*)) => { (&ty::ty_rptr(*), ty::vstore_slice(*)) => {
let location_info = let location_info =
&location_info_for_expr(ex); &location_info_for_expr(ex);
let vtable_opt = let vtable_opt =
@ -634,8 +634,8 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
// Now, if this is &trait, we need to link // Now, if this is &trait, we need to link
// the regions. // the regions.
match (ty::get(ty).sty, vstore) { match (&ty::get(ty).sty, vstore) {
(ty::ty_rptr(ra, _), (&ty::ty_rptr(ra, _),
ty::vstore_slice(rb)) => { ty::vstore_slice(rb)) => {
infer::mk_subr(fcx.infcx(), infer::mk_subr(fcx.infcx(),
false, false,
@ -646,7 +646,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
_ => {} _ => {}
} }
} }
(ty::ty_box(_), _) => { (&ty::ty_box(_), _) => {
fcx.ccx.tcx.sess.span_err(ex.span, fcx.ccx.tcx.sess.span_err(ex.span,
~"must cast \ ~"must cast \
a boxed \ a boxed \
@ -655,7 +655,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
trait"); trait");
err = true; err = true;
} }
(ty::ty_rptr(*), _) => { (&ty::ty_rptr(*), _) => {
fcx.ccx.tcx.sess.span_err(ex.span, fcx.ccx.tcx.sess.span_err(ex.span,
~"must cast \ ~"must cast \
a borrowed \ a borrowed \
@ -663,7 +663,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
a borrowed \ a borrowed \
trait"); trait");
} }
(ty::ty_uniq(*), _) => { (&ty::ty_uniq(*), _) => {
fcx.ccx.tcx.sess.span_err(ex.span, fcx.ccx.tcx.sess.span_err(ex.span,
~"must cast \ ~"must cast \
a unique \ a unique \

View file

@ -165,7 +165,7 @@ fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
resolve_method_map_entry(wbcx.fcx, e.span, e.id); resolve_method_map_entry(wbcx.fcx, e.span, e.id);
resolve_method_map_entry(wbcx.fcx, e.span, e.callee_id); resolve_method_map_entry(wbcx.fcx, e.span, e.callee_id);
match e.node { match e.node {
ast::expr_fn_block(ref decl, _, _) => { ast::expr_fn_block(ref decl, _) => {
for vec::each(decl.inputs) |input| { for vec::each(decl.inputs) |input| {
let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id); let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);

View file

@ -362,11 +362,11 @@ pub fn super_lattice_tys<L:LatticeDir TyLatticeDir Combine>(
let tcx = self.infcx().tcx; let tcx = self.infcx().tcx;
match (ty::get(a).sty, ty::get(b).sty) { match (&ty::get(a).sty, &ty::get(b).sty) {
(ty::ty_bot, _) => { return self.ty_bot(b); } (&ty::ty_bot, _) => { return self.ty_bot(b); }
(_, ty::ty_bot) => { return self.ty_bot(a); } (_, &ty::ty_bot) => { return self.ty_bot(a); }
(ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
let r = if_ok!(lattice_vars(self, a_id, b_id, let r = if_ok!(lattice_vars(self, a_id, b_id,
|x, y| self.tys(*x, *y))); |x, y| self.tys(*x, *y)));
return match r { return match r {
@ -375,12 +375,12 @@ pub fn super_lattice_tys<L:LatticeDir TyLatticeDir Combine>(
}; };
} }
(ty::ty_infer(TyVar(a_id)), _) => { (&ty::ty_infer(TyVar(a_id)), _) => {
return lattice_var_and_t(self, a_id, &b, return lattice_var_and_t(self, a_id, &b,
|x, y| self.tys(*x, *y)); |x, y| self.tys(*x, *y));
} }
(_, ty::ty_infer(TyVar(b_id))) => { (_, &ty::ty_infer(TyVar(b_id))) => {
return lattice_var_and_t(self, b_id, &a, return lattice_var_and_t(self, b_id, &a,
|x, y| self.tys(*x, *y)); |x, y| self.tys(*x, *y));
} }

View file

@ -106,25 +106,25 @@ pub impl Sub: Combine {
a.inf_str(self.infcx), b.inf_str(self.infcx)); a.inf_str(self.infcx), b.inf_str(self.infcx));
if a == b { return Ok(a); } if a == b { return Ok(a); }
let _indenter = indenter(); let _indenter = indenter();
match (ty::get(a).sty, ty::get(b).sty) { match (&ty::get(a).sty, &ty::get(b).sty) {
(ty::ty_bot, _) => { (&ty::ty_bot, _) => {
Ok(a) Ok(a)
} }
(ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
if_ok!(self.var_sub_var(a_id, b_id)); if_ok!(self.var_sub_var(a_id, b_id));
Ok(a) Ok(a)
} }
(ty::ty_infer(TyVar(a_id)), _) => { (&ty::ty_infer(TyVar(a_id)), _) => {
if_ok!(self.var_sub_t(a_id, b)); if_ok!(self.var_sub_t(a_id, b));
Ok(a) Ok(a)
} }
(_, ty::ty_infer(TyVar(b_id))) => { (_, &ty::ty_infer(TyVar(b_id))) => {
if_ok!(self.t_sub_var(a, b_id)); if_ok!(self.t_sub_var(a, b_id));
Ok(a) Ok(a)
} }
(_, ty::ty_bot) => { (_, &ty::ty_bot) => {
Err(ty::terr_sorts(expected_found(&self, a, b))) Err(ty::terr_sorts(expected_found(&self, a, b)))
} }

View file

@ -91,14 +91,13 @@ pub mod middle {
pub mod liveness; pub mod liveness;
pub mod kind; pub mod kind;
pub mod freevars; pub mod freevars;
pub mod capture;
pub mod pat_util; pub mod pat_util;
pub mod region; pub mod region;
pub mod const_eval; pub mod const_eval;
pub mod astencode; pub mod astencode;
pub mod lang_items; pub mod lang_items;
pub mod privacy; pub mod privacy;
pub mod mode; pub mod moves;
} }
pub mod front { pub mod front {
@ -221,9 +220,9 @@ pub fn run_compiler(args: &~[~str], demitter: diagnostic::emitter) {
let matches = let matches =
&match getopts::groups::getopts(args, optgroups()) { &match getopts::groups::getopts(args, optgroups()) {
Ok(ref m) => (*m), Ok(m) => m,
Err(ref f) => { Err(f) => {
early_error(demitter, getopts::fail_str((*f))) early_error(demitter, getopts::fail_str(f));
} }
}; };
@ -375,7 +374,7 @@ pub fn monitor(+f: fn~(diagnostic::emitter)) {
} }
pub fn main() { pub fn main() {
let mut args = os::args(); let args = os::args();
do monitor |move args, demitter| { do monitor |move args, demitter| {
run_compiler(&args, demitter); run_compiler(&args, demitter);
} }

View file

@ -421,8 +421,15 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
} }
ty_infer(infer_ty) => infer_ty.to_str(), ty_infer(infer_ty) => infer_ty.to_str(),
ty_err => ~"[type error]", ty_err => ~"[type error]",
ty_param(param_ty {idx: id, _}) => { ty_param(param_ty {idx: id, def_id: did}) => {
~"'" + str::from_bytes(~[('a' as u8) + (id as u8)]) if cx.sess.verbose() {
fmt!("'%s:%?",
str::from_bytes(~[('a' as u8) + (id as u8)]),
did)
} else {
fmt!("'%s",
str::from_bytes(~[('a' as u8) + (id as u8)]))
}
} }
ty_self => ~"self", ty_self => ~"self",
ty_enum(did, ref substs) | ty_struct(did, ref substs) => { ty_enum(did, ref substs) | ty_struct(did, ref substs) => {

View file

@ -166,20 +166,27 @@ fn fold_enum(
doc::EnumDoc { doc::EnumDoc {
variants: do par::map(doc.variants) |variant| { variants: do par::map(doc.variants) |variant| {
let variant = copy *variant; let variant = copy *variant;
let desc = do astsrv::exec(srv) |ctxt, copy variant| { let desc = {
match ctxt.ast_map.get(doc_id) { let variant = copy variant;
ast_map::node_item(@ast::item { do astsrv::exec(srv) |ctxt| {
node: ast::item_enum(ref enum_definition, _), _ match ctxt.ast_map.get(doc_id) {
}, _) => { ast_map::node_item(@ast::item {
let ast_variant = option::get( node: ast::item_enum(ref enum_definition, _), _
vec::find(enum_definition.variants, |v| { }, _) => {
to_str(v.node.name) == variant.name let ast_variant = option::get(
})); vec::find(enum_definition.variants, |v| {
to_str(v.node.name) == variant.name
}));
attr_parser::parse_desc(copy ast_variant.node.attrs) attr_parser::parse_desc(
} copy ast_variant.node.attrs)
_ => fail fmt!("Enum variant %s has id that's not bound \ }
to an enum item", variant.name) _ => {
fail fmt!("Enum variant %s has id that's \
not bound to an enum item",
variant.name)
}
}
} }
}; };

View file

@ -26,6 +26,7 @@ use pass::Pass;
use core::str; use core::str;
use core::vec; use core::vec;
use core::util;
use std::par; use std::par;
pub fn mk_pass() -> Pass { pub fn mk_pass() -> Pass {
@ -194,8 +195,8 @@ fn paragraphs(s: ~str) -> ~[~str] {
} else { } else {
if whitespace_lines > 0 { if whitespace_lines > 0 {
if !accum.is_empty() { if !accum.is_empty() {
res += ~[accum]; let v = util::replace(&mut accum, ~"");
accum = ~""; res.push(v);
} }
} }

View file

@ -102,10 +102,11 @@ fn item_to_entry(
let link = match doc { let link = match doc {
doc::ModTag(_) | doc::NmodTag(_) doc::ModTag(_) | doc::NmodTag(_)
if config.output_style == config::DocPerMod => { if config.output_style == config::DocPerMod => {
markdown_writer::make_filename(config, doc::ItemPage(doc)).to_str() markdown_writer::make_filename(config,
doc::ItemPage(copy doc)).to_str()
} }
_ => { _ => {
~"#" + pandoc_header_id(markdown_pass::header_text(doc)) ~"#" + pandoc_header_id(markdown_pass::header_text(copy doc))
} }
}; };

View file

@ -291,12 +291,13 @@ pub fn header_text(doc: doc::ItemTag) -> ~str {
fmt!("of `%s` for `%s`", ImplDoc.trait_types[0], fmt!("of `%s` for `%s`", ImplDoc.trait_types[0],
(&ImplDoc.self_ty).get()) (&ImplDoc.self_ty).get())
}; };
fmt!("%s %s", header_kind, desc) return fmt!("%s %s", header_kind, desc);
}
_ => {
header_text_(header_kind(doc), header_name(doc))
} }
_ => {}
} }
header_text_(header_kind(copy doc),
header_name(doc))
} }
fn header_text_(kind: &str, name: &str) -> ~str { fn header_text_(kind: &str, name: &str) -> ~str {

View file

@ -73,7 +73,7 @@ fn fold_item(
} }
fn apply_to_sections( fn apply_to_sections(
op: NominalOp<Op>, +op: NominalOp<Op>,
sections: ~[doc::Section] sections: ~[doc::Section]
) -> ~[doc::Section] { ) -> ~[doc::Section] {
par::map(sections, |section, copy op| doc::Section { par::map(sections, |section, copy op| doc::Section {
@ -115,7 +115,8 @@ fn apply_to_methods(
op: NominalOp<Op>, op: NominalOp<Op>,
docs: ~[doc::MethodDoc] docs: ~[doc::MethodDoc]
) -> ~[doc::MethodDoc] { ) -> ~[doc::MethodDoc] {
do par::map(docs) |doc, copy op| { let op = copy op;
do par::map(docs) |doc| {
doc::MethodDoc { doc::MethodDoc {
brief: maybe_apply_op(copy op, &doc.brief), brief: maybe_apply_op(copy op, &doc.brief),
desc: maybe_apply_op(copy op, &doc.desc), desc: maybe_apply_op(copy op, &doc.desc),

View file

@ -103,16 +103,18 @@ fn fold_const(
let srv = fold.ctxt; let srv = fold.ctxt;
doc::SimpleItemDoc { doc::SimpleItemDoc {
sig: Some(do astsrv::exec(srv) |copy doc, ctxt| { sig: Some({
match ctxt.ast_map.get(doc.id()) { let doc = copy doc;
ast_map::node_item(@ast::item { do astsrv::exec(srv) |ctxt| {
node: ast::item_const(ty, _), _ match ctxt.ast_map.get(doc.id()) {
}, _) => { ast_map::node_item(@ast::item {
pprust::ty_to_str(ty, extract::interner()) node: ast::item_const(ty, _), _
} }, _) => {
_ => fail ~"fold_const: id not bound to a const item" pprust::ty_to_str(ty, extract::interner())
} }
}), _ => fail ~"fold_const: id not bound to a const item"
}
}}),
.. doc .. doc
} }
} }
@ -132,26 +134,29 @@ fn fold_enum(
doc::EnumDoc { doc::EnumDoc {
variants: do par::map(doc.variants) |variant| { variants: do par::map(doc.variants) |variant| {
let variant = copy *variant; let sig = {
let sig = do astsrv::exec(srv) |copy variant, ctxt| { let variant = copy *variant;
match ctxt.ast_map.get(doc_id) { do astsrv::exec(srv) |copy variant, ctxt| {
ast_map::node_item(@ast::item { match ctxt.ast_map.get(doc_id) {
node: ast::item_enum(ref enum_definition, _), _ ast_map::node_item(@ast::item {
}, _) => { node: ast::item_enum(ref enum_definition, _), _
let ast_variant = }, _) => {
do vec::find(enum_definition.variants) |v| { let ast_variant =
to_str(v.node.name) == variant.name do vec::find(enum_definition.variants) |v| {
}.get(); to_str(v.node.name) == variant.name
}.get();
pprust::variant_to_str(ast_variant, extract::interner()) pprust::variant_to_str(
} ast_variant, extract::interner())
_ => fail ~"enum variant not bound to an enum item" }
_ => fail ~"enum variant not bound to an enum item"
}
} }
}; };
doc::VariantDoc { doc::VariantDoc {
sig: Some(sig), sig: Some(sig),
.. variant .. copy *variant
} }
}, },
.. doc .. doc
@ -262,18 +267,22 @@ fn fold_impl(
let srv = fold.ctxt; let srv = fold.ctxt;
let (trait_types, self_ty) = do astsrv::exec(srv) |copy doc, ctxt| { let (trait_types, self_ty) = {
match ctxt.ast_map.get(doc.id()) { let doc = copy doc;
ast_map::node_item(@ast::item { do astsrv::exec(srv) |ctxt| {
node: ast::item_impl(_, opt_trait_type, self_ty, _), _ match ctxt.ast_map.get(doc.id()) {
}, _) => { ast_map::node_item(@ast::item {
let trait_types = opt_trait_type.map_default(~[], |p| { node: ast::item_impl(_, opt_trait_type, self_ty, _), _
~[pprust::path_to_str(p.path, extract::interner())] }, _) => {
}); let trait_types = opt_trait_type.map_default(~[], |p| {
(trait_types, Some(pprust::ty_to_str(self_ty, ~[pprust::path_to_str(p.path, extract::interner())]
extract::interner()))) });
} (trait_types,
_ => fail ~"expected impl" Some(pprust::ty_to_str(
self_ty, extract::interner())))
}
_ => fail ~"expected impl"
}
} }
}; };
@ -318,20 +327,25 @@ fn fold_type(
let srv = fold.ctxt; let srv = fold.ctxt;
doc::SimpleItemDoc { doc::SimpleItemDoc {
sig: do astsrv::exec(srv) |copy doc, ctxt| { sig: {
match ctxt.ast_map.get(doc.id()) { let doc = copy doc;
ast_map::node_item(@ast::item { do astsrv::exec(srv) |ctxt| {
ident: ident, match ctxt.ast_map.get(doc.id()) {
node: ast::item_ty(ty, ref params), _ ast_map::node_item(@ast::item {
}, _) => { ident: ident,
Some(fmt!( node: ast::item_ty(ty, ref params), _
"type %s%s = %s", }, _) => {
to_str(ident), Some(fmt!(
pprust::typarams_to_str(*params, extract::interner()), "type %s%s = %s",
pprust::ty_to_str(ty, extract::interner()) to_str(ident),
)) pprust::typarams_to_str(*params,
} extract::interner()),
_ => fail ~"expected type" pprust::ty_to_str(ty,
extract::interner())
))
}
_ => fail ~"expected type"
}
} }
}, },
.. doc .. doc
@ -351,14 +365,17 @@ fn fold_struct(
let srv = fold.ctxt; let srv = fold.ctxt;
doc::StructDoc { doc::StructDoc {
sig: do astsrv::exec(srv) |copy doc, ctxt| { sig: {
match ctxt.ast_map.get(doc.id()) { let doc = copy doc;
ast_map::node_item(item, _) => { do astsrv::exec(srv) |ctxt| {
let item = strip_struct_extra_stuff(item); match ctxt.ast_map.get(doc.id()) {
Some(pprust::item_to_str(item, ast_map::node_item(item, _) => {
extract::interner())) let item = strip_struct_extra_stuff(item);
Some(pprust::item_to_str(item,
extract::interner()))
}
_ => fail ~"not an item"
} }
_ => fail ~"not an item"
} }
}, },
.. doc .. doc

View file

@ -788,13 +788,14 @@ mod test {
// The main task will wait until the test is over to proceed // The main task will wait until the test is over to proceed
let (finish_port, finish_chan) = pipes::stream(); let (finish_port, finish_chan) = pipes::stream();
let addr = ip::v4::parse_addr("127.0.0.1"); let addr0 = ip::v4::parse_addr("127.0.0.1");
let begin_connect_chan = Cell(move begin_connect_chan); let begin_connect_chan = Cell(move begin_connect_chan);
let accept_chan = Cell(move accept_chan); let accept_chan = Cell(move accept_chan);
// The server task // The server task
do task::spawn |copy addr, move begin_connect_chan, let addr = copy addr0;
do task::spawn |move begin_connect_chan,
move accept_chan| { move accept_chan| {
let iotask = &uv::global_loop::get(); let iotask = &uv::global_loop::get();
let begin_connect_chan = begin_connect_chan.take(); let begin_connect_chan = begin_connect_chan.take();
@ -821,7 +822,8 @@ mod test {
} }
// Client task // Client task
do task::spawn |copy addr, move begin_connect_port, let addr = copy addr0;
do task::spawn |move begin_connect_port,
move writer_chan| { move writer_chan| {
// Wait for the server to start listening // Wait for the server to start listening

View file

@ -117,8 +117,9 @@ enum IpGetAddrErr {
pub fn get_addr(node: &str, iotask: &iotask) pub fn get_addr(node: &str, iotask: &iotask)
-> result::Result<~[IpAddr], IpGetAddrErr> { -> result::Result<~[IpAddr], IpGetAddrErr> {
let (output_po, output_ch) = stream(); let (output_po, output_ch) = stream();
let output_ch = SharedChan(output_ch); let mut output_ch = Some(SharedChan(output_ch));
do str::as_buf(node) |node_ptr, len| { do str::as_buf(node) |node_ptr, len| {
let output_ch = output_ch.swap_unwrap();
unsafe { unsafe {
log(debug, fmt!("slice len %?", len)); log(debug, fmt!("slice len %?", len));
let handle = create_uv_getaddrinfo_t(); let handle = create_uv_getaddrinfo_t();

View file

@ -59,7 +59,8 @@ fn map_slices<A: Copy Owned, B: Copy Owned>(
let end = uint::min(len, base + items_per_task); let end = uint::min(len, base + items_per_task);
do vec::as_imm_buf(xs) |p, _len| { do vec::as_imm_buf(xs) |p, _len| {
let f = f(); let f = f();
let f = do future_spawn() |move f, copy base| { let base = base;
let f = do future_spawn() |move f| {
unsafe { unsafe {
let len = end - base; let len = end - base;
let slice = (ptr::offset(p, base), let slice = (ptr::offset(p, base),
@ -94,7 +95,8 @@ fn map_slices<A: Copy Owned, B: Copy Owned>(
pub fn map<A: Copy Owned, B: Copy Owned>( pub fn map<A: Copy Owned, B: Copy Owned>(
xs: &[A], f: fn~(&A) -> B) -> ~[B] { xs: &[A], f: fn~(&A) -> B) -> ~[B] {
vec::concat(map_slices(xs, || { vec::concat(map_slices(xs, || {
fn~(_base: uint, slice : &[A], copy f) -> ~[B] { let f = copy f;
fn~(_base: uint, slice : &[A]) -> ~[B] {
vec::map(slice, |x| f(x)) vec::map(slice, |x| f(x))
} }
})) }))
@ -104,6 +106,7 @@ pub fn map<A: Copy Owned, B: Copy Owned>(
pub fn mapi<A: Copy Owned, B: Copy Owned>(xs: &[A], pub fn mapi<A: Copy Owned, B: Copy Owned>(xs: &[A],
f: fn~(uint, &A) -> B) -> ~[B] { f: fn~(uint, &A) -> B) -> ~[B] {
let slices = map_slices(xs, || { let slices = map_slices(xs, || {
let f = copy f;
fn~(base: uint, slice : &[A], copy f) -> ~[B] { fn~(base: uint, slice : &[A], copy f) -> ~[B] {
vec::mapi(slice, |i, x| { vec::mapi(slice, |i, x| {
f(i + base, x) f(i + base, x)
@ -141,6 +144,7 @@ pub fn mapi_factory<A: Copy Owned, B: Copy Owned>(
/// Returns true if the function holds for all elements in the vector. /// Returns true if the function holds for all elements in the vector.
pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool { pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool {
do vec::all(map_slices(xs, || { do vec::all(map_slices(xs, || {
let f = copy f;
fn~(base: uint, slice : &[A], copy f) -> bool { fn~(base: uint, slice : &[A], copy f) -> bool {
vec::alli(slice, |i, x| { vec::alli(slice, |i, x| {
f(i + base, x) f(i + base, x)
@ -152,6 +156,7 @@ pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool {
/// Returns true if the function holds for any elements in the vector. /// Returns true if the function holds for any elements in the vector.
pub fn any<A: Copy Owned>(xs: &[A], f: fn~(&A) -> bool) -> bool { pub fn any<A: Copy Owned>(xs: &[A], f: fn~(&A) -> bool) -> bool {
do vec::any(map_slices(xs, || { do vec::any(map_slices(xs, || {
let f = copy f;
fn~(_base : uint, slice: &[A], copy f) -> bool { fn~(_base : uint, slice: &[A], copy f) -> bool {
vec::any(slice, |x| f(x)) vec::any(slice, |x| f(x))
} }

View file

@ -337,9 +337,9 @@ pub struct field_pat {
#[auto_encode] #[auto_encode]
#[auto_decode] #[auto_decode]
#[deriving_eq]
pub enum binding_mode { pub enum binding_mode {
bind_by_value, bind_by_copy,
bind_by_move,
bind_by_ref(mutability), bind_by_ref(mutability),
bind_infer bind_infer
} }
@ -347,51 +347,17 @@ pub enum binding_mode {
pub impl binding_mode : to_bytes::IterBytes { pub impl binding_mode : to_bytes::IterBytes {
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
match *self { match *self {
bind_by_value => 0u8.iter_bytes(lsb0, f), bind_by_copy => 0u8.iter_bytes(lsb0, f),
bind_by_move => 1u8.iter_bytes(lsb0, f),
bind_by_ref(ref m) => bind_by_ref(ref m) =>
to_bytes::iter_bytes_2(&2u8, m, lsb0, f), to_bytes::iter_bytes_2(&1u8, m, lsb0, f),
bind_infer => bind_infer =>
3u8.iter_bytes(lsb0, f), 2u8.iter_bytes(lsb0, f),
} }
} }
} }
pub impl binding_mode : cmp::Eq {
pure fn eq(&self, other: &binding_mode) -> bool {
match (*self) {
bind_by_value => {
match (*other) {
bind_by_value => true,
_ => false
}
}
bind_by_move => {
match (*other) {
bind_by_move => true,
_ => false
}
}
bind_by_ref(e0a) => {
match (*other) {
bind_by_ref(e0b) => e0a == e0b,
_ => false
}
}
bind_infer => {
match (*other) {
bind_infer => true,
_ => false
}
}
}
}
pure fn ne(&self, other: &binding_mode) -> bool { !(*self).eq(other) }
}
#[auto_encode] #[auto_encode]
#[auto_decode] #[auto_decode]
pub enum pat_ { pub enum pat_ {
@ -603,7 +569,7 @@ pub impl<T:cmp::Eq> inferable<T> : cmp::Eq {
// "resolved" mode: the real modes. // "resolved" mode: the real modes.
#[auto_encode] #[auto_encode]
#[auto_decode] #[auto_decode]
pub enum rmode { by_ref, by_val, by_move, by_copy } pub enum rmode { by_ref, by_val, by_copy }
pub impl rmode : to_bytes::IterBytes { pub impl rmode : to_bytes::IterBytes {
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
@ -729,8 +695,8 @@ pub enum expr_ {
(implicit) condition is always true. */ (implicit) condition is always true. */
expr_loop(blk, Option<ident>), expr_loop(blk, Option<ident>),
expr_match(@expr, ~[arm]), expr_match(@expr, ~[arm]),
expr_fn(Proto, fn_decl, blk, capture_clause), expr_fn(Proto, fn_decl, blk),
expr_fn_block(fn_decl, blk, capture_clause), expr_fn_block(fn_decl, blk),
// Inner expr is always an expr_fn_block. We need the wrapping node to // Inner expr is always an expr_fn_block. We need the wrapping node to
// easily type this (a function returning nil on the inside but bool on // easily type this (a function returning nil on the inside but bool on
// the outside). // the outside).
@ -740,7 +706,6 @@ pub enum expr_ {
expr_block(blk), expr_block(blk),
expr_copy(@expr), expr_copy(@expr),
expr_unary_move(@expr),
expr_assign(@expr, @expr), expr_assign(@expr, @expr),
expr_swap(@expr, @expr), expr_swap(@expr, @expr),
expr_assign_op(binop, @expr, @expr), expr_assign_op(binop, @expr, @expr),
@ -769,20 +734,6 @@ pub enum expr_ {
expr_paren(@expr) expr_paren(@expr)
} }
#[auto_encode]
#[auto_decode]
pub struct capture_item_ {
id: int,
is_move: bool,
name: ident, // Currently, can only capture a local var.
span: span,
}
pub type capture_item = @capture_item_;
pub type capture_clause = @~[capture_item];
//
// When the main rust parser encounters a syntax-extension invocation, it // When the main rust parser encounters a syntax-extension invocation, it
// parses the arguments to the invocation as a token-tree. This is a very // parses the arguments to the invocation as a token-tree. This is a very
// loose structure, such that all sorts of different AST-fragments can // loose structure, such that all sorts of different AST-fragments can

View file

@ -256,7 +256,7 @@ pub fn ident_to_path(s: span, +i: ident) -> @path {
pub fn ident_to_pat(id: node_id, s: span, +i: ident) -> @pat { pub fn ident_to_pat(id: node_id, s: span, +i: ident) -> @pat {
@ast::pat { id: id, @ast::pat { id: id,
node: pat_ident(bind_by_value, ident_to_path(s, i), None), node: pat_ident(bind_by_copy, ident_to_path(s, i), None),
span: s } span: s }
} }
@ -503,11 +503,8 @@ pub fn id_visitor(vfn: fn@(node_id)) -> visit::vt<()> {
vfn(m.self_id); vfn(m.self_id);
for vec::each(tps) |tp| { vfn(tp.id); } for vec::each(tps) |tp| { vfn(tp.id); }
} }
visit::fk_anon(_, capture_clause) | visit::fk_anon(_) |
visit::fk_fn_block(capture_clause) => { visit::fk_fn_block => {
for vec::each(*capture_clause) |clause| {
vfn(clause.id);
}
} }
} }

View file

@ -173,8 +173,8 @@ pub fn find_meta_items_by_name(metas: &[@ast::meta_item], name: &str) ->
* Returns true if a list of meta items contains another meta item. The * Returns true if a list of meta items contains another meta item. The
* comparison is performed structurally. * comparison is performed structurally.
*/ */
pub fn contains(haystack: ~[@ast::meta_item], needle: @ast::meta_item) pub fn contains(haystack: &[@ast::meta_item],
-> bool { needle: @ast::meta_item) -> bool {
for haystack.each |item| { for haystack.each |item| {
if eq(*item, needle) { return true; } if eq(*item, needle) { return true; }
} }

View file

@ -569,7 +569,7 @@ fn mk_ser_method(
pat: @ast::pat { pat: @ast::pat {
id: cx.next_id(), id: cx.next_id(),
node: ast::pat_ident( node: ast::pat_ident(
ast::bind_by_value, ast::bind_by_copy,
ast_util::ident_to_path(span, cx.ident_of(~"__s")), ast_util::ident_to_path(span, cx.ident_of(~"__s")),
None), None),
span: span, span: span,
@ -633,7 +633,7 @@ fn mk_deser_method(
pat: @ast::pat { pat: @ast::pat {
id: cx.next_id(), id: cx.next_id(),
node: ast::pat_ident( node: ast::pat_ident(
ast::bind_by_value, ast::bind_by_copy,
ast_util::ident_to_path(span, cx.ident_of(~"__d")), ast_util::ident_to_path(span, cx.ident_of(~"__d")),
None), None),
span: span, span: span,
@ -1095,7 +1095,7 @@ fn mk_enum_deser_body(
pat: @ast::pat { pat: @ast::pat {
id: cx.next_id(), id: cx.next_id(),
node: ast::pat_ident( node: ast::pat_ident(
ast::bind_by_value, ast::bind_by_copy,
ast_util::ident_to_path(span, cx.ident_of(~"i")), ast_util::ident_to_path(span, cx.ident_of(~"i")),
None), None),
span: span, span: span,
@ -1114,8 +1114,7 @@ fn mk_enum_deser_body(
span, span,
ast::expr_match(cx.expr_var(span, ~"i"), arms) ast::expr_match(cx.expr_var(span, ~"i"), arms)
) )
), )
@~[]
) )
); );

View file

@ -203,7 +203,7 @@ pub fn mk_local(cx: ext_ctxt, sp: span, mutbl: bool,
let pat = @ast::pat { let pat = @ast::pat {
id: cx.next_id(), id: cx.next_id(),
node: ast::pat_ident( node: ast::pat_ident(
ast::bind_by_value, ast::bind_by_copy,
mk_raw_path(sp, ~[ident]), mk_raw_path(sp, ~[ident]),
None), None),
span: sp, span: sp,
@ -279,9 +279,8 @@ pub fn mk_pat(cx: ext_ctxt, span: span, +pat: ast::pat_) -> @ast::pat {
} }
pub fn mk_pat_ident(cx: ext_ctxt, pub fn mk_pat_ident(cx: ext_ctxt,
span: span, span: span,
ident: ast::ident) ident: ast::ident) -> @ast::pat {
-> @ast::pat { mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_copy)
mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_value)
} }
pub fn mk_pat_ident_with_binding_mode(cx: ext_ctxt, pub fn mk_pat_ident_with_binding_mode(cx: ext_ctxt,
span: span, span: span,

View file

@ -105,7 +105,6 @@ pub trait ext_ctxt_ast_builder {
fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt; fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt;
fn stmt_expr(e: @ast::expr) -> @ast::stmt; fn stmt_expr(e: @ast::expr) -> @ast::stmt;
fn block_expr(b: ast::blk) -> @ast::expr; fn block_expr(b: ast::blk) -> @ast::expr;
fn move_expr(e: @ast::expr) -> @ast::expr;
fn ty_option(ty: @ast::Ty) -> @ast::Ty; fn ty_option(ty: @ast::Ty) -> @ast::Ty;
fn ty_infer() -> @ast::Ty; fn ty_infer() -> @ast::Ty;
fn ty_nil_ast_builder() -> @ast::Ty; fn ty_nil_ast_builder() -> @ast::Ty;
@ -130,15 +129,6 @@ pub impl ext_ctxt: ext_ctxt_ast_builder {
} }
} }
fn move_expr(e: @ast::expr) -> @ast::expr {
@expr {
id: self.next_id(),
callee_id: self.next_id(),
node: ast::expr_unary_move(e),
span: e.span,
}
}
fn stmt_expr(e: @ast::expr) -> @ast::stmt { fn stmt_expr(e: @ast::expr) -> @ast::stmt {
@spanned { node: ast::stmt_expr(e, self.next_id()), @spanned { node: ast::stmt_expr(e, self.next_id()),
span: dummy_sp()} span: dummy_sp()}
@ -205,7 +195,7 @@ pub impl ext_ctxt: ext_ctxt_ast_builder {
pat: @ast::pat { pat: @ast::pat {
id: self.next_id(), id: self.next_id(),
node: ast::pat_ident( node: ast::pat_ident(
ast::bind_by_value, ast::bind_by_copy,
ast_util::ident_to_path(dummy_sp(), name), ast_util::ident_to_path(dummy_sp(), name),
None), None),
span: dummy_sp(), span: dummy_sp(),

View file

@ -466,30 +466,17 @@ pub fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
expr_match(fld.fold_expr(expr), expr_match(fld.fold_expr(expr),
vec::map((*arms), |x| fld.fold_arm(*x))) vec::map((*arms), |x| fld.fold_arm(*x)))
} }
expr_fn(proto, decl, ref body, captures) => { expr_fn(proto, decl, ref body) => {
let captures = do captures.map |cap_item| { expr_fn(proto,
@ast::capture_item_ { fold_fn_decl(decl, fld),
id: fld.new_id(cap_item.id), fld.fold_block(*body))
..**cap_item
}
};
expr_fn(proto, fold_fn_decl(decl, fld),
fld.fold_block((*body)),
@captures)
} }
expr_fn_block(decl, ref body, captures) => { expr_fn_block(decl, ref body) => {
let captures = do captures.map |cap_item| { expr_fn_block(fold_fn_decl(decl, fld),
@ast::capture_item_ { fld.fold_block(*body))
id: fld.new_id(cap_item.id),
..**cap_item
}
};
expr_fn_block(fold_fn_decl(decl, fld), fld.fold_block((*body)),
@captures)
} }
expr_block(ref blk) => expr_block(fld.fold_block((*blk))), expr_block(ref blk) => expr_block(fld.fold_block((*blk))),
expr_copy(e) => expr_copy(fld.fold_expr(e)), expr_copy(e) => expr_copy(fld.fold_expr(e)),
expr_unary_move(e) => expr_unary_move(fld.fold_expr(e)),
expr_assign(el, er) => { expr_assign(el, er) => {
expr_assign(fld.fold_expr(el), fld.fold_expr(er)) expr_assign(fld.fold_expr(el), fld.fold_expr(er))
} }

View file

@ -13,9 +13,9 @@ use core::prelude::*;
use ast::{ProtoBox, ProtoUniq, RegionTyParamBound, TraitTyParamBound}; use ast::{ProtoBox, ProtoUniq, RegionTyParamBound, TraitTyParamBound};
use ast::{provided, public, pure_fn, purity, re_static}; use ast::{provided, public, pure_fn, purity, re_static};
use ast::{_mod, add, arg, arm, attribute, bind_by_ref, bind_infer}; use ast::{_mod, add, arg, arm, attribute, bind_by_ref, bind_infer};
use ast::{bind_by_value, bind_by_move, bitand, bitor, bitxor, blk}; use ast::{bind_by_copy, bitand, bitor, bitxor, blk};
use ast::{blk_check_mode, box, by_copy, by_move, by_ref, by_val}; use ast::{blk_check_mode, box, by_copy, by_ref, by_val};
use ast::{capture_clause, capture_item, crate, crate_cfg, decl, decl_item}; use ast::{crate, crate_cfg, decl, decl_item};
use ast::{decl_local, default_blk, deref, div, enum_def, enum_variant_kind}; use ast::{decl_local, default_blk, deref, div, enum_def, enum_variant_kind};
use ast::{expl, expr, expr_, expr_addr_of, expr_match, expr_again}; use ast::{expl, expr, expr_, expr_addr_of, expr_match, expr_again};
use ast::{expr_assert, expr_assign, expr_assign_op, expr_binary, expr_block}; use ast::{expr_assert, expr_assign, expr_assign_op, expr_binary, expr_block};
@ -24,7 +24,7 @@ use ast::{expr_fail, expr_field, expr_fn, expr_fn_block, expr_if, expr_index};
use ast::{expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac}; use ast::{expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac};
use ast::{expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat}; use ast::{expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat};
use ast::{expr_ret, expr_swap, expr_struct, expr_tup, expr_unary}; use ast::{expr_ret, expr_swap, expr_struct, expr_tup, expr_unary};
use ast::{expr_unary_move, expr_vec, expr_vstore, expr_vstore_mut_box}; use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
use ast::{expr_vstore_fixed, expr_vstore_slice, expr_vstore_box}; use ast::{expr_vstore_fixed, expr_vstore_slice, expr_vstore_box};
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, field, fn_decl}; use ast::{expr_vstore_mut_slice, expr_while, extern_fn, field, fn_decl};
use ast::{expr_vstore_uniq, TyFn, Onceness, Once, Many}; use ast::{expr_vstore_uniq, TyFn, Onceness, Once, Many};
@ -102,7 +102,7 @@ enum restriction {
enum class_contents { dtor_decl(blk, ~[attribute], codemap::span), enum class_contents { dtor_decl(blk, ~[attribute], codemap::span),
members(~[@struct_field]) } members(~[@struct_field]) }
type arg_or_capture_item = Either<arg, capture_item>; type arg_or_capture_item = Either<arg, ()>;
type item_info = (ident, item_, Option<~[attribute]>); type item_info = (ident, item_, Option<~[attribute]>);
pub enum item_or_view_item { pub enum item_or_view_item {
@ -401,7 +401,7 @@ pub impl Parser {
let tps = p.parse_ty_params(); let tps = p.parse_ty_params();
let (self_ty, d, _) = do self.parse_fn_decl_with_self() |p| { let (self_ty, d) = do self.parse_fn_decl_with_self() |p| {
// This is somewhat dubious; We don't want to allow argument // This is somewhat dubious; We don't want to allow argument
// names to be left off if there is a definition... // names to be left off if there is a definition...
either::Left(p.parse_arg_general(false)) either::Left(p.parse_arg_general(false))
@ -651,7 +651,7 @@ pub impl Parser {
fn parse_arg_mode() -> mode { fn parse_arg_mode() -> mode {
if self.eat(token::BINOP(token::MINUS)) { if self.eat(token::BINOP(token::MINUS)) {
expl(by_move) expl(by_copy) // NDM outdated syntax
} else if self.eat(token::ANDAND) { } else if self.eat(token::ANDAND) {
expl(by_ref) expl(by_ref)
} else if self.eat(token::BINOP(token::PLUS)) { } else if self.eat(token::BINOP(token::PLUS)) {
@ -689,23 +689,12 @@ pub impl Parser {
} }
fn parse_capture_item_or(parse_arg_fn: fn(Parser) -> arg_or_capture_item) fn parse_capture_item_or(parse_arg_fn: fn(Parser) -> arg_or_capture_item)
-> arg_or_capture_item { -> arg_or_capture_item
{
fn parse_capture_item(p:Parser, is_move: bool) -> capture_item { if self.eat_keyword(~"move") || self.eat_keyword(~"copy") {
let sp = mk_sp(p.span.lo, p.span.hi); // XXX outdated syntax now that moves-based-on-type has gone in
let ident = p.parse_ident(); self.parse_ident();
@ast::capture_item_ { either::Right(())
id: p.get_id(),
is_move: is_move,
name: ident,
span: sp,
}
}
if self.eat_keyword(~"move") {
either::Right(parse_capture_item(self, true))
} else if self.eat_keyword(~"copy") {
either::Right(parse_capture_item(self, false))
} else { } else {
parse_arg_fn(self) parse_arg_fn(self)
} }
@ -1078,9 +1067,8 @@ pub impl Parser {
ex = expr_copy(e); ex = expr_copy(e);
hi = e.span.hi; hi = e.span.hi;
} else if self.eat_keyword(~"move") { } else if self.eat_keyword(~"move") {
let e = self.parse_expr(); // XXX move keyword is no longer important, remove after snapshot
ex = expr_unary_move(e); return self.parse_expr();
hi = e.span.hi;
} else if self.token == token::MOD_SEP || } else if self.token == token::MOD_SEP ||
is_ident(self.token) && !self.is_keyword(~"true") && is_ident(self.token) && !self.is_keyword(~"true") &&
!self.is_keyword(~"false") { !self.is_keyword(~"false") {
@ -1576,12 +1564,10 @@ pub impl Parser {
// if we want to allow fn expression argument types to be inferred in // if we want to allow fn expression argument types to be inferred in
// the future, just have to change parse_arg to parse_fn_block_arg. // the future, just have to change parse_arg to parse_fn_block_arg.
let (decl, capture_clause) = let decl = self.parse_fn_decl(|p| p.parse_arg_or_capture_item());
self.parse_fn_decl(|p| p.parse_arg_or_capture_item());
let body = self.parse_block(); let body = self.parse_block();
return self.mk_expr(lo, body.span.hi, return self.mk_expr(lo, body.span.hi,expr_fn(proto, decl, body));
expr_fn(proto, decl, body, capture_clause));
} }
// `|args| { ... }` like in `do` expressions // `|args| { ... }` like in `do` expressions
@ -1594,18 +1580,15 @@ pub impl Parser {
} }
_ => { _ => {
// No argument list - `do foo {` // No argument list - `do foo {`
( ast::fn_decl {
ast::fn_decl { inputs: ~[],
inputs: ~[], output: @Ty {
output: @Ty { id: self.get_id(),
id: self.get_id(), node: ty_infer,
node: ty_infer, span: self.span
span: self.span },
}, cf: return_val
cf: return_val }
},
@~[]
)
} }
} }
}, },
@ -1621,10 +1604,10 @@ pub impl Parser {
|| self.parse_expr()) || self.parse_expr())
} }
fn parse_lambda_expr_(parse_decl: fn&() -> (fn_decl, capture_clause), fn parse_lambda_expr_(parse_decl: fn&() -> fn_decl,
parse_body: fn&() -> @expr) -> @expr { parse_body: fn&() -> @expr) -> @expr {
let lo = self.last_span.lo; let lo = self.last_span.lo;
let (decl, captures) = parse_decl(); let decl = parse_decl();
let body = parse_body(); let body = parse_body();
let fakeblock = ast::blk_ { let fakeblock = ast::blk_ {
view_items: ~[], view_items: ~[],
@ -1636,7 +1619,7 @@ pub impl Parser {
let fakeblock = spanned(body.span.lo, body.span.hi, let fakeblock = spanned(body.span.lo, body.span.hi,
fakeblock); fakeblock);
return self.mk_expr(lo, body.span.hi, return self.mk_expr(lo, body.span.hi,
expr_fn_block(decl, fakeblock, captures)); expr_fn_block(decl, fakeblock));
} }
fn parse_else_expr() -> @expr { fn parse_else_expr() -> @expr {
@ -2065,22 +2048,16 @@ pub impl Parser {
let mutbl = self.parse_mutability(); let mutbl = self.parse_mutability();
pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl)); pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl));
} else if self.eat_keyword(~"copy") { } else if self.eat_keyword(~"copy") {
pat = self.parse_pat_ident(refutable, bind_by_value); pat = self.parse_pat_ident(refutable, bind_by_copy);
} else if self.eat_keyword(~"move") {
pat = self.parse_pat_ident(refutable, bind_by_move);
} else { } else {
let binding_mode; if self.eat_keyword(~"move") {
// XXX: Aren't these two cases deadcode? -- bblum /* XXX---remove move keyword */
if self.eat_keyword(~"copy") {
binding_mode = bind_by_value;
} else if self.eat_keyword(~"move") {
binding_mode = bind_by_move;
} else if refutable {
binding_mode = bind_infer;
} else {
binding_mode = bind_by_value;
} }
// XXX---refutable match bindings should work same as let
let binding_mode =
if refutable {bind_infer} else {bind_by_copy};
let cannot_be_enum_or_struct; let cannot_be_enum_or_struct;
match self.look_ahead(1) { match self.look_ahead(1) {
token::LPAREN | token::LBRACKET | token::LT | token::LPAREN | token::LBRACKET | token::LT |
@ -2560,25 +2537,21 @@ pub impl Parser {
} }
fn parse_fn_decl(parse_arg_fn: fn(Parser) -> arg_or_capture_item) fn parse_fn_decl(parse_arg_fn: fn(Parser) -> arg_or_capture_item)
-> (fn_decl, capture_clause) { -> fn_decl
{
let args_or_capture_items: ~[arg_or_capture_item] = let args_or_capture_items: ~[arg_or_capture_item] =
self.parse_unspanned_seq( self.parse_unspanned_seq(
token::LPAREN, token::RPAREN, token::LPAREN, token::RPAREN,
seq_sep_trailing_disallowed(token::COMMA), parse_arg_fn); seq_sep_trailing_disallowed(token::COMMA), parse_arg_fn);
let inputs = either::lefts(args_or_capture_items); let inputs = either::lefts(args_or_capture_items);
let capture_clause = @either::rights(args_or_capture_items);
let (ret_style, ret_ty) = self.parse_ret_ty(); let (ret_style, ret_ty) = self.parse_ret_ty();
( ast::fn_decl {
ast::fn_decl { inputs: inputs,
inputs: inputs, output: ret_ty,
output: ret_ty, cf: ret_style,
cf: ret_style, }
},
capture_clause
)
} }
fn is_self_ident() -> bool { fn is_self_ident() -> bool {
@ -2598,8 +2571,8 @@ pub impl Parser {
} }
fn parse_fn_decl_with_self(parse_arg_fn: fn parse_fn_decl_with_self(parse_arg_fn:
fn(Parser) -> arg_or_capture_item) fn(Parser) -> arg_or_capture_item)
-> (self_ty, fn_decl, capture_clause) { -> (self_ty, fn_decl) {
fn maybe_parse_self_ty(cnstr: fn(+v: mutability) -> ast::self_ty_, fn maybe_parse_self_ty(cnstr: fn(+v: mutability) -> ast::self_ty_,
p: Parser) -> ast::self_ty_ { p: Parser) -> ast::self_ty_ {
@ -2675,7 +2648,6 @@ pub impl Parser {
let hi = self.span.hi; let hi = self.span.hi;
let inputs = either::lefts(args_or_capture_items); let inputs = either::lefts(args_or_capture_items);
let capture_clause = @either::rights(args_or_capture_items);
let (ret_style, ret_ty) = self.parse_ret_ty(); let (ret_style, ret_ty) = self.parse_ret_ty();
let fn_decl = ast::fn_decl { let fn_decl = ast::fn_decl {
@ -2684,10 +2656,10 @@ pub impl Parser {
cf: ret_style cf: ret_style
}; };
(spanned(lo, hi, self_ty), fn_decl, capture_clause) (spanned(lo, hi, self_ty), fn_decl)
} }
fn parse_fn_block_decl() -> (fn_decl, capture_clause) { fn parse_fn_block_decl() -> fn_decl {
let inputs_captures = { let inputs_captures = {
if self.eat(token::OROR) { if self.eat(token::OROR) {
~[] ~[]
@ -2704,14 +2676,11 @@ pub impl Parser {
@Ty { id: self.get_id(), node: ty_infer, span: self.span } @Ty { id: self.get_id(), node: ty_infer, span: self.span }
}; };
( ast::fn_decl {
ast::fn_decl { inputs: either::lefts(inputs_captures),
inputs: either::lefts(inputs_captures), output: output,
output: output, cf: return_val,
cf: return_val, }
},
@either::rights(inputs_captures)
)
} }
fn parse_fn_header() -> {ident: ident, tps: ~[ty_param]} { fn parse_fn_header() -> {ident: ident, tps: ~[ty_param]} {
@ -2733,7 +2702,7 @@ pub impl Parser {
fn parse_item_fn(purity: purity) -> item_info { fn parse_item_fn(purity: purity) -> item_info {
let t = self.parse_fn_header(); let t = self.parse_fn_header();
let (decl, _) = self.parse_fn_decl(|p| p.parse_arg()); let decl = self.parse_fn_decl(|p| p.parse_arg());
let (inner_attrs, body) = self.parse_inner_attrs_and_block(true); let (inner_attrs, body) = self.parse_inner_attrs_and_block(true);
(t.ident, item_fn(decl, purity, t.tps, body), Some(inner_attrs)) (t.ident, item_fn(decl, purity, t.tps, body), Some(inner_attrs))
} }
@ -2753,7 +2722,7 @@ pub impl Parser {
let pur = self.parse_fn_purity(); let pur = self.parse_fn_purity();
let ident = self.parse_method_name(); let ident = self.parse_method_name();
let tps = self.parse_ty_params(); let tps = self.parse_ty_params();
let (self_ty, decl, _) = do self.parse_fn_decl_with_self() |p| { let (self_ty, decl) = do self.parse_fn_decl_with_self() |p| {
p.parse_arg() p.parse_arg()
}; };
// XXX: interaction between staticness, self_ty is broken now // XXX: interaction between staticness, self_ty is broken now
@ -3262,7 +3231,7 @@ pub impl Parser {
let vis = self.parse_visibility(); let vis = self.parse_visibility();
let purity = self.parse_fn_purity(); let purity = self.parse_fn_purity();
let t = self.parse_fn_header(); let t = self.parse_fn_header();
let (decl, _) = self.parse_fn_decl(|p| p.parse_arg()); let decl = self.parse_fn_decl(|p| p.parse_arg());
let mut hi = self.span.hi; let mut hi = self.span.hi;
self.expect(token::SEMI); self.expect(token::SEMI);
@ast::foreign_item { ident: t.ident, @ast::foreign_item { ident: t.ident,

View file

@ -1305,24 +1305,24 @@ pub fn print_expr(s: ps, &&expr: @ast::expr) {
} }
bclose_(s, expr.span, match_indent_unit); bclose_(s, expr.span, match_indent_unit);
} }
ast::expr_fn(proto, decl, ref body, cap_clause) => { ast::expr_fn(proto, decl, ref body) => {
// containing cbox, will be closed by print-block at } // containing cbox, will be closed by print-block at }
cbox(s, indent_unit); cbox(s, indent_unit);
// head-box, will be closed by print-block at start // head-box, will be closed by print-block at start
ibox(s, 0u); ibox(s, 0u);
print_fn_header_info(s, None, None, ast::Many, print_fn_header_info(s, None, None, ast::Many,
Some(proto), ast::inherited); Some(proto), ast::inherited);
print_fn_args_and_ret(s, decl, *cap_clause, None); print_fn_args_and_ret(s, decl, None);
space(s.s); space(s.s);
print_block(s, (*body)); print_block(s, (*body));
} }
ast::expr_fn_block(decl, ref body, cap_clause) => { ast::expr_fn_block(decl, ref body) => {
// in do/for blocks we don't want to show an empty // in do/for blocks we don't want to show an empty
// argument list, but at this point we don't know which // argument list, but at this point we don't know which
// we are inside. // we are inside.
// //
// if !decl.inputs.is_empty() { // if !decl.inputs.is_empty() {
print_fn_block_args(s, decl, *cap_clause); print_fn_block_args(s, decl);
space(s.s); space(s.s);
// } // }
assert (*body).node.stmts.is_empty(); assert (*body).node.stmts.is_empty();
@ -1357,10 +1357,6 @@ pub fn print_expr(s: ps, &&expr: @ast::expr) {
print_block(s, (*blk)); print_block(s, (*blk));
} }
ast::expr_copy(e) => { word_space(s, ~"copy"); print_expr(s, e); } ast::expr_copy(e) => { word_space(s, ~"copy"); print_expr(s, e); }
ast::expr_unary_move(e) => {
word_space(s, ~"move");
print_expr(s, e);
}
ast::expr_assign(lhs, rhs) => { ast::expr_assign(lhs, rhs) => {
print_expr(s, lhs); print_expr(s, lhs);
space(s.s); space(s.s);
@ -1554,10 +1550,7 @@ pub fn print_pat(s: ps, &&pat: @ast::pat, refutable: bool) {
word_nbsp(s, ~"ref"); word_nbsp(s, ~"ref");
print_mutability(s, mutbl); print_mutability(s, mutbl);
} }
ast::bind_by_move => { ast::bind_by_copy => {
word_nbsp(s, ~"move");
}
ast::bind_by_value => {
word_nbsp(s, ~"copy"); word_nbsp(s, ~"copy");
} }
ast::bind_infer => {} ast::bind_infer => {}
@ -1693,16 +1686,14 @@ pub fn print_fn(s: ps,
nbsp(s); nbsp(s);
print_ident(s, name); print_ident(s, name);
print_type_params(s, typarams); print_type_params(s, typarams);
print_fn_args_and_ret(s, decl, ~[], opt_self_ty); print_fn_args_and_ret(s, decl, opt_self_ty);
} }
pub fn print_fn_args(s: ps, decl: ast::fn_decl, pub fn print_fn_args(s: ps, decl: ast::fn_decl,
cap_items: ~[ast::capture_item], opt_self_ty: Option<ast::self_ty_>) {
opt_self_ty: Option<ast::self_ty_>) { // It is unfortunate to duplicate the commasep logic, but we we want the
// It is unfortunate to duplicate the commasep logic, but we // self type and the args all in the same box.
// we want the self type, the args, and the capture clauses all box(s, 0u, inconsistent);
// in the same box.
box(s, 0, inconsistent);
let mut first = true; let mut first = true;
for opt_self_ty.each |self_ty| { for opt_self_ty.each |self_ty| {
first = !print_self_ty(s, *self_ty); first = !print_self_ty(s, *self_ty);
@ -1713,21 +1704,13 @@ pub fn print_fn_args(s: ps, decl: ast::fn_decl,
print_arg(s, *arg); print_arg(s, *arg);
} }
for cap_items.each |cap_item| {
if first { first = false; } else { word_space(s, ~","); }
if cap_item.is_move { word_nbsp(s, ~"move") }
else { word_nbsp(s, ~"copy") }
print_ident(s, cap_item.name);
}
end(s); end(s);
} }
pub fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl, pub fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl,
cap_items: ~[ast::capture_item],
opt_self_ty: Option<ast::self_ty_>) { opt_self_ty: Option<ast::self_ty_>) {
popen(s); popen(s);
print_fn_args(s, decl, cap_items, opt_self_ty); print_fn_args(s, decl, opt_self_ty);
pclose(s); pclose(s);
maybe_print_comment(s, decl.output.span.lo); maybe_print_comment(s, decl.output.span.lo);
@ -1741,10 +1724,9 @@ pub fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl,
} }
} }
pub fn print_fn_block_args(s: ps, decl: ast::fn_decl, pub fn print_fn_block_args(s: ps, decl: ast::fn_decl) {
cap_items: ~[ast::capture_item]) {
word(s.s, ~"|"); word(s.s, ~"|");
print_fn_args(s, decl, cap_items, None); print_fn_args(s, decl, None);
word(s.s, ~"|"); word(s.s, ~"|");
match decl.output.node { match decl.output.node {
@ -1761,10 +1743,9 @@ pub fn print_fn_block_args(s: ps, decl: ast::fn_decl,
pub fn mode_to_str(m: ast::mode) -> ~str { pub fn mode_to_str(m: ast::mode) -> ~str {
match m { match m {
ast::expl(ast::by_move) => ~"-",
ast::expl(ast::by_ref) => ~"&&", ast::expl(ast::by_ref) => ~"&&",
ast::expl(ast::by_val) => ~"++",
ast::expl(ast::by_copy) => ~"+", ast::expl(ast::by_copy) => ~"+",
ast::expl(ast::by_val) => ~"++",
ast::infer(_) => ~"" ast::infer(_) => ~""
} }
} }

View file

@ -32,8 +32,8 @@ pub enum vt<E> { mk_vt(visitor<E>), }
pub enum fn_kind { pub enum fn_kind {
fk_item_fn(ident, ~[ty_param], purity), //< an item declared with fn() fk_item_fn(ident, ~[ty_param], purity), //< an item declared with fn()
fk_method(ident, ~[ty_param], @method), fk_method(ident, ~[ty_param], @method),
fk_anon(Proto, capture_clause), //< an anonymous function like fn@(...) fk_anon(Proto), //< an anonymous function like fn@(...)
fk_fn_block(capture_clause), //< a block {||...} fk_fn_block, //< a block {||...}
fk_dtor(~[ty_param], ~[attribute], node_id /* self id */, fk_dtor(~[ty_param], ~[attribute], node_id /* self id */,
def_id /* parent class id */) // class destructor def_id /* parent class id */) // class destructor
@ -457,12 +457,12 @@ pub fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
(v.visit_expr)(x, e, v); (v.visit_expr)(x, e, v);
for (*arms).each |a| { (v.visit_arm)(*a, e, v); } for (*arms).each |a| { (v.visit_arm)(*a, e, v); }
} }
expr_fn(proto, decl, ref body, cap_clause) => { expr_fn(proto, decl, ref body) => {
(v.visit_fn)(fk_anon(proto, cap_clause), decl, (*body), (v.visit_fn)(fk_anon(proto), decl, (*body),
ex.span, ex.id, e, v); ex.span, ex.id, e, v);
} }
expr_fn_block(decl, ref body, cap_clause) => { expr_fn_block(decl, ref body) => {
(v.visit_fn)(fk_fn_block(cap_clause), decl, (*body), (v.visit_fn)(fk_fn_block, decl, (*body),
ex.span, ex.id, e, v); ex.span, ex.id, e, v);
} }
expr_block(ref b) => (v.visit_block)((*b), e, v), expr_block(ref b) => (v.visit_block)((*b), e, v),
@ -471,7 +471,6 @@ pub fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
(v.visit_expr)(a, e, v); (v.visit_expr)(a, e, v);
} }
expr_copy(a) => (v.visit_expr)(a, e, v), expr_copy(a) => (v.visit_expr)(a, e, v),
expr_unary_move(a) => (v.visit_expr)(a, e, v),
expr_swap(a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); } expr_swap(a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); }
expr_assign_op(_, a, b) => { expr_assign_op(_, a, b) => {
(v.visit_expr)(b, e, v); (v.visit_expr)(b, e, v);

View file

@ -377,7 +377,8 @@ fn validate(edges: ~[(node_id, node_id)],
log(info, ~"Verifying tree and graph edges..."); log(info, ~"Verifying tree and graph edges...");
let status = do par::alli(tree) |u, v, copy edges| { let edges = copy edges;
let status = do par::alli(tree) |u, v| {
let u = u as node_id; let u = u as node_id;
if *v == -1i64 || u == root { if *v == -1i64 || u == root {
true true

View file

@ -1,8 +0,0 @@
fn main() {
let a = [mut 1, 2, 3, 4];
let _ = match a {
[1, 2, ..move tail] => tail,
_ => core::util::unreachable()
};
a[0] = 0; //~ ERROR: use of moved value
}

View file

@ -1,18 +0,0 @@
// Copyright 2012 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.
// error-pattern:moving out of captured outer immutable variable in a stack closure
fn force(f: fn()) { f(); }
fn main() {
let mut x = @{x: 17, y: 2};
let y = @{x: 5, y: 5};
force(|| x = move y );
}

View file

@ -13,18 +13,18 @@ fn borrow(v: &int, f: fn(x: &int)) {
} }
fn box_imm() { fn box_imm() {
let mut v = ~3; let v = ~3;
let _w = &mut v; //~ NOTE loan of mutable local variable granted here let _w = &v; //~ NOTE loan of immutable local variable granted here
do task::spawn |move v| { do task::spawn {
//~^ ERROR moving out of mutable local variable prohibited due to outstanding loan
debug!("v=%d", *v); debug!("v=%d", *v);
//~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
} }
let mut v = ~3; let v = ~3;
let _w = &mut v; //~ NOTE loan of mutable local variable granted here let _w = &v; //~ NOTE loan of immutable local variable granted here
task::spawn(fn~(move v) { task::spawn(fn~() {
//~^ ERROR moving out of mutable local variable prohibited due to outstanding loan
debug!("v=%d", *v); debug!("v=%d", *v);
//~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
}); });
} }

View file

@ -0,0 +1,18 @@
extern mod std;
use std::ebml::reader;
use std::ebml::writer;
use std::serialize;
fn main() {
let foo = ~3;
let _pfoo = &foo;
let _f: @fn() -> int = || *foo + 5;
//~^ ERROR by-move capture
let bar = ~3;
let _g = || {
let _h: @fn() -> int = || *bar;
//~^ ERROR illegal by-move capture
};
}

View file

@ -0,0 +1,8 @@
fn main() {
let a = [mut 1, 2, 3, 4];
let _ = match a {
[1, 2, ..tail] => tail,
_ => core::util::unreachable()
};
a[0] = 0; //~ ERROR: assigning to mutable vec content prohibited due to outstanding loan
}

View file

@ -1,15 +0,0 @@
// Copyright 2012 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.
// error-pattern:variable `x` captured more than once
fn main() {
let x = 5;
let y = fn~(move x, copy x) -> int { x };
}

View file

@ -1,15 +0,0 @@
// Copyright 2012 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.
// error-pattern:variable `x` captured more than once
fn main() {
let x = 5;
let y = fn~(copy x, copy x) -> int { x };
}

View file

@ -1,15 +0,0 @@
// Copyright 2012 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.
// error-pattern:variable `x` captured more than once
fn main() {
let x = 5;
let y = fn~(move x, move x) -> int { x };
}

View file

@ -1,32 +0,0 @@
// Copyright 2012 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.
// error-pattern: copying a noncopyable value
struct foo { x: int, }
impl foo : Drop {
fn finalize(&self) {}
}
fn foo(x: int) -> foo {
foo {
x: x
}
}
fn to_lambda2(b: foo) -> fn@(uint) -> uint {
// test case where copy clause specifies a value that is not used
// in fn@ body, but value is illegal to copy:
return fn@(u: uint, copy b) -> uint { 22u };
}
fn main() {
}

View file

@ -1,17 +0,0 @@
// Copyright 2012 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.
fn main() {
let x = 5;
let _y = fn~(move x) -> int {
let _z = fn~(move x) -> int { x }; //~ ERROR moving out of captured outer variable in a heap closure
22
};
}

View file

@ -1,16 +0,0 @@
// Copyright 2012 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.
// error-pattern:unresolved name
fn main() {
let x = 5;
let y = fn~(copy z, copy x) {
};
}

View file

@ -1,16 +0,0 @@
// Copyright 2012 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.
// error-pattern:unresolved name
fn main() {
let x = 5;
let y = fn~(move z, move x) {
};
}

View file

@ -1,15 +0,0 @@
// Copyright 2012 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.
fn main() {
let x = 5;
let _y = fn~(move x) { }; //~ WARNING captured variable `x` not used in closure
let _z = x; //~ ERROR use of moved value: `x`
}

View file

@ -1,24 +0,0 @@
// Copyright 2012 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.
fn foo(_f: fn()) {}
fn bar(_f: @int) {}
fn main() {
let x = @3;
foo(|| bar(x) );
let x = @3;
foo(|copy x| bar(x) ); //~ ERROR cannot capture values explicitly with a block closure
let x = @3;
foo(|move x| bar(x) ); //~ ERROR cannot capture values explicitly with a block closure
}

View file

@ -1,30 +0,0 @@
// Copyright 2012 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.
fn closure2(+x: core::util::NonCopyable)
-> (core::util::NonCopyable, fn@() -> core::util::NonCopyable) {
let f = fn@(copy x) -> core::util::NonCopyable {
//~^ ERROR copying a noncopyable value
//~^^ NOTE non-copyable value cannot be copied into a @fn closure
copy x
//~^ ERROR copying a noncopyable value
};
(move x,f)
}
fn closure3(+x: core::util::NonCopyable) {
do task::spawn |copy x| {
//~^ ERROR copying a noncopyable value
//~^^ NOTE non-copyable value cannot be copied into a ~fn closure
error!("%?", x);
}
error!("%?", x);
}
fn main() {
}

View file

@ -25,7 +25,7 @@ struct Foo {
fn main() { fn main() {
let a = Foo { x: 1, y: Bar { x: 5 } }; let a = Foo { x: 1, y: Bar { x: 5 } };
let c = Foo { x: 4, .. a}; //~ ERROR copying a noncopyable value let c = Foo { x: 4, .. a}; //~ ERROR cannot copy field `y` of base expression, which has a noncopyable type
io::println(fmt!("%?", c)); io::println(fmt!("%?", c));
} }

View file

@ -1,17 +0,0 @@
// Copyright 2012 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.
// error-pattern:moving out of captured outer immutable variable in a stack closure
fn test(-x: uint) {}
fn main() {
let i = 3;
for uint::range(0, 10) |_x| {test(move i)}
}

View file

@ -31,11 +31,14 @@ fn bar() {
} }
fn car() { fn car() {
// Here, i is mutable, but *explicitly* copied: // Here, i is mutable, but *explicitly* shadowed copied:
let mut i = 0; let mut i = 0;
while i < 10 { while i < 10 {
do task::spawn |copy i| { {
user(i); let i = i;
do task::spawn {
user(i);
}
} }
i += 1; i += 1;
} }

View file

@ -8,11 +8,12 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// error-pattern: use of moved value fn take(_x: ~int) {}
fn main() { fn main() {
let x = 3;
let y = move x;
debug!("%d", x);
}
let x: ~int = ~25;
loop {
take(x); //~ ERROR use of moved value: `x`
}
}

Some files were not shown because too many files have changed in this diff Show more