diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2013-01-10 10:59:58 -0800 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2013-01-31 12:09:00 -0800 |
| commit | 0682ad0eb9a6b268498a81b2e16a40544e44f0fa (patch) | |
| tree | 694819bae28cd319401c121afa4daa00adcbdde2 | |
| parent | 42b462e0765f02fd7bb0f2613240ae2489a47fee (diff) | |
| download | rust-0682ad0eb9a6b268498a81b2e16a40544e44f0fa.tar.gz rust-0682ad0eb9a6b268498a81b2e16a40544e44f0fa.zip | |
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.
125 files changed, 2685 insertions, 2644 deletions
diff --git a/doc/rust.md b/doc/rust.md index e31441c7cd2..7a5b8dfd004 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1234,7 +1234,7 @@ For example: ~~~~ trait Num { - static pure fn from_int(n: int) -> self; + static pure fn from_int(n: int) -> Self; } impl float: Num { static pure fn from_int(n: int) -> float { n as float } diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md index 26541eca7d3..98ed6b86584 100644 --- a/doc/tutorial-tasks.md +++ b/doc/tutorial-tasks.md @@ -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_. ~~~ -# fn random() -> int { 100 } -# fn sleep_for(i: int) { for i.times { task::yield() } } +# fn random() -> uint { 100 } +# fn sleep_for(i: uint) { for i.times { task::yield() } } # do task::try::<()> { let (time1, time2) = (random(), random()); do task::spawn_unlinked { diff --git a/src/etc/tidy.py b/src/etc/tidy.py index 65737c53e61..997a9dd50fd 100644 --- a/src/etc/tidy.py +++ b/src/etc/tidy.py @@ -51,7 +51,7 @@ try: report_err("TODO is deprecated; use FIXME") idx = line.find("// NOTE") if idx != -1: - report_warn("NOTE:" + line[idx + len("// NOTE"):]) + report_warn("NOTE" + line[idx + len("// NOTE"):]) if (line.find('\t') != -1 and fileinput.filename().find("Makefile") == -1): report_err("tab character") diff --git a/src/libcargo/cargo.rc b/src/libcargo/cargo.rc index f2a7c5c3cf4..bb5e3f9f74d 100644 --- a/src/libcargo/cargo.rc +++ b/src/libcargo/cargo.rc @@ -1965,19 +1965,17 @@ pub fn main() { c = configure(o); } - let c = &move c; - match o.free[1] { - ~"init" => cmd_init(c), - ~"install" => cmd_install(c), - ~"uninstall" => cmd_uninstall(c), - ~"list" => cmd_list(c), - ~"search" => cmd_search(c), - ~"sources" => cmd_sources(c), + ~"init" => cmd_init(&c), + ~"install" => cmd_install(&c), + ~"uninstall" => cmd_uninstall(&c), + ~"list" => cmd_list(&c), + ~"search" => cmd_search(&c), + ~"sources" => cmd_sources(&c), _ => cmd_usage() } - dump_cache(c); - dump_sources(c); + dump_cache(&c); + dump_sources(&c); } diff --git a/src/libcore/private/global.rs b/src/libcore/private/global.rs index ee20fb665be..f129022957c 100644 --- a/src/libcore/private/global.rs +++ b/src/libcore/private/global.rs @@ -177,8 +177,10 @@ fn get_global_state() -> Exclusive<GlobalState> { let state = ~exclusive(state); // Convert it to an integer - let state_ptr: &Exclusive<GlobalState> = state; - let state_i: int = unsafe { transmute(state_ptr) }; + let state_i: int = unsafe { + let state_ptr: &Exclusive<GlobalState> = state; + transmute(state_ptr) + }; // Swap our structure into the global pointer let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) }; diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 9ddafee6938..62d317abb95 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -837,11 +837,19 @@ fn test_run_basic() { } #[test] +struct Wrapper { + mut f: Option<Chan<()>> +} + +#[test] fn test_add_wrapper() { let (po, ch) = stream::<()>(); let b0 = task(); + let ch = Wrapper { f: Some(ch) }; let b1 = do b0.add_wrapper |body| { + let ch = Wrapper { f: Some(ch.f.swap_unwrap()) }; fn~(move body) { + let ch = ch.f.swap_unwrap(); body(); ch.send(()); } @@ -929,9 +937,12 @@ fn test_spawn_sched_childs_on_default_sched() { // Assuming tests run on the default scheduler let default_id = unsafe { rt::rust_get_sched_id() }; + let ch = Wrapper { f: Some(ch) }; do spawn_sched(SingleThreaded) { let parent_sched_id = unsafe { rt::rust_get_sched_id() }; + let ch = Wrapper { f: Some(ch.f.swap_unwrap()) }; do spawn { + let ch = ch.f.swap_unwrap(); let child_sched_id = unsafe { rt::rust_get_sched_id() }; assert parent_sched_id != child_sched_id; assert child_sched_id == default_id; diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index 7440f3f70ac..560b26951a6 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -191,7 +191,7 @@ pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] { let mut minimized = ~[]; for rpaths.each |rpath| { let s = rpath.to_str(); - if !set.contains_key(s) { + if !set.contains_key_ref(&s) { minimized.push(/*bad*/copy *rpath); set.insert(s, ()); } diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 12281e0b980..eca6a598f8d 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -16,7 +16,7 @@ use front; use lib::llvm::llvm; use metadata::{creader, cstore, filesearch}; use metadata; -use middle::{trans, freevars, kind, ty, typeck, lint}; +use middle::{trans, freevars, kind, ty, typeck, lint, astencode}; use middle; use session::{Session, Session_, OptLevel, No, Less, Default, Aggressive}; use session; @@ -281,20 +281,26 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg, time(time_passes, ~"loop checking", || middle::check_loop::check_crate(ty_cx, crate)); - time(time_passes, ~"mode computation", || - middle::mode::compute_modes(ty_cx, method_map, crate)); + let middle::moves::MoveMaps {moves_map, variable_moves_map, + capture_map} = + time(time_passes, ~"compute moves", || + middle::moves::compute_moves(ty_cx, method_map, crate)); 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 = 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) = time(time_passes, ~"borrow checking", || middle::borrowck::check_crate(ty_cx, method_map, - last_use_map, crate)); + moves_map, capture_map, + crate)); time(time_passes, ~"kind checking", || 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)}; } - let maps = {mutbl_map: mutbl_map, - root_map: root_map, - last_use_map: last_use_map, - method_map: method_map, - vtable_map: vtable_map, - write_guard_map: write_guard_map}; + let maps = astencode::Maps { + mutbl_map: mutbl_map, + root_map: root_map, + last_use_map: last_use_map, + method_map: method_map, + vtable_map: vtable_map, + write_guard_map: write_guard_map, + moves_map: moves_map, + capture_map: capture_map + }; time(time_passes, ~"translation", || 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)); for flags.each |lint_name| { let lint_name = str::replace(*lint_name, ~"-", ~"_"); - match lint_dict.find(lint_name) { + match lint_dict.find(/*bad*/ copy lint_name) { None => { early_error(demitter, fmt!("unknown %s flag: %s", level_name, lint_name)); diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index 9195da2a166..a7c071919ff 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -190,7 +190,7 @@ pub fn metas_in_cfg(cfg: ast::crate_cfg, if !has_cfg_metas { return true; } 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; diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index 8e4d17e6eba..53c2f71c548 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -46,7 +46,7 @@ pub fn modify_for_testing(sess: session::Session, // We generate the test harness when building in the 'test' // configuration, either with the '--test' or '--cfg test' // 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")); if should_test { @@ -510,7 +510,7 @@ fn mk_test_wrapper(cx: test_ctxt, let wrapper_expr = ast::expr { 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 }; diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index 8f2774f15fa..375a3b43aad 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -1344,10 +1344,10 @@ pub fn mk_type_names() -> type_names { } 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 { unsafe { 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(copy outer0, ty); + let outer = vec::append_one(outer0.to_vec(), 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 { let mut s = ~""; let mut first: bool = true; diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 52f7c874af4..37c19e80600 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -133,7 +133,8 @@ pub enum astencode_tag { // Reserves 0x50 -- 0x6f tag_table_vtable_map = 0x61, tag_table_adjustments = 0x62, 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; diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 96f64c2a088..e91c4f93864 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -163,8 +163,8 @@ fn visit_item(e: env, i: @ast::item) { None => /*bad*/copy *e.intr.get(i.ident) }; if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() { - already_added = !cstore::add_used_library(cstore, - foreign_name); + already_added = + !cstore::add_used_library(cstore, copy foreign_name); } if !link_args.is_empty() && already_added { 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", ~[]); debug!("resolving dep crate %s ver: %s hash: %s", *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) { Some(local_cnum) => { debug!("already have it"); diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index ebedc341319..49d7bfeb2f9 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -648,14 +648,22 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: writer::Encoder, /* Encode the dtor */ do struct_def.dtor.iter |dtor| { 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.str_of(item.ident) + ~"_dtor"), - path, if tps.len() > 0u { - Some(ii_dtor(*dtor, item.ident, tps, + path, + if tps.len() > 0u { + Some(ii_dtor(copy *dtor, + item.ident, + copy tps, local_def(item.id))) } - else { None }, tps); + else { + None + }, + tps); } /* 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); visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor { visit_expr: |_e, _cx, _v| { }, - visit_item: |i, cx, v, copy ebml_w| { - visit::visit_item(i, cx, v); - match ecx.tcx.items.get(i.id) { - ast_map::node_item(_, pt) => { - encode_info_for_item(ecx, ebml_w, i, index, *pt); - } - _ => fail ~"bad item" + visit_item: { + let ebml_w = copy ebml_w; + |i, cx, v| { + visit::visit_item(i, cx, v); + match ecx.tcx.items.get(i.id) { + ast_map::node_item(_, pt) => { + encode_info_for_item(ecx, ebml_w, i, + index, *pt); + } + _ => fail ~"bad item" + } } }, - visit_foreign_item: |ni, cx, v, copy ebml_w| { - visit::visit_foreign_item(ni, cx, v); - match ecx.tcx.items.get(ni.id) { - ast_map::node_foreign_item(_, abi, pt) => { - encode_info_for_foreign_item(ecx, ebml_w, ni, - index, /*bad*/copy *pt, abi); - } - // case for separate item and foreign-item tables - _ => fail ~"bad foreign item" + visit_foreign_item: { + let ebml_w = copy ebml_w; + |ni, cx, v| { + visit::visit_foreign_item(ni, cx, v); + match ecx.tcx.items.get(ni.id) { + ast_map::node_foreign_item(_, abi, pt) => { + encode_info_for_foreign_item(ecx, ebml_w, ni, + index, /*bad*/copy *pt, + abi); + } + // case for separate item and foreign-item tables + _ => fail ~"bad foreign item" + } } - } - ,.. *visit::default_visitor() + }, + ..*visit::default_visitor() })); ebml_w.end_tag(); return /*bad*/copy *index; diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 3979eeee87e..d523b198b31 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -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 { let attrs = decoder::get_crate_attributes(crate_data); 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], - local_metas: ~[@ast::meta_item]) -> bool { + local_metas: &[@ast::meta_item]) -> bool { debug!("matching %u metadata requirements against %u items", vec::len(local_metas), vec::len(extern_metas)); diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index c50666d039f..4c2937b952e 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -275,6 +275,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t { } 'p' => { 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); } 's' => { @@ -422,7 +423,6 @@ fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg { fn parse_mode(st: @pstate) -> ast::mode { let m = ast::expl(match next(st) { - '-' => ast::by_move, '+' => ast::by_copy, '=' => ast::by_ref, '#' => ast::by_val, diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index f06bbb5a0a8..97f8c8f12a1 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -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) { match ty::resolved_mode(cx.tcx, m) { - by_move => w.write_char('-'), by_copy => w.write_char('+'), by_ref => w.write_char('='), by_val => w.write_char('#') diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index ff7e5363d7c..95a9f602a6d 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -22,7 +22,7 @@ use metadata::tyencode; use middle::freevars::freevar_entry; use middle::typeck::{method_origin, method_map_entry, vtable_res}; use middle::typeck::{vtable_origin}; -use middle::{ty, typeck}; +use middle::{ty, typeck, moves}; use middle; use util::ppaux::ty_to_str; @@ -51,19 +51,21 @@ use syntax; use writer = std::ebml::writer; // Auxiliary maps of things to be encoded -pub type maps = { +pub struct Maps { mutbl_map: middle::borrowck::mutbl_map, root_map: middle::borrowck::root_map, last_use_map: middle::liveness::last_use_map, method_map: middle::typeck::method_map, vtable_map: middle::typeck::vtable_map, write_guard_map: middle::borrowck::write_guard_map, -}; + moves_map: middle::moves::MovesMap, + capture_map: middle::moves::CaptureMap, +} type decode_ctxt = @{ cdata: cstore::crate_metadata, tcx: ty::ctxt, - maps: maps + maps: Maps }; type extended_decode_ctxt_ = { @@ -91,7 +93,7 @@ pub fn encode_inlined_item(ecx: @e::encode_ctxt, ebml_w: writer::Encoder, path: &[ast_map::path_elt], ii: ast::inlined_item, - maps: maps) { + maps: Maps) { debug!("> Encoding inlined item: %s::%s (%u)", ast_map::path_to_str(path, ecx.tcx.sess.parse_sess.interner), 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, tcx: ty::ctxt, - maps: maps, + maps: Maps, +path: ast_map::path, par_doc: ebml::Doc) -> Option<ast::inlined_item> { @@ -514,6 +516,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 trait read_method_map_entry_helper { @@ -788,10 +814,11 @@ impl writer::Encoder: write_tag_and_id { } fn encode_side_tables_for_ii(ecx: @e::encode_ctxt, - maps: maps, + maps: Maps, ebml_w: writer::Encoder, ii: ast::inlined_item) { do ebml_w.wr_tag(c::tag_table as uint) { + let ebml_w = copy ebml_w; ast_util::visit_ids_for_inlined_item( ii, 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, - maps: maps, + maps: Maps, ebml_w: writer::Encoder, id: ast::node_id) { 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| { - do ebml_w.tag(c::tag_table_value_mode) { + for maps.moves_map.find(id).each |_| { + 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); 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 // are not used during trans. - do self.read_opaque |doc| { - tydecode::parse_ty_data( + return do self.read_opaque |doc| { + + let ty = tydecode::parse_ty_data( 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. */ - match source { + let r = match source { NominalType | TypeWithId => xcx.tr_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, ()); } else if tag == (c::tag_table_legacy_boxed_trait as uint) { dcx.tcx.legacy_boxed_traits.insert(id, ()); + } else if tag == (c::tag_table_moves_map as uint) { + dcx.maps.moves_map.insert(id, ()); } else { let val_doc = entry_doc[c::tag_table_val as uint]; 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); } else if tag == (c::tag_table_node_type as uint) { 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); } else if tag == (c::tag_table_node_type_subst as uint) { 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); adj.tr(xcx); dcx.tcx.adjustments.insert(id, adj); - } else if tag == (c::tag_table_value_mode as uint) { - let vm: ty::ValueMode = Decodable::decode(val_dsr); - dcx.tcx.value_modes.insert(id, vm); + } else if tag == (c::tag_table_capture_map as uint) { + let cvars = + at_vec::from_owned( + val_dsr.read_to_vec( + || val_dsr.read_capture_var(xcx))); + dcx.maps.capture_map.insert(id, cvars); } else { xcx.dcx.tcx.sess.bug( fmt!("unknown tag found in side tables: %x", tag)); diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index b1c306fbd91..76b3aea2dd1 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -19,13 +19,15 @@ 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::{MoveError, MoveOk, MoveFromIllegalCmt}; +use middle::borrowck::{MoveWhileBorrowed}; 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_special, cmt, gc_ptr, loan_path, lp_arg}; use middle::mem_categorization::{lp_comp, lp_deref, lp_local}; -use middle::ty::{CopyValue, MoveValue, ReadValue}; use middle::ty; use util::ppaux::ty_to_str; @@ -42,7 +44,7 @@ use syntax::print::pprust; use syntax::visit; enum check_loan_ctxt = @{ - bccx: borrowck_ctxt, + bccx: @BorrowckCtxt, req_maps: req_maps, reported: HashMap<ast::node_id, ()>, @@ -63,9 +65,9 @@ enum purity_cause { pc_cmt(bckerr) } -pub fn check_loans(bccx: borrowck_ctxt, - req_maps: req_maps, - crate: @ast::crate) { +pub fn check_loans(bccx: @BorrowckCtxt, + req_maps: req_maps, + crate: @ast::crate) { let clcx = check_loan_ctxt(@{bccx: bccx, req_maps: req_maps, reported: HashMap(), @@ -471,12 +473,40 @@ impl check_loan_ctxt { } } - fn check_move_out(ex: @ast::expr) { - let cmt = self.bccx.cat_expr(ex); - self.check_move_out_from_cmt(cmt); + fn check_move_out_from_expr(ex: @ast::expr) { + match ex.node { + 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)", self.bccx.cmt_to_repr(cmt)); @@ -493,59 +523,27 @@ impl check_loan_ctxt { // Nothing else. _ => { - self.bccx.span_err( - cmt.span, - fmt!("moving out of %s", self.bccx.cmt_to_str(cmt))); - return; + return MoveFromIllegalCmt(cmt); } } self.bccx.add_to_mutbl_map(cmt); // check for a conflicting loan: - let lp = match cmt.lp { - None => return, - Some(lp) => lp - }; - 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; + for cmt.lp.each |lp| { + for self.walk_loans_of(cmt.id, *lp) |loan| { + return MoveWhileBorrowed(cmt, loan.cmt); } - 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, callee: Option<@ast::expr>, callee_id: ast::node_id, callee_span: span, - args: ~[@ast::expr]) { + args: &[@ast::expr]) { match self.purity(expr.id) { None => {} 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, 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); do save_and_restore(&mut(self.declared_purity)) { 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( copy self.declared_purity, ty::ty_fn_purity(fty), - ty::ty_fn_proto(fty)); + fty_proto); match fk { 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); + + 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, @@ -632,48 +665,24 @@ fn check_loans_in_expr(expr: @ast::expr, self.check_for_conflicting_loans(expr.id); - // If this is a move, check it. - match self.tcx().value_modes.find(expr.id) { - Some(MoveValue) => self.check_move_out(expr), - Some(ReadValue) | Some(CopyValue) | None => {} + if self.bccx.moves_map.contains_key(expr.id) { + self.check_move_out_from_expr(expr); } - match /*bad*/copy expr.node { - ast::expr_path(*) if self.bccx.last_use_map.contains_key(expr.id) => { - self.check_last_use(expr); - } - + match expr.node { ast::expr_swap(l, r) => { self.check_assignment(at_swap, l); self.check_assignment(at_swap, r); } - ast::expr_unary_move(src) => { - self.check_move_out(src); - } ast::expr_assign(dest, _) | ast::expr_assign_op(_, dest, _) => { self.check_assignment(at_straight_up, dest); } - ast::expr_fn(_, _, _, cap_clause) | - ast::expr_fn_block(_, _, cap_clause) => { - 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, ref args, _) => { + self.check_call(expr, Some(f), f.id, f.span, *args); } - ast::expr_call(f, args, _) => { - self.check_call(expr, Some(f), f.id, f.span, args); - } - ast::expr_method_call(_, _, _, args, _) => { - self.check_call(expr, None, expr.callee_id, expr.span, args); + ast::expr_method_call(_, _, _, ref args, _) => { + self.check_call(expr, None, expr.callee_id, expr.span, *args); } ast::expr_index(_, rval) | ast::expr_binary(_, _, rval) @@ -692,6 +701,18 @@ fn check_loans_in_expr(expr: @ast::expr, 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. + } _ => { } } diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index a0119a19b9a..bfcdacf811d 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -18,8 +18,8 @@ use core::prelude::*; -use middle::borrowck::preserve::{preserve_condition, pc_ok, pc_if_pure}; -use middle::borrowck::{Loan, bckerr, bckres, borrowck_ctxt, err_mutbl}; +use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure}; +use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl}; use middle::borrowck::{req_maps}; use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant}; 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 /// 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`. -enum gather_loan_ctxt = @{bccx: borrowck_ctxt, +enum gather_loan_ctxt = @{bccx: @BorrowckCtxt, req_maps: req_maps, mut item_ub: ast::node_id, mut root_ub: 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, req_maps: {req_loan_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); for vec::each2(args, arg_tys) |arg, arg_ty| { match ty::resolved_mode(self.tcx(), arg_ty.mode) { - ast::by_ref => { - let arg_cmt = self.bccx.cat_expr(*arg); - self.guarantee_valid(arg_cmt, m_imm, scope_r); - } - ast::by_val | ast::by_move | ast::by_copy => {} + ast::by_ref => { + let arg_cmt = self.bccx.cat_expr(*arg); + self.guarantee_valid(arg_cmt, m_imm, scope_r); + } + ast::by_val | ast::by_copy => {} } } 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); for vec::each2(args, arg_tys) |arg, arg_ty| { match ty::resolved_mode(self.tcx(), arg_ty.mode) { - ast::by_ref => { - let arg_cmt = self.bccx.cat_expr(*arg); - self.guarantee_valid(arg_cmt, m_imm, scope_r); - } - ast::by_val | ast::by_move | ast::by_copy => {} + ast::by_ref => { + let arg_cmt = self.bccx.cat_expr(*arg); + self.guarantee_valid(arg_cmt, m_imm, scope_r); + } + ast::by_val | ast::by_copy => {} } } @@ -374,7 +374,7 @@ impl gather_loan_ctxt { // matches with the actual mutability (but if an immutable // pointer is desired, that is ok as long as we are pure) None => { - let result: bckres<preserve_condition> = { + let result: bckres<PreserveCondition> = { do self.check_mutbl(req_mutbl, cmt).chain |pc1| { do self.bccx.preserve(cmt, scope_r, self.item_ub, @@ -385,16 +385,16 @@ impl gather_loan_ctxt { }; match result { - Ok(pc_ok) => { - debug!("result of preserve: pc_ok"); + Ok(PcOk) => { + debug!("result of preserve: PcOk"); // we were able guarantee the validity of the ptr, // perhaps by rooting or because it is immutably // rooted. good. self.bccx.stable_paths += 1; } - Ok(pc_if_pure(ref e)) => { - debug!("result of preserve: %?", pc_if_pure((*e))); + Ok(PcIfPure(ref e)) => { + debug!("result of preserve: %?", PcIfPure((*e))); // we are only able to guarantee the validity if // the scope is pure @@ -443,25 +443,25 @@ impl gather_loan_ctxt { // mutable memory. fn check_mutbl(&self, req_mutbl: ast::mutability, - cmt: cmt) -> bckres<preserve_condition> { + cmt: cmt) -> bckres<PreserveCondition> { debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)", req_mutbl, cmt.mutbl); if req_mutbl == m_const || req_mutbl == cmt.mutbl { debug!("required is const or they are the same"); - Ok(pc_ok) + Ok(PcOk) } else { let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) }; if req_mutbl == m_imm { // if this is an @mut box, then it's generally OK to borrow as // &imm; this will result in a write guard if cmt.cat.is_mutable_box() { - Ok(pc_ok) + Ok(PcOk) } else { // you can treat mutable things as imm if you are pure debug!("imm required, must be pure"); - Ok(pc_if_pure(e)) + Ok(PcIfPure(e)) } } else { Err(e) @@ -556,11 +556,6 @@ impl gather_loan_ctxt { match pat.node { ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => { 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) => { // ref x or ref x @ p --- creates a ptr which must // remain valid for the scope of the match @@ -582,9 +577,9 @@ impl gather_loan_ctxt { self.guarantee_valid(cmt, mutbl, scope_r); } } - ast::bind_infer => { - // Nothing to do here; this is either a copy or a move; - // thus either way there is nothing to check. Yay! + ast::bind_by_copy | ast::bind_infer => { + // Nothing to do here; neither copies nor moves induce + // borrows. } } } diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs index be12ae9dff4..c4b36085ec8 100644 --- a/src/librustc/middle/borrowck/loan.rs +++ b/src/librustc/middle/borrowck/loan.rs @@ -43,7 +43,7 @@ XXX --- much more needed, don't have time to write this all up now 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::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp}; 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; -impl borrowck_ctxt { - fn loan(cmt: cmt, +impl BorrowckCtxt { + fn loan(&self, + cmt: cmt, scope_region: ty::Region, mutbl: ast::mutability) -> bckres<~[Loan]> { let lc = LoanContext { @@ -77,7 +78,7 @@ impl borrowck_ctxt { } struct LoanContext { - bccx: borrowck_ctxt, + bccx: &BorrowckCtxt, // the region scope for which we must preserve the memory scope_region: ty::Region, diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 9f9d5f20b69..c0c59c68699 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -230,6 +230,7 @@ use middle::liveness; use middle::mem_categorization::*; use middle::region; use middle::ty; +use middle::moves; use util::common::{indenter, stmt_set}; use util::ppaux::{expr_repr, note_and_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 preserve; -pub fn check_crate(tcx: ty::ctxt, - method_map: typeck::method_map, - last_use_map: liveness::last_use_map, - crate: @ast::crate) - -> (root_map, mutbl_map, write_guard_map) { - - let bccx = borrowck_ctxt_(@{tcx: tcx, +pub fn check_crate( + tcx: ty::ctxt, + method_map: typeck::method_map, + moves_map: moves::MovesMap, + capture_map: moves::CaptureMap, + crate: @ast::crate) -> (root_map, mutbl_map, write_guard_map) +{ + let bccx = @BorrowckCtxt {tcx: tcx, method_map: method_map, - last_use_map: last_use_map, + moves_map: moves_map, + capture_map: capture_map, root_map: root_map(), mutbl_map: HashMap(), write_guard_map: HashMap(), @@ -271,7 +274,7 @@ pub fn check_crate(tcx: ty::ctxt, mut loaned_paths_imm: 0, mut stable_paths: 0, mut req_pure_paths: 0, - mut guaranteed_paths: 0}); + mut guaranteed_paths: 0}; let req_maps = gather_loans::gather_loans(bccx, 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); - fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str { + fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str { let stat_f = stat as float; let total = bccx.guaranteed_paths as float; fmt!("%u (%.0f%%)", stat , stat_f * 100f / total) @@ -302,23 +305,22 @@ pub fn check_crate(tcx: ty::ctxt, // ---------------------------------------------------------------------- // Type definitions -pub type borrowck_ctxt_ = {tcx: ty::ctxt, - method_map: typeck::method_map, - last_use_map: liveness::last_use_map, - root_map: root_map, - mutbl_map: mutbl_map, - write_guard_map: write_guard_map, - stmt_map: stmt_set, - - // Statistics: - mut loaned_paths_same: uint, - mut loaned_paths_imm: uint, - mut stable_paths: uint, - mut req_pure_paths: uint, - mut guaranteed_paths: uint}; - -pub enum borrowck_ctxt { - borrowck_ctxt_(@borrowck_ctxt_) +pub struct BorrowckCtxt { + tcx: ty::ctxt, + method_map: typeck::method_map, + moves_map: moves::MovesMap, + capture_map: moves::CaptureMap, + root_map: root_map, + mutbl_map: mutbl_map, + write_guard_map: write_guard_map, + stmt_map: stmt_set, + + // Statistics: + mut loaned_paths_same: uint, + mut loaned_paths_imm: uint, + mut stable_paths: uint, + mut req_pure_paths: uint, + mut guaranteed_paths: uint } pub struct RootInfo { @@ -371,6 +373,12 @@ pub struct bckerr { 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` pub type bckres<T> = Result<T, bckerr>; @@ -411,60 +419,62 @@ pub fn root_map() -> root_map { // ___________________________________________________________________________ // Misc -pub impl borrowck_ctxt { - fn is_subregion_of(r_sub: ty::Region, r_sup: ty::Region) -> bool { +pub impl BorrowckCtxt { + 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) } - fn cat_expr(expr: @ast::expr) -> cmt { + fn cat_expr(&self, expr: @ast::expr) -> cmt { 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) } - fn cat_expr_autoderefd(expr: @ast::expr, + fn cat_expr_autoderefd(&self, expr: @ast::expr, adj: @ty::AutoAdjustment) -> cmt { 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, ty: ty::t, def: ast::def) -> cmt { 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, cmt: cmt) -> cmt { cat_variant(self.tcx, self.method_map, arg, enum_did, cmt) } - fn cat_discr(cmt: cmt, match_id: ast::node_id) -> cmt { - return @cmt_ { cat: cat_discr(cmt, match_id),.. *cmt }; + fn cat_discr(&self, cmt: cmt, match_id: ast::node_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, 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(); mc.cat_pattern(cmt, pat, op); } - fn report_if_err(bres: bckres<()>) { + fn report_if_err(&self, bres: bckres<()>) { match bres { Ok(()) => (), Err(ref e) => self.report((*e)) } } - fn report(err: bckerr) { + fn report(&self, err: bckerr) { self.span_err( err.cmt.span, fmt!("illegal borrow: %s", @@ -472,15 +482,15 @@ pub impl borrowck_ctxt { 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); } - fn span_note(s: span, +m: ~str) { + fn span_note(&self, s: span, +m: ~str) { 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 { cat_local(id) | cat_arg(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 { err_mutbl(req) => { 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; match code { 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, method_map: self.method_map}; 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, method_map: self.method_map}; 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, method_map: self.method_map}; 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=%?)", loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl) } diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs index 048fd7b623c..570b439cf8c 100644 --- a/src/librustc/middle/borrowck/preserve.rs +++ b/src/librustc/middle/borrowck/preserve.rs @@ -15,7 +15,7 @@ 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_out_of_root_scope, err_out_of_scope}; 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; -pub enum preserve_condition { - pc_ok, - pc_if_pure(bckerr) +pub enum PreserveCondition { + PcOk, + PcIfPure(bckerr) } -impl preserve_condition { +impl PreserveCondition { // combines two preservation conditions such that if either of // them requires purity, the result requires purity - fn combine(pc: preserve_condition) -> preserve_condition { - match self { - pc_ok => {pc} - pc_if_pure(_) => {self} + fn combine(&self, pc: PreserveCondition) -> PreserveCondition { + match *self { + PcOk => {pc} + PcIfPure(_) => {*self} } } } -impl borrowck_ctxt { - fn preserve(cmt: cmt, +impl BorrowckCtxt { + fn preserve(&self, + cmt: cmt, scope_region: ty::Region, item_ub: ast::node_id, - root_ub: ast::node_id) - -> bckres<preserve_condition> { - - let ctxt = preserve_ctxt({bccx: self, - scope_region: scope_region, - item_ub: item_ub, - root_ub: root_ub, - root_managed_data: true}); - (&ctxt).preserve(cmt) + root_ub: ast::node_id) -> bckres<PreserveCondition> + { + let ctxt = PreserveCtxt { + bccx: self, + scope_region: scope_region, + item_ub: item_ub, + root_ub: root_ub, + root_managed_data: true + }; + ctxt.preserve(cmt) } } -enum preserve_ctxt = { - bccx: borrowck_ctxt, +struct PreserveCtxt { + bccx: &BorrowckCtxt, // the region scope for which we must preserve the memory scope_region: ty::Region, @@ -76,13 +78,12 @@ enum preserve_ctxt = { // if false, do not attempt to root managed data root_managed_data: bool -}; - +} -priv impl &preserve_ctxt { - fn tcx() -> ty::ctxt { self.bccx.tcx } +impl PreserveCtxt { + fn tcx(&self) -> ty::ctxt { self.bccx.tcx } - fn preserve(cmt: cmt) -> bckres<preserve_condition> { + fn preserve(&self, cmt: cmt) -> bckres<PreserveCondition> { debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)", self.bccx.cmt_to_repr(cmt), self.root_ub, self.root_managed_data); @@ -94,7 +95,7 @@ priv impl &preserve_ctxt { self.compare_scope(cmt, ty::re_scope(self.item_ub)) } cat_special(sk_static_item) | cat_special(sk_method) => { - Ok(pc_ok) + Ok(PcOk) } cat_rvalue => { // when we borrow an rvalue, we can keep it rooted but only @@ -181,7 +182,7 @@ priv impl &preserve_ctxt { } cat_deref(_, _, unsafe_ptr) => { // Unsafe pointers are the user's problem - Ok(pc_ok) + Ok(PcOk) } cat_deref(base, derefs, gc_ptr(*)) => { // 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() { self.attempt_root(cmt, base, derefs) } else if base.mutbl == m_imm { - let non_rooting_ctxt = - preserve_ctxt({root_managed_data: false,.. **self}); - match (&non_rooting_ctxt).preserve(base) { - Ok(pc_ok) => { - Ok(pc_ok) + let non_rooting_ctxt = PreserveCtxt { + root_managed_data: false, + ..*self + }; + match non_rooting_ctxt.preserve(base) { + Ok(PcOk) => { + Ok(PcOk) } - Ok(pc_if_pure(_)) => { + Ok(PcIfPure(_)) => { debug!("must root @T, otherwise purity req'd"); 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 // in the *arm* vs the *match*. - let match_rooting_ctxt = - preserve_ctxt({scope_region: ty::re_scope(match_id), - .. **self}); - (&match_rooting_ctxt).preserve(base) + let match_rooting_ctxt = PreserveCtxt { + scope_region: ty::re_scope(match_id), + ..*self + }; + match_rooting_ctxt.preserve(base) } } } @@ -279,28 +283,29 @@ priv impl &preserve_ctxt { /// `base`) be found in an immutable location (that is, `base` /// must be immutable). Also requires that `base` itself is /// preserved. - fn require_imm(cmt: cmt, + fn require_imm(&self, + cmt: cmt, cmt_base: cmt, - code: bckerr_code) -> bckres<preserve_condition> { + code: bckerr_code) -> bckres<PreserveCondition> { // Variant contents and unique pointers: must be immutably // rooted to a preserved address. match self.preserve(cmt_base) { // the base is preserved, but if we are not mutable then // purity is required - Ok(pc_ok) => { + Ok(PcOk) => { match cmt_base.mutbl { m_mutbl | m_const => { - Ok(pc_if_pure(bckerr { cmt: cmt, code: code })) + Ok(PcIfPure(bckerr {cmt:cmt, code:code})) } m_imm => { - Ok(pc_ok) + Ok(PcOk) } } } // the base requires purity too, that's fine - Ok(pc_if_pure(ref e)) => { - Ok(pc_if_pure((*e))) + Ok(PcIfPure(ref e)) => { + Ok(PcIfPure((*e))) } // 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 /// is a subscope of `scope_ub`; if so, success. - fn compare_scope(cmt: cmt, - scope_ub: ty::Region) -> bckres<preserve_condition> { + fn compare_scope(&self, + cmt: cmt, + scope_ub: ty::Region) -> bckres<PreserveCondition> { if self.bccx.is_subregion_of(self.scope_region, scope_ub) { - Ok(pc_ok) + Ok(PcOk) } else { Err(bckerr { cmt:cmt, @@ -333,10 +339,8 @@ priv impl &preserve_ctxt { /// value live for longer than the current fn or else potentially /// require that an statically unbounded number of values be /// rooted (if a loop exists). - fn attempt_root(cmt: cmt, - base: cmt, - derefs: uint) - -> bckres<preserve_condition> { + fn attempt_root(&self, cmt: cmt, base: cmt, + derefs: uint) -> bckres<PreserveCondition> { if !self.root_managed_data { // normally, there is a root_ub; the only time that this // is none is when a boxed value is stored in an immutable @@ -387,7 +391,7 @@ priv impl &preserve_ctxt { scope: scope_to_use, freezes: cmt.cat.derefs_through_mutable_box() }); - return Ok(pc_ok); + return Ok(PcOk); } else { debug!("Unable to root"); return Err(bckerr { diff --git a/src/librustc/middle/capture.rs b/src/librustc/middle/capture.rs deleted file mode 100644 index 5b77228d1ab..00000000000 --- a/src/librustc/middle/capture.rs +++ /dev/null @@ -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; -} diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index de3056639cc..bfe73a9d15c 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -32,13 +32,13 @@ pub fn check_crate(tcx: ty::ctxt, crate: @crate) { expr_loop(ref b, _) => { (v.visit_block)((*b), {in_loop: true,.. cx}, v); } - expr_fn(_, _, _, _) => { + expr_fn(_, _, _) => { 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); } - 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 blk = (proto == ProtoBorrowed); (v.visit_block)((*b), {in_loop: true, can_ret: blk}, v); diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 4587d3b7b07..406fdbaeac7 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -16,6 +16,7 @@ use middle::pat_util::*; use middle::ty::*; use middle::ty; use middle::typeck::method_map; +use middle::moves; use util::ppaux::ty_to_str; use core::cmp; @@ -34,10 +35,16 @@ use syntax::visit; pub struct MatchCheckCtxt { tcx: ty::ctxt, method_map: method_map, + moves_map: moves::MovesMap } -pub fn check_crate(tcx: ty::ctxt, method_map: method_map, crate: @crate) { - let cx = @MatchCheckCtxt { tcx: tcx, method_map: method_map }; +pub fn check_crate(tcx: ty::ctxt, + 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_expr: |a,b,c| check_expr(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; } - match cx.tcx.value_modes.find(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"); - } - } + !cx.moves_map.contains_key(expr.id) } 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 arm.pats.each |pat| { let v = ~[*pat]; - match is_useful(cx, seen, v) { + match is_useful(cx, copy seen, v) { not_useful => { 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. // 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[0].len() == 0u { return not_useful; } 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, m: matrix, - +v: ~[@pat], + +v: &[@pat], +ctor: ctor, arity: uint, lty: ty::t) -> 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)); let could_be_useful = is_useful( cx, ms, specialize(cx, v, ctor, arity, lty).get()); @@ -467,7 +468,7 @@ pub fn wild() -> @pat { } pub fn specialize(cx: @MatchCheckCtxt, - +r: ~[@pat], + +r: &[@pat], ctor_id: ctor, arity: uint, left_ty: ty::t) @@ -729,21 +730,13 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt, for pats.each |pat| { do pat_bindings(def_map, *pat) |bm, id, span, _path| { match bm { + bind_by_copy => {} bind_by_ref(_) => { by_ref_span = Some(span); } - bind_by_move => { - any_by_move = true; - } - bind_by_value => {} bind_infer => { - match cx.tcx.value_modes.find(id) { - Some(MoveValue) => any_by_move = true, - Some(CopyValue) | Some(ReadValue) => {} - None => { - cx.tcx.sess.span_bug(span, ~"no mode for pat \ - binding"); - } + if cx.moves_map.contains_key(id) { + any_by_move = true; } } } @@ -781,18 +774,18 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt, do walk_pat(*pat) |p| { if pat_is_binding(def_map, p) { match p.node { - pat_ident(bind_by_move, _, sub) => check_move(p, sub), - pat_ident(bind_infer, _, sub) => { - match tcx.value_modes.find(p.id) { - Some(MoveValue) => check_move(p, sub), - Some(CopyValue) | Some(ReadValue) => {} - None => { - cx.tcx.sess.span_bug( - pat.span, ~"no mode for pat binding"); - } + pat_ident(_, _, sub) => { + if cx.moves_map.contains_key(p.id) { + check_move(p, sub); } } - _ => {} + _ => { + 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 &. // This is always illegal. let vt = visit::mk_vt(@visit::Visitor { - visit_pat: |pat, behind_bad_pointer, v| { - let error_out = || { - cx.tcx.sess.span_err(pat.span, ~"by-move pattern \ - bindings may not occur \ - behind @ or & bindings"); - }; + visit_pat: |pat, behind_bad_pointer: bool, v| { match pat.node { - pat_ident(binding_mode, _, sub) => { + pat_ident(_, _, sub) => { debug!("(check legality of move) checking pat \ ident with behind_bad_pointer %?", behind_bad_pointer); - match binding_mode { - bind_by_move if behind_bad_pointer => error_out(), - bind_infer if behind_bad_pointer => { - match cx.tcx.value_modes.find(pat.id) { - Some(MoveValue) => error_out(), - Some(CopyValue) | - Some(ReadValue) => {} - None => { - cx.tcx.sess.span_bug(pat.span, - ~"no mode for pat binding"); - } - } - } - _ => {} + + if behind_bad_pointer && + cx.moves_map.contains_key(pat.id) + { + cx.tcx.sess.span_err( + pat.span, + ~"by-move pattern \ + bindings may not occur \ + behind @ or & bindings"); } + match sub { None => {} Some(subpat) => { @@ -833,9 +817,11 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt, } } } + pat_box(subpat) | pat_region(subpat) => { (v.visit_pat)(subpat, true, v); } + _ => visit::visit_pat(pat, behind_bad_pointer, v) } }, diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 022fbe7f306..7edc345adc4 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -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 { - match (a, b) { - (const_int(a), const_int(b)) => { + match (&a, &b) { + (&const_int(a), &const_int(b)) => { if a == b { 0 } else if a < b { @@ -423,7 +423,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int { 1 } } - (const_uint(a), const_uint(b)) => { + (&const_uint(a), &const_uint(b)) => { if a == b { 0 } else if a < b { @@ -432,7 +432,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int { 1 } } - (const_float(a), const_float(b)) => { + (&const_float(a), &const_float(b)) => { if a == b { 0 } else if a < b { @@ -441,7 +441,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int { 1 } } - (const_str(ref a), const_str(ref b)) => { + (&const_str(ref a), &const_str(ref b)) => { if (*a) == (*b) { 0 } else if (*a) < (*b) { @@ -450,7 +450,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int { 1 } } - (const_bool(a), const_bool(b)) => { + (&const_bool(a), &const_bool(b)) => { if a == b { 0 } else if a < b { diff --git a/src/librustc/middle/freevars.rs b/src/librustc/middle/freevars.rs index ba4aae75fbd..68bb1db9af3 100644 --- a/src/librustc/middle/freevars.rs +++ b/src/librustc/middle/freevars.rs @@ -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>) { match expr.node { - ast::expr_fn(proto, _, _, _) => { + ast::expr_fn(proto, _, _) => { if proto != ast::ProtoBare { 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 } } + pub fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool { return vec::len(*get_freevars(tcx, fid)) != 0u; } diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index f3556778984..8977052e029 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -15,7 +15,6 @@ use middle::freevars; use middle::lint::{non_implicitly_copyable_typarams, implicit_copies}; use middle::liveness; use middle::pat_util; -use middle::ty::{CopyValue, MoveValue, ReadValue}; use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const}; use middle::ty; use middle::typeck; @@ -102,8 +101,6 @@ pub fn check_crate(tcx: ty::ctxt, let visit = visit::mk_vt(@visit::Visitor { visit_arm: check_arm, visit_expr: check_expr, - visit_stmt: check_stmt, - visit_block: check_block, visit_fn: check_fn, visit_ty: check_ty, 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(); } -// bool flag is only used for checking closures, -// 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); +type check_fn = fn@(ctx, @freevar_entry); // Yields the appropriate function to check the kind of closed over // variables. `id` is the node_id for some expression that creates the // closure. -pub 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>, - is_move: bool, var_t: ty::t, sp: span) { +fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) { + fn check_for_uniq(cx: ctx, fv: @freevar_entry) { // all captured data must be sendable, regardless of whether it is // moved in or copied in. Note that send implies owned. - if !check_send(cx, var_t, sp) { 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|`"))); - } + 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; } + // 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>, - is_move: bool, var_t: ty::t, sp: span) { + fn check_for_box(cx: ctx, fv: @freevar_entry) { // all captured data must be owned - if !check_durable(cx.tcx, var_t, sp) { 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|`"))); - } + 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; } + // 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>, - _is_move: bool, _var_t: ty::t, sp: span) { - // 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_block(_cx: ctx, _fv: @freevar_entry) { + // no restrictions } - fn check_for_bare(cx: ctx, _id: node_id, _fv: Option<@freevar_entry>, - _is_move: bool, _var_t: ty::t, sp: span) { - cx.tcx.sess.span_err(sp, ~"attempted dynamic environment capture"); + fn check_for_bare(cx: ctx, fv: @freevar_entry) { + cx.tcx.sess.span_err( + fv.span, + ~"attempted dynamic environment capture"); } 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 // 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_id: node_id, cx: ctx, v: visit::vt<ctx>) { - // Find the check function that enforces the appropriate bounds for this - // kind of function: - 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 - }; +fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span, + fn_id: node_id, cx: ctx, v: visit::vt<ctx>) { - // Iterate over any free variables that may not have appeared in the - // capture list. Ensure that they too are of the appropriate kind. + // Check kinds on free variables: + do with_appropriate_checker(cx, fn_id) |chk| { for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) |fv| { - let id = ast_util::def_id_of_def(fv.def).node; - - // 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); + chk(cx, *fv); } } visit::visit_fn(fk, decl, body, sp, fn_id, cx, v); } -pub fn check_block(b: blk, 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>) { +fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) { for vec::each(a.pats) |p| { 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 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>) { 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_unary(*)|expr_binary(*)|expr_method_call(*) => e.callee_id, _ => e.id }; - - // Handle any kind bounds on type parameters - do option::iter(&cx.tcx.node_type_substs.find(id_to_use)) |ts| { + do option::iter(&cx.tcx.node_type_substs.find(type_parameter_id)) |ts| { let bounds = match e.node { expr_path(_) => { 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()); } 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 { - expr_assign(_, ex) | - expr_unary(box(_), ex) | expr_unary(uniq(_), ex) | - expr_ret(Some(ex)) => { - maybe_copy(cx, ex, Some(("returned values must be copyable", - try_adding))); - } - expr_cast(source, _) => { - maybe_copy(cx, source, Some(("casted values must be copyable", - try_adding))); - check_cast_for_escaping_regions(cx, source, e); - check_kind_bounds_of_cast(cx, source, e); - } - expr_copy(expr) => check_copy_ex(cx, expr, false, - Some(("explicit copy requires a copyable argument", ""))), - // Vector add copies, but not "implicitly" - expr_assign_op(_, _, ex) => check_copy_ex(cx, ex, false, - Some(("assignment with operation requires \ - a copyable argument", ""))), - expr_binary(add, ls, rs) => { - let reason = Some(("binary operators require copyable arguments", - "")); - check_copy_ex(cx, ls, false, reason); - check_copy_ex(cx, rs, false, reason); - } - expr_rec(ref fields, def) | expr_struct(_, ref fields, def) => { - for (*fields).each |field| { maybe_copy(cx, field.node.expr, - Some(("record or struct fields require \ - copyable arguments", ""))); } - match def { - Some(ex) => { - // All noncopyable fields must be overridden - let t = ty::expr_ty(cx.tcx, ex); - let ty_fields = match /*bad*/copy ty::get(t).sty { - ty::ty_rec(f) => f, - ty::ty_struct(did, ref substs) => - ty::struct_fields(cx.tcx, did, &(*substs)), - _ => cx.tcx.sess.span_bug(ex.span, - ~"bad base expr type in record") - }; - for ty_fields.each |tf| { - 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(e.span, - ~"copying a noncopyable value"); - } - } - } - _ => {} + match e.node { + expr_cast(source, _) => { + check_cast_for_escaping_regions(cx, source, e); + check_kind_bounds_of_cast(cx, source, e); } - } - 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_copy(expr) => { + // Note: This is the only place where we must check whether the + // argument is copyable. This is not because this is the only + // kind of expression that may copy things, but rather because all + // other copies will have been converted to moves by by the + // `moves` pass if the value is not copyable. + check_copy(cx, + ty::expr_ty(cx.tcx, expr), + expr.span, + "explicit copy requires a copyable argument"); } - } - expr_method_call(_, _, _, args, _) => { - for ty::ty_fn_args(ty::node_id_to_type(cx.tcx, e.callee_id)).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_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_rec(ref fields, def) | expr_struct(_, ref fields, def) => { + match def { + Some(ex) => { + // All noncopyable fields must be overridden + let t = ty::expr_ty(cx.tcx, ex); + let ty_fields = match ty::get(t).sty { + ty::ty_rec(ref f) => { + copy *f + } + ty::ty_struct(did, ref substs) => { + ty::struct_fields(cx.tcx, did, substs) + } + _ => { + cx.tcx.sess.span_bug( + ex.span, + ~"bad base expr type in record") + } + }; + for ty_fields.each |tf| { + // If this field would not be copied, ok. + if fields.any(|f| f.node.ident == tf.ident) { loop; } + + // If this field is copyable, ok. + let kind = ty::type_kind(cx.tcx, tf.mt.ty); + if ty::kind_can_be_copied(kind) { loop; } + + cx.tcx.sess.span_err( + e.span, + fmt!("cannot copy field `%s` of base expression, \ + which has a noncopyable type", + *cx.tcx.sess.intr().get(tf.ident))); + } } + _ => {} } - _ => () } - } - 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); -} - -pub fn check_stmt(stmt: @stmt, 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", ""))), - _ => {} + expr_repeat(element, count_expr, _) => { + let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span); + if count > 1 { + let element_ty = ty::expr_ty(cx.tcx, element); + check_copy(cx, element_ty, element.span, + "repeated element will be copied"); } } - } - _ => {} + _ => {} } - visit::visit_stmt(stmt, cx, v); + visit::visit_expr(e, cx, v); } -pub fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) { +fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) { match aty.node { ty_path(_, id) => { 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)>) { - check_copy_ex(cx, ex, true, why); -} - -pub fn is_nullary_variant(cx: ctx, ex: @expr) -> bool { +fn is_nullary_variant(cx: ctx, ex: @expr) -> bool { match ex.node { expr_path(_) => { 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, - why: Option<(&str,&str)>) { - if ty::expr_is_lval(cx.tcx, cx.method_map, ex) && - - // a reference to a constant like `none`... no need to warn - // about *this* even if the type is Option<~int> - !is_nullary_variant(cx, ex) && - - // 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); +fn check_imm_free_var(cx: ctx, def: def, sp: span) { + match def { + def_local(_, is_mutbl) => { + if is_mutbl { + cx.tcx.sess.span_err( + sp, + ~"mutable variables cannot be implicitly captured"); } } - } - - fn is_autorefd(cx: ctx, ex: @expr) -> bool { - match cx.tcx.adjustments.find(ex.id) { - None => false, - Some(ref adj) => adj.autoref.is_some() - } - } -} - -pub fn check_imm_free_var(cx: ctx, def: def, sp: span) { - 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)); } - } - 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)>) { +fn check_copy(cx: ctx, ty: ty::t, sp: span, reason: &str) { let k = ty::type_kind(cx.tcx, ty); if !ty::kind_can_be_copied(k) { cx.tcx.sess.span_err(sp, ~"copying a noncopyable value"); - do why.map |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())); - }; + cx.tcx.sess.span_note(sp, fmt!("%s", reason)); } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 0f9fe013d6f..6d691438359 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -352,7 +352,7 @@ impl LanguageItemCollector { return; // Didn't match. } - match self.item_refs.find(value) { + match self.item_refs.find(/*bad*/copy value) { None => { // Didn't match. } diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index ea8a40520e8..1bc3827586c 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -344,7 +344,7 @@ impl ctxt { for triples.each |pair| { let (meta, level, lintname) = /*bad*/copy *pair; - match self.dict.find(lintname) { + match self.dict.find(/*bad*/ copy lintname) { None => { self.span_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, r: &ast::expr) -> bool { - let (lit, expr, swap) = match (l.node, r.node) { - (ast::expr_lit(_), _) => (l, r, true), - (_, ast::expr_lit(_)) => (r, l, false), + let (lit, expr, swap) = match (&l.node, &r.node) { + (&ast::expr_lit(_), _) => (l, r, true), + (_, &ast::expr_lit(_)) => (r, l, false), _ => return true }; // Normalize the binop so that the literal is always on the RHS in diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 90bcdb54e19..e5e0181bd3c 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -105,12 +105,11 @@ use core::prelude::*; -use middle::capture::{cap_move, cap_drop, cap_copy, cap_ref}; -use middle::capture; use middle::pat_util; -use middle::ty::MoveValue; use middle::ty; use middle::typeck; +use middle::moves; +use util::ppaux::ty_to_str; use core::cmp; 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, method_map: typeck::method_map, + variable_moves_map: moves::VariableMovesMap, + capture_map: moves::CaptureMap, crate: @crate) -> last_use_map { let visitor = visit::mk_vt(@visit::Visitor { visit_fn: visit_fn, @@ -213,7 +214,8 @@ pub fn check_crate(tcx: ty::ctxt, }); 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); tcx.sess.abort_if_errors(); return last_use_map; @@ -294,28 +296,35 @@ fn relevant_def(def: def) -> Option<node_id> { struct IrMaps { tcx: ty::ctxt, method_map: typeck::method_map, + variable_moves_map: moves::VariableMovesMap, + capture_map: moves::CaptureMap, last_use_map: last_use_map, mut num_live_nodes: uint, mut num_vars: uint, live_node_map: HashMap<node_id, LiveNode>, variable_map: HashMap<node_id, Variable>, - capture_map: HashMap<node_id, @~[CaptureInfo]>, + capture_info_map: HashMap<node_id, @~[CaptureInfo]>, mut var_kinds: ~[VarKind], 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 { IrMaps { tcx: tcx, method_map: method_map, + variable_moves_map: variable_moves_map, + capture_map: capture_map, last_use_map: last_use_map, num_live_nodes: 0, num_vars: 0, live_node_map: HashMap(), variable_map: HashMap(), - capture_map: HashMap(), + capture_info_map: HashMap(), var_kinds: ~[], lnks: ~[] } @@ -377,11 +386,11 @@ impl IrMaps { } 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] { - match self.capture_map.find(expr.id) { + match self.capture_info_map.find(expr.id) { Some(caps) => caps, None => { self.tcx.sess.span_bug(expr.span, ~"no registered caps"); @@ -397,7 +406,6 @@ impl IrMaps { let vk = self.var_kinds[*var]; debug!("Node %d is a last use of variable %?", expr_id, vk); match vk { - Arg(id, _, by_move) | Arg(id, _, by_copy) | Local(LocalInfo {id: id, kind: FromLetNoInitializer, _}) | 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(); // 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); 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); } - expr_fn(_, _, _, cap_clause) | - expr_fn_block(_, _, cap_clause) => { + expr_fn(_, _, _) | + expr_fn_block(_, _) => { // Interesting control flow (for loops can contain labeled // breaks or continues) 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 // in better error messages than just pointing at the closure // construction site. - let proto = ty::ty_fn_proto(ty::expr_ty(self.tcx, expr)); - let cvs = capture::compute_capture_vars(self.tcx, expr.id, proto, - cap_clause); + let cvs = self.capture_map.get(expr.id); let mut call_caps = ~[]; for cvs.each |cv| { match relevant_def(cv.def) { Some(rv) => { let cv_ln = self.add_live_node(FreeVarNode(cv.span)); let is_move = match cv.mode { - cap_move | cap_drop => true, // var must be dead afterwards - cap_copy | cap_ref => false // var can still be used + // var must be dead afterwards + moves::CapMove => true, + + // var can stil be used + moves::CapCopy | moves::CapRef => false }; call_caps.push(CaptureInfo {ln: cv_ln, 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_unary(*) | expr_fail(*) | 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_repeat(*) | expr_paren(*) => { visit::visit_expr(expr, self, vt); @@ -700,7 +712,7 @@ impl Liveness { } 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, @@ -760,7 +772,7 @@ impl Liveness { assert ln.is_valid(); 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(); 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) @@ -980,21 +992,22 @@ impl Liveness { // inputs passed by & mode should be considered live on exit: for decl.inputs.each |arg| { match ty::resolved_mode(self.tcx, arg.mode) { - by_ref | by_val => { - // These are "non-owned" modes, so register a read at - // the end. This will prevent us from moving out of - // such variables but also prevent us from registering - // last uses and so forth. - do pat_util::pat_bindings(self.tcx.def_map, arg.pat) - |_bm, arg_id, _sp, _path| { - let var = self.variable(arg_id, blk.span); - self.acc(self.s.exit_ln, var, ACC_READ); + by_val | by_ref => { + // By val and by ref do not own, so register a + // read at the end. This will prevent us from + // moving out of such variables but also prevent + // us from registering last uses and so forth. + do pat_util::pat_bindings(self.tcx.def_map, arg.pat) + |_bm, arg_id, _sp, _path| + { + 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) } - 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", expr_to_str(expr, self.tcx.sess.intr())); @@ -1105,8 +1118,8 @@ impl Liveness { // the construction of a closure itself is not important, // but we have to consider the closed over variables. - let caps = (*self.ir).captures(expr); - do (*caps).foldr(succ) |cap, succ| { + let caps = self.ir.captures(expr); + do caps.foldr(succ) |cap, succ| { self.init_from_succ(cap.ln, succ); let var = self.variable(cap.var_nid, expr.span); self.acc(cap.ln, var, ACC_READ | ACC_USE); @@ -1315,7 +1328,6 @@ impl Liveness { expr_assert(e) | expr_addr_of(_, e) | expr_copy(e) | - expr_unary_move(e) | expr_loop_body(e) | expr_do_body(e) | expr_cast(e, _) | @@ -1541,26 +1553,6 @@ fn check_arm(arm: arm, &&self: @Liveness, vt: vt<@Liveness>) { 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>) { match /*bad*/copy expr.node { 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); self.consider_last_use(expr, ln, *var); - match self.tcx.value_modes.find(expr.id) { - Some(MoveValue) => { + match self.ir.variable_moves_map.find(expr.id) { + None => {} + Some(entire_expr) => { debug!("(checking expr) is a move: `%s`", expr_to_str(expr, self.tcx.sess.intr())); - self.check_move_from_var(expr.span, ln, *var); - } - Some(v) => { - debug!("(checking expr) not a move (%?): `%s`", - v, - expr_to_str(expr, self.tcx.sess.intr())); - } - None => { - fail ~"no mode for lval"; + self.check_move_from_var(ln, *var, entire_expr); } } } @@ -1589,12 +1574,12 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) { } expr_fn(*) | expr_fn_block(*) => { - let caps = (*self.ir).captures(expr); - for (*caps).each |cap| { + let caps = self.ir.captures(expr); + for caps.each |cap| { let var = self.variable(cap.var_nid, expr.span); self.consider_last_use(expr, cap.ln, var); 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); } - expr_unary_move(r) => { - self.check_move_from_expr(r, vt); - - visit::visit_expr(expr, self, vt); - } - expr_assign_op(_, l, _) => { self.check_lvalue(l, 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 + expr_call(*) | expr_method_call(*) | expr_if(*) | expr_match(*) | expr_while(*) | expr_loop(*) | expr_index(*) | expr_field(*) | expr_vstore(*) | @@ -1659,7 +1626,8 @@ fn check_fn(_fk: visit::fn_kind, _decl: fn_decl, enum ReadKind { PossiblyUninitializedVariable, PossiblyUninitializedField, - MovedValue + MovedValue, + PartiallyMovedValue } impl @Liveness { @@ -1683,17 +1651,28 @@ impl @Liveness { } } - /* - Checks whether <var> is live on entry to any of the successors of <ln>. - If it is, report an error. - */ - fn check_move_from_var(span: span, ln: LiveNode, var: Variable) { + fn check_move_from_var(ln: LiveNode, + var: Variable, + move_expr: @expr) + { + /*! + * + * 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)", ln.to_str(), var.to_str()); match self.live_on_exit(ln, var) { 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) { Some(_) => {} - 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 - } + None => self.ir.add_last_use(expr.id, var) } } @@ -1808,36 +1746,84 @@ impl @Liveness { } } - fn report_illegal_move(move_span: span, - lnk: LiveNodeKind, - var: Variable) { - - // the only time that it is possible to have a moved value + fn report_illegal_move(lnk: LiveNodeKind, + var: Variable, + move_expr: @expr) + { + // the only time that it is possible to have a moved variable // used by ExitNode would be arguments or fields in a ctor. // we give a slightly different error message in those cases. if lnk == ExitNode { + // XXX this seems like it should be reported in the borrow checker let vk = self.ir.var_kinds[*var]; match vk { Arg(_, name, _) => { self.tcx.sess.span_err( - move_span, + move_expr.span, fmt!("illegal move from argument `%s`, which is not \ copy or move mode", self.tcx.sess.str_of(name))); return; } Local(*) | ImplicitRet => { self.tcx.sess.span_bug( - move_span, + move_expr.span, fmt!("illegal reader (%?) for `%?`", lnk, vk)); } } } - self.report_illegal_read(move_span, lnk, var, MovedValue); - self.tcx.sess.span_note( - move_span, ~"move of value occurred here"); + match move_expr.node { + expr_fn(*) | expr_fn_block(*) => { + 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, @@ -1845,13 +1831,13 @@ impl @Liveness { var: Variable, rk: ReadKind) { let msg = match rk { - PossiblyUninitializedVariable => { - ~"possibly uninitialized variable" - } - PossiblyUninitializedField => ~"possibly uninitialized field", - MovedValue => ~"moved value" + PossiblyUninitializedVariable => "possibly uninitialized \ + variable", + PossiblyUninitializedField => "possibly uninitialized field", + MovedValue => "moved value", + PartiallyMovedValue => "partially moved value" }; - let name = (*self.ir).variable_name(var); + let name = self.ir.variable_name(var); match lnk { FreeVarNode(span) => { self.tcx.sess.span_err( @@ -1863,8 +1849,7 @@ impl @Liveness { span, fmt!("use of %s: `%s`", msg, name)); } - ExitNode | - VarDefNode(_) => { + ExitNode | VarDefNode(_) => { self.tcx.sess.span_bug( chk_span, fmt!("illegal reader: %?", lnk)); @@ -1873,7 +1858,7 @@ impl @Liveness { } 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)} } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 687e9cdc74d..625b9889aad 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -394,7 +394,7 @@ pub impl &mem_categorization_ctxt { ast::expr_block(*) | ast::expr_loop(*) | ast::expr_match(*) | ast::expr_lit(*) | ast::expr_break(*) | ast::expr_mac(*) | 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); } } @@ -430,20 +430,16 @@ pub impl &mem_categorization_ctxt { // lp: loan path, must be none for aliasable things let m = if mutbl {m_mutbl} else {m_imm}; let lp = match ty::resolved_mode(self.tcx, mode) { - ast::by_move | ast::by_copy => { - Some(@lp_arg(vid)) - } - ast::by_ref => { - None - } - ast::by_val => { - // by-value is this hybrid mode where we have a - // pointer but we do not own it. This is not - // considered loanable because, for example, a by-ref - // and and by-val argument might both actually contain - // the same unique ptr. - None - } + ast::by_copy => Some(@lp_arg(vid)), + ast::by_ref => None, + ast::by_val => { + // by-value is this hybrid mode where we have a + // pointer but we do not own it. This is not + // considered loanable because, for example, a by-ref + // and and by-val argument might both actually contain + // the same unique ptr. + None + } }; @cmt_ { id:id, diff --git a/src/librustc/middle/mode.rs b/src/librustc/middle/mode.rs deleted file mode 100644 index 149821db289..00000000000 --- a/src/librustc/middle/mode.rs +++ /dev/null @@ -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); -} - diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs new file mode 100644 index 00000000000..2d6565cc9c6 --- /dev/null +++ b/src/librustc/middle/moves.rs @@ -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} + }) + } + } +} \ No newline at end of file diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index 056ab96008a..205be5f891b 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -11,7 +11,6 @@ use core::prelude::*; use middle::resolve; -use middle::ty::{CopyValue, MoveValue, ReadValue}; use middle::ty; use syntax::ast::*; @@ -94,30 +93,3 @@ pub fn pat_binding_ids(dm: resolve::DefMap, pat: @pat) -> ~[node_id] { 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; -} - diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 698da4e6661..31ac2f0f676 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -290,15 +290,6 @@ pub fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) { cx.sess.intr())); 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, _) => { new_cx.root_exprs.insert(cond.id, ()); } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index a17db8652ce..5314005a956 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -24,8 +24,8 @@ use core::cmp; use core::str; use core::vec; use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm}; -use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk, capture_clause}; -use syntax::ast::{bind_by_value, bind_infer, bind_by_ref, bind_by_move}; +use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk}; +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::{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}; @@ -175,11 +175,6 @@ pub enum SelfBinding { HasSelfBinding(node_id, bool /* is implicit */) } -pub enum CaptureClause { - NoCaptureClause, - HasCaptureClause(capture_clause) -} - pub type ResolveVisitor = vt<()>; #[deriving_eq] @@ -3727,7 +3722,6 @@ pub impl Resolver { OpaqueFunctionRibKind), (*block), NoSelfBinding, - NoCaptureClause, visitor); } @@ -3803,33 +3797,7 @@ pub impl Resolver { type_parameters: TypeParameters, block: blk, self_binding: SelfBinding, - capture_clause: CaptureClause, 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. let function_value_rib = @Rib(rib_kind); (*self.value_ribs).push(function_value_rib); @@ -3945,7 +3913,6 @@ pub impl Resolver { HasSelfBinding ((*destructor).node.self_id, true), - NoCaptureClause, visitor); } } @@ -3976,7 +3943,6 @@ pub impl Resolver { type_parameters, method.body, self_binding, - NoCaptureClause, visitor); } @@ -4047,7 +4013,6 @@ pub impl Resolver { NormalRibKind), method.body, HasSelfBinding(method.self_id), - NoCaptureClause, visitor); */ } @@ -4855,14 +4820,13 @@ pub impl Resolver { visit_expr(expr, (), visitor); } - expr_fn(_, ref fn_decl, ref block, capture_clause) | - expr_fn_block(ref fn_decl, ref block, capture_clause) => { + expr_fn(_, ref fn_decl, ref block) | + expr_fn_block(ref fn_decl, ref block) => { self.resolve_function(FunctionRibKind(expr.id, block.node.id), Some(@/*bad*/copy *fn_decl), NoTypeParameters, (*block), NoSelfBinding, - HasCaptureClause(capture_clause), visitor); } @@ -5139,18 +5103,12 @@ pub impl Resolver { descr: &str) { match pat_binding_mode { bind_infer => {} - bind_by_value => { + bind_by_copy => { self.session.span_err( pat.span, fmt!("cannot use `copy` binding mode with %s", descr)); } - bind_by_move => { - self.session.span_err( - pat.span, - fmt!("cannot use `move` binding mode with %s", - descr)); - } bind_by_ref(*) => { self.session.span_err( pat.span, diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 86dad3b7c1b..2c36d0d088a 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -160,7 +160,6 @@ use middle::trans::datum::*; use middle::trans::expr::Dest; use middle::trans::expr; use middle::trans::glue; -use middle::ty::{CopyValue, MoveValue, ReadValue}; use util::common::indenter; 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). 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); // If we kept going, we'd only re-root the same value, so // return now. @@ -1091,7 +1090,7 @@ pub fn store_non_ref_bindings(bcx: block, TrByValue(is_move, lldest) => { let llval = Load(bcx, binding_info.llmatch); // get a T* let datum = Datum {val: llval, ty: binding_info.ty, - mode: ByRef, source: FromLvalue}; + mode: ByRef, source: ZeroMem}; bcx = { if is_move { 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 // from the various alternatives. 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 variable_ty = node_id_type(bcx, p_id); let llvariable_ty = type_of::type_of(bcx.ccx(), variable_ty); let llmatch, trmode; match bm { - ast::bind_by_value | ast::bind_by_move => { - // in this case, the type of the variable will be T, - // but we need to store a *T - let is_move = (bm == ast::bind_by_move); - llmatch = alloca(bcx, T_ptr(llvariable_ty)); - trmode = TrByValue(is_move, alloca(bcx, llvariable_ty)); - } - 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 - }; + ast::bind_by_copy | ast::bind_infer => { + // in this case, the final type of the variable will be T, + // but during matching we need to store a *T as explained + // above + let is_move = + scope_cx.ccx().maps.moves_map.contains_key(p_id); llmatch = alloca(bcx, T_ptr(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); } - 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, finished: @mut Option<BasicBlockRef>) -> BasicBlockRef { @@ -1697,7 +1686,7 @@ pub fn bind_irrefutable_pat(bcx: block, if make_copy { let binding_ty = node_id_type(bcx, pat.id); let datum = Datum {val: val, ty: binding_ty, - mode: ByRef, source: FromRvalue}; + mode: ByRef, source: RevokeClean}; let scratch = scratch_datum(bcx, binding_ty, false); datum.copy_to_datum(bcx, INIT, scratch); match binding_mode { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 864ffd5e820..e7d21cbbe53 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1676,7 +1676,7 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt, ast::by_ref => { llarg = raw_llarg; } - ast::by_move | ast::by_copy => { + ast::by_copy => { // only by value if immediate: if datum::appropriate_mode(arg_ty.ty).is_by_value() { 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); 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 llfty = type_of_fn(ccx, ~[vecarg_ty], nt); + let llfty = type_of_fn(ccx, ~[], nt); let llfdecl = decl_fn(ccx.llmod, ~"_rust_main", lib::llvm::CCallConv, llfty); @@ -2953,7 +2945,7 @@ pub fn trans_crate(sess: session::Session, tcx: ty::ctxt, output: &Path, emap2: resolve::ExportMap2, - maps: astencode::maps) -> (ModuleRef, link_meta) { + maps: astencode::Maps) -> (ModuleRef, link_meta) { let symbol_hasher = @hash::default_state(); let link_meta = diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs index 584e5d9f184..850da79290c 100644 --- a/src/librustc/middle/trans/build.rs +++ b/src/librustc/middle/trans/build.rs @@ -79,7 +79,7 @@ pub fn count_insn(cx: block, category: &str) { s += ~"/"; s += category; - let n = match h.find(s) { + let n = match h.find(/*bad*/ copy s) { Some(n) => n, _ => 0u }; diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index af45dd7064e..91eabaf6ab8 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -334,7 +334,7 @@ pub fn trans_method_call(in_cx: block, pub fn trans_rtcall_or_lang_call(bcx: block, did: ast::def_id, - args: ~[ValueRef], + args: &[ValueRef], dest: expr::Dest) -> block { 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, did: ast::def_id, - args: ~[ValueRef], + args: &[ValueRef], type_params: ~[ty::t], dest: expr::Dest) -> block { @@ -418,11 +418,11 @@ pub fn trans_call_inner( dest: expr::Dest, autoref_arg: AutorefArg) -> block { 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) => { args.len() > 0u && match vec::last(args).node { ast::expr_loop_body(@ast::expr { - node: ast::expr_fn_block(_, ref body, _), + node: ast::expr_fn_block(_, ref body), _ }) => body_contains_ret((*body)), _ => 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); bcx = args_res.bcx; let mut llargs = /*bad*/copy args_res.args; @@ -520,9 +520,10 @@ pub fn trans_call_inner( } } + pub enum CallArgs { - ArgExprs(~[@ast::expr]), - ArgVals(~[ValueRef]) + ArgExprs(&[@ast::expr]), + ArgVals(&[ValueRef]) } pub fn trans_args(cx: block, @@ -625,7 +626,7 @@ pub fn trans_arg_expr(bcx: block, ast::expr_loop_body( // XXX: Bad copy. 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 bcx = closure::trans_expr_fn( 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, datum: Datum {val: scratch, ty: scratch_ty, mode: ByRef, - source: FromRvalue}} + source: RevokeClean}} } _ => { bcx.sess().impossible_case( @@ -670,34 +671,35 @@ pub fn trans_arg_expr(bcx: block, } else { // FIXME(#3548) use the adjustments table 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 => { match arg_mode { 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); } ast::by_val => { // NB: avoid running the take glue. + + assert !bcx.ccx().maps.moves_map.contains_key( + arg_expr.id); 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); - if arg_mode == ast::by_move { - // NDM---Doesn't seem like this should be - // 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); + arg_datum.store_to_datum(bcx, arg_expr.id, + INIT, scratch); // Technically, ownership of val passes to the callee. // However, we must cleanup should we fail before the diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index f6a6c5af5ff..52bb8f56218 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -15,12 +15,12 @@ use back::link::{mangle_internal_name_by_path_and_seq}; use back::link::{mangle_internal_name_by_path}; use lib::llvm::llvm; use lib::llvm::{ValueRef, TypeRef}; -use middle::capture; +use middle::moves; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; 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::glue; use middle::trans::machine; @@ -106,7 +106,7 @@ use syntax::print::pprust::expr_to_str; pub enum EnvAction { /// Copy the value from this llvm ValueRef into the environment. - EnvStore, + EnvCopy, /// Move the value from this llvm ValueRef into the environment. EnvMove, @@ -123,7 +123,7 @@ pub struct EnvValue { pub impl EnvAction { fn to_str() -> ~str { match self { - EnvStore => ~"EnvStore", + EnvCopy => ~"EnvCopy", EnvMove => ~"EnvMove", EnvRef => ~"EnvRef" } @@ -151,7 +151,7 @@ pub fn mk_closure_tys(tcx: ty::ctxt, // converted to ptrs. let bound_tys = bound_values.map(|bv| { match bv.action { - EnvStore | EnvMove => bv.datum.ty, + EnvCopy | EnvMove => 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]); match bv.action { - EnvStore => { - bcx = bv.datum.store_to(bcx, INIT, bound_data); + EnvCopy => { + bcx = bv.datum.copy_to(bcx, INIT, bound_data); } EnvMove => { 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 // collects the upvars and packages them up for store_environment. pub fn build_closure(bcx0: block, - cap_vars: ~[capture::capture_var], + cap_vars: &[moves::CaptureVar], proto: ast::Proto, include_ret_handle: Option<ValueRef>) -> closure_result { let _icx = bcx0.insn_ctxt("closure::build_closure"); @@ -274,27 +274,23 @@ pub fn build_closure(bcx0: block, // Package up the captured upvars let mut env_vals = ~[]; - for vec::each(cap_vars) |cap_var| { + for cap_vars.each |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 { - capture::cap_ref => { + moves::CapRef => { assert proto == ast::ProtoBorrowed; env_vals.push(EnvValue {action: EnvRef, datum: datum}); } - capture::cap_copy => { - env_vals.push(EnvValue {action: EnvStore, + moves::CapCopy => { + env_vals.push(EnvValue {action: EnvCopy, datum: datum}); } - capture::cap_move => { + moves::CapMove => { env_vals.push(EnvValue {action: EnvMove, 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| { // Flag indicating we have returned (a by-ref bool): 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, 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_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx), - mode: ByRef, source: FromLvalue}; + mode: ByRef, source: ZeroMem}; env_vals.push(EnvValue {action: EnvRef, datum: ret_datum}); } @@ -328,7 +324,7 @@ pub fn build_closure(bcx0: block, // with the upvars and type descriptors. pub fn load_environment(fcx: fn_ctxt, cdata_ty: ty::t, - cap_vars: ~[capture::capture_var], + cap_vars: &[moves::CaptureVar], load_ret_handle: bool, proto: ast::Proto) { 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. let mut i = 0u; - for vec::each(cap_vars) |cap_var| { - match cap_var.mode { - capture::cap_drop => { /* ignore */ } - _ => { - let mut upvarptr = GEPi(bcx, llcdata, [0u, i]); - 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; - } + for cap_vars.each |cap_var| { + let mut upvarptr = GEPi(bcx, llcdata, [0u, i]); + 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; } if load_ret_handle { let flagptr = Load(bcx, GEPi(bcx, llcdata, [0u, i])); @@ -383,9 +374,9 @@ pub fn trans_expr_fn(bcx: block, +body: ast::blk, outer_id: ast::node_id, user_id: ast::node_id, - cap_clause: ast::capture_clause, is_loop_body: Option<Option<ValueRef>>, - dest: expr::Dest) -> block { + dest: expr::Dest) -> block +{ /*! * * Translates the body of a closure expression. @@ -426,29 +417,24 @@ pub fn trans_expr_fn(bcx: block, ~"expr_fn"); 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 { 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 => { trans_closure(ccx, sub_path, decl, body, llfn, no_self, None, diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 7ab90dd1ef7..6a2618412b9 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -205,7 +205,7 @@ pub struct crate_ctxt { type_short_names: HashMap<ty::t, ~str>, all_llvm_symbols: Set<~str>, tcx: ty::ctxt, - maps: astencode::maps, + maps: astencode::Maps, stats: stats, upcalls: @upcall::upcalls, tydesc_type: TypeRef, @@ -1134,7 +1134,7 @@ pub fn C_u8(i: uint) -> ValueRef { // our boxed-and-length-annotated strings. pub fn C_cstr(cx: @crate_ctxt, +s: ~str) -> ValueRef { unsafe { - match cx.const_cstr_cache.find(s) { + match cx.const_cstr_cache.find(/*bad*/copy s) { Some(llval) => return llval, None => () } diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index b4dd59cd624..6498b69fbc0 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -41,45 +41,35 @@ * convenient for interfacing with the various code floating around * that predates datums. * - * # Datum sources + * # Datum cleanup styles * - * Each datum carries with it an idea of its "source". This indicates - * the kind of expression from which the datum originated. The source - * affects what happens when the datum is stored or moved. + * Each datum carries with it an idea of how its value will be cleaned + * up. This is important after a move, because we need to know how to + * 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. - * This is therefore the owning reference to the datum. If the - * 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. `ZeroMem`: To cancel the cleanup, we zero out the memory where + * the value resides. This is used for lvalues. * - * 2. `FromLvalue`: This value originates from an lvalue. If the datum - * is stored, it will be *copied* into its new home. If the datum - * is moved, it will be zeroed out. + * # Copying, moving, and storing * - * 3. `FromLastUseLvalue`: The same as FromLvalue, except that it - * originates from the *last use* of an lvalue. If the datum is - * stored, then, it will be moved (and zeroed out). + * There are three methods for moving the value into a new + * location: * - * # 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 - * location. *Storing* a datum is probably the one you want to reach - * for first: it is used when you will no longer use the datum and - * would like to place it somewhere. It may translate to a copy or a - * 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. + * - `move_to()` will copy the value, meaning that the value is mem-copied + * into its new home and then the cleanup on the this datum is revoked. + * This is a "shallow" clone. After `move_to()`, the current datum + * is invalid and should no longer be used. * - * Sometimes, though, you want to use an explicit copy or move. A - * copy copies the data from the datum into a new location and - * 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. + * - `store_to()` either performs a copy or a move by consulting the + * moves_map computed by `middle::moves`. * * # Scratch datum * @@ -133,8 +123,8 @@ pub struct Datum { /// How did this value originate? This is particularly important /// if the value is MOVED or prematurely DROPPED, because it /// describes how to cancel the cleanup that was scheduled before. - /// See the def'n of the `DatumSource` type. - source: DatumSource + /// See the def'n of the `DatumCleanup` type. + source: DatumCleanup } pub struct DatumBlock { @@ -173,32 +163,16 @@ pub impl DatumMode: to_bytes::IterBytes { } } -/// See `Datum Sources` section at the head of this module. -pub enum DatumSource { - FromRvalue, - FromLvalue, - FromLastUseLvalue, -} - -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 - } - } +/// See `Datum cleanup styles` section at the head of this module. +#[deriving_eq] +pub enum DatumCleanup { + RevokeClean, + ZeroMem } pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { return Datum {val: val, ty: ty, - mode: ByValue, source: FromRvalue}; + mode: ByValue, source: RevokeClean}; } 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 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 { @@ -241,42 +215,39 @@ pub fn appropriate_mode(ty: ty::t) -> DatumMode { } pub impl Datum { - fn store_will_move() -> bool { - match self.source { - FromRvalue | FromLastUseLvalue => true, - FromLvalue => false - } - } - - fn store_to(bcx: block, action: CopyAction, dst: ValueRef) -> block { + fn store_to(bcx: block, id: ast::node_id, + action: CopyAction, dst: ValueRef) -> block { /*! * * 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) } else { 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 { expr::Ignore => { return bcx; } 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)", self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx())); 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 { @@ -396,7 +367,7 @@ pub impl Datum { * Schedules this datum for cleanup in `bcx`. The datum * must be an rvalue. */ - assert self.source.is_rvalue(); + assert self.source == RevokeClean; match self.mode { ByValue => { add_clean_temp_immediate(bcx, self.val, self.ty); @@ -410,10 +381,10 @@ pub impl Datum { fn cancel_clean(bcx: block) { if ty::type_needs_drop(bcx.tcx(), self.ty) { match self.source { - FromRvalue => { + RevokeClean => { revoke_clean(bcx, self.val); } - FromLvalue | FromLastUseLvalue => { + ZeroMem => { // Lvalues which potentially need to be dropped // must be passed by ref, so that we can zero them // out. @@ -444,7 +415,7 @@ pub impl Datum { ByValue => self, ByRef => { 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, ByValue => { 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, ixs: &[uint], ty: ty::t, - source: DatumSource) + source: DatumCleanup) -> Datum { let base_val = self.to_ref_llval(bcx); Datum { @@ -618,7 +589,7 @@ pub impl Datum { let ptr = self.to_value_llval(bcx); 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 { @@ -636,7 +607,7 @@ pub impl Datum { let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static, self.ty); Datum {val: llval, ty: rptr_ty, - mode: ByValue, source: FromRvalue} + mode: ByValue, source: RevokeClean} } fn try_deref( @@ -701,7 +672,7 @@ pub impl Datum { val: PointerCast(bcx, self.val, llty), ty: ty, mode: ByRef, - source: FromLvalue + source: ZeroMem }), bcx ) @@ -740,7 +711,7 @@ pub impl Datum { val: GEPi(bcx, self.val, [0, 0, 0]), ty: ty, mode: ByRef, - source: FromLvalue + source: ZeroMem }), bcx ) @@ -768,7 +739,7 @@ pub impl Datum { val: lv.to_value_llval(bcx), ty: ty, 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) } - fn store_to(action: CopyAction, dst: ValueRef) -> block { - self.datum.store_to(self.bcx, action, dst) + fn store_to(id: ast::node_id, action: CopyAction, + dst: ValueRef) -> block { + self.datum.store_to(self.bcx, id, action, dst) } fn copy_to(action: CopyAction, dst: ValueRef) -> block { diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 4580a621f99..7d724ef7441 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -789,10 +789,10 @@ pub fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> { } ast_map::node_expr(expr) => { match /*bad*/copy expr.node { - ast::expr_fn(_, decl, _, _) => { + ast::expr_fn(_, decl, _) => { ((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) } _ => 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 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 { match ret_ty.node { ast::ty_nil => llnull(), diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 15830e48a07..2a8a6a696c2 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -12,45 +12,53 @@ # Translation of expressions. -## User's guide - -If you wish to translate an expression, there are two basic modes: - -1. `trans_into(block, expr, Dest) -> block` -2. `trans_to_datum(block, expr) -> DatumBlock` - -`trans_into()` is the preferred form to use whenever possible. It -evaluates the expression and stores its result into `Dest`, which -must either be the special flag ignore (throw the result away) or -be a pointer to memory of the same type/size as the expression. - -Sometimes, though, you just want to evaluate the expression into -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 - -The two functions above are the most general and can handle any -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 - works on lvalues. This is mostly used as an assertion for those - places where only an lvalue is expected. It also guarantees that - you will get a by-ref Datum back (almost, see [1]). -- `trans_local_var()` can be used to trans a ref to a local variable - that is not an expression. +## Recommended entry point + +If you wish to translate an expression, the preferred way to do +so is to use: + + expr::trans_into(block, expr, Dest) -> block + +This will generate code that evaluates `expr`, storing the result into +`Dest`, which must either be the special flag ignore (throw the result +away) or be a pointer to memory of the same type/size as the +expression. It returns the resulting basic block. This form will +handle all automatic adjustments and moves for you. + +## Translation to a datum + +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). + +In such cases, you want the following function: + + trans_to_datum(block, expr) -> DatumBlock + +This function generates code to 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). + +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 @@ -127,7 +135,6 @@ use middle::trans::datum::*; use middle::trans::machine; use middle::trans::meth; use middle::trans::tvec; -use middle::ty::MoveValue; use middle::ty::struct_mutable_fields; use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn}; 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 { - return match bcx.tcx().adjustments.find(expr.id) { - None => trans_into_unadjusted(bcx, expr, dest), - Some(_) => { - // use trans_to_datum, which is mildly less efficient but - // which will perform the adjustments: - let datumblock = trans_to_datum(bcx, expr); + if bcx.tcx().adjustments.contains_key(expr.id) { + // use trans_to_datum, which is mildly less efficient but + // which will perform the adjustments: + let datumblock = trans_to_datum(bcx, expr); + return match dest { + 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 { 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 { + /*! + * + * 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) { None => trans_lvalue_unadjusted(bcx, expr), 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 { 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); } ast::expr_assign(dst, src) => { - let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src)); - let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst)); - return src_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum); + let src_datum = unpack_datum!( + bcx, trans_to_datum(bcx, src)); + 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) => { 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))); - // XXX: This copy is really bad. - match /*bad*/copy expr.node { + match expr.node { ast::expr_paren(e) => { 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); } 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) => { return _match::trans_match(bcx, expr, discr, /*bad*/copy *arms, dest); } 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| { 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) => { return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest); } - ast::expr_tup(args) => { - return trans_tup(bcx, args, dest); + ast::expr_tup(ref args) => { + return trans_tup(bcx, *args, dest); } ast::expr_lit(@ast::spanned {node: ast::lit_str(s), _}) => { 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); } // 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 // astconv instead. return closure::trans_expr_fn(bcx, proto, decl, /*bad*/copy *body, 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); match ty::get(expr_ty).sty { 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( bcx, fn_ty.meta.proto, /*bad*/copy *decl, /*bad*/copy *body, expr.id, expr.id, - cap_clause, None, dest); + None, dest); } _ => { 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 { ty::ty_fn(ref fn_ty) => { 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( bcx, fn_ty.meta.proto, @@ -590,7 +604,6 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, /*bad*/copy *body, expr.id, blk.id, - cap, Some(None), dest); } @@ -613,26 +626,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr, ast::expr_copy(a) => { return trans_into(bcx, a, dest); } - ast::expr_unary_move(a) => { - 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, _) => { + ast::expr_call(f, ref args, _) => { 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, expr, rcvr, - callee::ArgExprs(args), + callee::ArgExprs(*args), dest); } 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 - * 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. */ + * datum. Does not apply any adjustments. */ let _icx = bcx.insn_ctxt("trans_lval"); 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}; 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; 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)); } 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) => { 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, - ref_expr: @ast::expr, - def: ast::def) - -> DatumBlock { - 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) - }; + fn trans_rec_field(bcx: block, + base: @ast::expr, + field: ast::ident) -> DatumBlock { + /*! + * + * Translates `base.field`. Note that this version always + * yields an unrooted, unmoved version. Rooting and possible + * moves are dealt with above in trans_lvalue_unadjusted(). + */ + + 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 { - bcx: bcx, - datum: Datum {val: val, - ty: const_ty, - mode: ByRef, - source: FromLvalue} + datum: base_datum.GEPi(bcx, + [0u, 0u, ix], + field_tys[ix].mt.ty, + ZeroMem), + bcx: bcx } } - _ => { - DatumBlock { - bcx: bcx, - datum: trans_local_var(bcx, def, Some(ref_expr.id)) + } + + fn trans_index(bcx: block, + 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, - def: ast::def, - expr_id_opt: Option<ast::node_id>) - -> Datum { +pub fn trans_local_var(bcx: block, def: ast::def) -> Datum { let _icx = bcx.insn_ctxt("trans_local_var"); return match def { 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); match bcx.fcx.llupvars.find(nid) { Some(val) => { @@ -832,7 +943,7 @@ pub fn trans_local_var(bcx: block, val: val, ty: local_ty, mode: ByRef, - source: FromLvalue + source: ZeroMem } } None => { @@ -842,10 +953,10 @@ pub fn trans_local_var(bcx: block, } } 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, _) => { - take_local(bcx, bcx.fcx.lllocals, nid, expr_id_opt) + take_local(bcx, bcx.fcx.lllocals, nid) } ast::def_self(nid, _) => { let self_info: ValSelfData = match bcx.fcx.llself { @@ -867,7 +978,7 @@ pub fn trans_local_var(bcx: block, val: casted_val, ty: self_info.t, 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, table: HashMap<ast::node_id, local_val>, - nid: ast::node_id, - expr_id_opt: Option<ast::node_id>) -> Datum { + nid: ast::node_id) -> Datum { let (v, mode) = match table.find(nid) { Some(local_mem(v)) => (v, ByRef), Some(local_imm(v)) => (v, ByValue), @@ -897,7 +1007,7 @@ pub fn trans_local_var(bcx: block, val: v, ty: ty, 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, fields: &[ast::field], base: Option<@ast::expr>, @@ -1158,7 +1172,7 @@ fn trans_rec_or_struct(bcx: block, let base_datum = unpack_datum!( bcx, trans_to_datum(bcx, *base_expr)); - // Copy over inherited fields + // Copy/move over inherited fields for field_tys.eachi |i, field_ty| { if !fields.any(|f| f.node.ident == field_ty.ident) { let dest = GEPi(bcx, addr, struct_field(i)); @@ -1166,8 +1180,8 @@ fn trans_rec_or_struct(bcx: block, base_datum.GEPi(bcx, struct_field(i), field_ty.mt.ty, - FromLvalue); - bcx = base_field.store_to(bcx, INIT, dest); + ZeroMem); + 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 mut bcx = bcx; let addr = match dest { @@ -1642,7 +1656,7 @@ fn trans_assign_op(bcx: block, trans_eager_binop( bcx, expr, dst_datum.ty, op, &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 { diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index e725e484a89..0431d0aa4dd 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -69,7 +69,7 @@ fn c_arg_and_ret_lltys(ccx: @crate_ctxt, ty::ty_fn(ref fn_ty) => { let llargtys = type_of_explicit_args( ccx, - /*bad*/copy fn_ty.sig.inputs); + fn_ty.sig.inputs); let llretty = type_of::type_of(ccx, 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 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 // zero out the memory where the datum resides. Zeroing // 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 mode = appropriate_mode(tp_ty); 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, get_param(decl, first_real_arg)); } @@ -458,7 +458,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt, let tp_ty = substs.tys[0]; let mode = appropriate_mode(tp_ty); 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)); } ~"min_align_of" => { @@ -546,16 +546,17 @@ pub fn trans_intrinsic(ccx: @crate_ctxt, onceness: ast::Many, region: ty::re_bound(ty::br_anon(0)), bounds: @~[]}, - sig: FnSig {inputs: ~[arg {mode: ast::expl(ast::by_val), + sig: FnSig {inputs: ~[arg {mode: ast::expl(ast::by_copy), ty: star_u8}], output: ty::mk_nil(bcx.tcx())} }); 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, None, fty, ty::mk_nil(bcx.tcx()), |bcx| Callee {bcx: bcx, data: Closure(datum)}, - ArgVals(~[frameaddress_val]), Ignore, DontAutorefArg); + ArgVals(arg_vals), Ignore, DontAutorefArg); } ~"morestack_addr" => { // XXX This is a hack to grab the address of this particular diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index f773b09d5de..26ea47d7e73 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -232,7 +232,6 @@ pub impl reflector { ast::expl(e) => match e { ast::by_ref => 1u, ast::by_val => 2u, - ast::by_move => 4u, ast::by_copy => 5u } }; diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index c8dc3e702be..6f183aa50b0 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -24,7 +24,7 @@ pub fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef { let llty = type_of(ccx, arg.ty); match ty::resolved_mode(ccx.tcx, arg.mode) { ast::by_val => llty, - ast::by_copy | ast::by_move => { + ast::by_copy => { if ty::type_is_immediate(arg.ty) { llty } 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]) - -> ~[TypeRef] { +pub fn type_of_explicit_args(ccx: @crate_ctxt, + inputs: &[ty::arg]) -> ~[TypeRef] { 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 { unsafe { let mut atys: ~[TypeRef] = ~[]; diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs index c19db4a75b9..d678b76a1e1 100644 --- a/src/librustc/middle/trans/type_use.rs +++ b/src/librustc/middle/trans/type_use.rs @@ -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) => { for vec::each(fn_ty.sig.inputs) |arg| { 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); } by_ref => {} @@ -255,7 +255,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) { expr_rec(_, _) | expr_struct(*) | expr_tup(_) | expr_unary(box(_), _) | expr_unary(uniq(_), _) | expr_binary(add, _, _) | - expr_copy(_) | expr_unary_move(_) | expr_repeat(*) => { + expr_copy(_) | expr_repeat(*) => { node_type_needs(cx, use_repr, e.id); } 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)) ) |a| { 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); } _ => () @@ -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, e.callee_id)).each |a| { 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); } _ => () diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2dd70f2f57d..255b87e2d5e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -99,15 +99,6 @@ pub struct field_ty { 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 // the types of AST nodes. #[deriving_eq] @@ -295,9 +286,6 @@ struct ctxt_ { // A method will be in this list if and only if it is a destructor. 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 trait_impls: HashMap<ast::def_id, HashMap<t, @Impl>> } @@ -875,7 +863,6 @@ pub fn mk_ctxt(s: session::Session, supertraits: HashMap(), destructor_for_type: HashMap(), destructors: HashMap(), - value_modes: 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) => { - 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 @@ -2925,7 +2919,12 @@ pub fn pat_ty(cx: ctxt, pat: @ast::pat) -> t { // 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" // instead of "fn(t) -> T with T = int". If this isn't what you want, see // 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); } +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, expr: @ast::expr) -> {params: ~[t], ty: t} { @@ -3060,7 +3165,6 @@ pub fn expr_kind(tcx: ctxt, ast::expr_do_body(*) | ast::expr_block(*) | ast::expr_copy(*) | - ast::expr_unary_move(*) | ast::expr_repeat(*) | ast::expr_lit(@ast::spanned {node: lit_str(_), _}) | ast::expr_vstore(_, ast::expr_vstore_slice) | diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 38788ac0d8d..33250c294e0 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -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); } // 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); } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index e360d8e7c88..f13de691a69 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -78,7 +78,6 @@ type parameter). use core::prelude::*; -use middle::capture; use middle::const_eval; use middle::pat_util::pat_id_map; 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) { Some(ref name) => { 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) { 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, ex: @ast::expr, rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t { - match lookup_op_method(fcx, ex, rhs_expr, rhs_t, - fcx.tcx().sess.ident_of(mname), ~[], - DontDerefArgs) { + match lookup_op_method( + fcx, ex, rhs_expr, rhs_t, + fcx.tcx().sess.ident_of(/*bad*/ copy mname), ~[], + DontDerefArgs) + { Some((ret_ty, _)) => ret_ty, _ => { 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)); 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); 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) => { 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), 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, decl, (*body), Vanilla, expected); - capture::check_capture_clause(tcx, expr.id, cap_clause); } ast::expr_loop_body(b) => { // 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 { - 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 // loop, so as to assign types to all nodes while also // 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, decl, *body, fn_kind, Some(inner_ty)); demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); - capture::check_capture_clause(tcx, b.id, cap_clause); } // argh _ => fail ~"expr_fn_block" @@ -2283,11 +2281,10 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt, } }; 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, decl, *body, DoBlock, Some(inner_ty)); demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); - capture::check_capture_clause(tcx, b.id, cap_clause); } // argh _ => fail ~"expected fn ty" @@ -3052,7 +3049,7 @@ pub fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) { ~"size_of" | ~"pref_align_of" | ~"min_align_of" => (1u, ~[], ty::mk_uint(ccx.tcx)), ~"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)), ~"reinterpret_cast" => (2u, ~[arg(ast::by_ref, param(ccx, 0u))], param(ccx, 1u)), @@ -3062,7 +3059,7 @@ pub fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) { (1u, ~[arg(ast::by_copy, ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)), param(ccx, 0u))), - arg(ast::by_move, param(ccx, 0u))], + arg(ast::by_copy, param(ccx, 0u))], ty::mk_nil(tcx)) } ~"needs_drop" => (1u, ~[], ty::mk_bool(tcx)), diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index abc79c8af0e..ae8574f8d1e 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -710,7 +710,6 @@ pub mod guarantor { ast::expr_do_body(*) | ast::expr_block(*) | ast::expr_copy(*) | - ast::expr_unary_move(*) | ast::expr_repeat(*) | ast::expr_vec(*) => { assert !ty::expr_is_lval( diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 3548125a39e..85db1fd6e10 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -606,10 +606,10 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { ty::ty_uniq(mt) => { // Ensure that the trait vstore and the pointer // type match. - match (ty::get(ty).sty, vstore) { - (ty::ty_box(_), ty::vstore_box) | - (ty::ty_uniq(_), ty::vstore_uniq) | - (ty::ty_rptr(*), ty::vstore_slice(*)) => { + match (&ty::get(ty).sty, vstore) { + (&ty::ty_box(_), ty::vstore_box) | + (&ty::ty_uniq(_), ty::vstore_uniq) | + (&ty::ty_rptr(*), ty::vstore_slice(*)) => { let location_info = &location_info_for_expr(ex); 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 // the regions. - match (ty::get(ty).sty, vstore) { - (ty::ty_rptr(ra, _), + match (&ty::get(ty).sty, vstore) { + (&ty::ty_rptr(ra, _), ty::vstore_slice(rb)) => { infer::mk_subr(fcx.infcx(), 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, ~"must cast \ a boxed \ @@ -655,7 +655,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { trait"); err = true; } - (ty::ty_rptr(*), _) => { + (&ty::ty_rptr(*), _) => { fcx.ccx.tcx.sess.span_err(ex.span, ~"must cast \ a borrowed \ @@ -663,7 +663,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) { a borrowed \ trait"); } - (ty::ty_uniq(*), _) => { + (&ty::ty_uniq(*), _) => { fcx.ccx.tcx.sess.span_err(ex.span, ~"must cast \ a unique \ diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index a72dfc0a934..8fe9b140406 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -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.callee_id); match e.node { - ast::expr_fn_block(ref decl, _, _) => { + ast::expr_fn_block(ref decl, _) => { for vec::each(decl.inputs) |input| { let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id); diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 5ea7937845d..4f7bedd8149 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -362,11 +362,11 @@ pub fn super_lattice_tys<L:LatticeDir TyLatticeDir Combine>( let tcx = self.infcx().tcx; - match (ty::get(a).sty, ty::get(b).sty) { - (ty::ty_bot, _) => { return self.ty_bot(b); } - (_, ty::ty_bot) => { return self.ty_bot(a); } + match (&ty::get(a).sty, &ty::get(b).sty) { + (&ty::ty_bot, _) => { return self.ty_bot(b); } + (_, &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, |x, y| self.tys(*x, *y))); 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, |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, |x, y| self.tys(*x, *y)); } diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 158cbb41abd..2e30bdc5888 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -106,25 +106,25 @@ pub impl Sub: Combine { a.inf_str(self.infcx), b.inf_str(self.infcx)); if a == b { return Ok(a); } let _indenter = indenter(); - match (ty::get(a).sty, ty::get(b).sty) { - (ty::ty_bot, _) => { + match (&ty::get(a).sty, &ty::get(b).sty) { + (&ty::ty_bot, _) => { 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)); Ok(a) } - (ty::ty_infer(TyVar(a_id)), _) => { + (&ty::ty_infer(TyVar(a_id)), _) => { if_ok!(self.var_sub_t(a_id, b)); Ok(a) } - (_, ty::ty_infer(TyVar(b_id))) => { + (_, &ty::ty_infer(TyVar(b_id))) => { if_ok!(self.t_sub_var(a, b_id)); Ok(a) } - (_, ty::ty_bot) => { + (_, &ty::ty_bot) => { Err(ty::terr_sorts(expected_found(&self, a, b))) } diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index 738665abb25..71a4600f4ad 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -91,14 +91,13 @@ pub mod middle { pub mod liveness; pub mod kind; pub mod freevars; - pub mod capture; pub mod pat_util; pub mod region; pub mod const_eval; pub mod astencode; pub mod lang_items; pub mod privacy; - pub mod mode; + pub mod moves; } pub mod front { @@ -221,9 +220,9 @@ pub fn run_compiler(args: &~[~str], demitter: diagnostic::emitter) { let matches = &match getopts::groups::getopts(args, optgroups()) { - Ok(ref m) => (*m), - Err(ref f) => { - early_error(demitter, getopts::fail_str((*f))) + Ok(m) => m, + Err(f) => { + early_error(demitter, getopts::fail_str(f)); } }; @@ -375,7 +374,7 @@ pub fn monitor(+f: fn~(diagnostic::emitter)) { } pub fn main() { - let mut args = os::args(); + let args = os::args(); do monitor |move args, demitter| { run_compiler(&args, demitter); } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index fbbcc904160..10d49ad4b0f 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -421,8 +421,15 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { } ty_infer(infer_ty) => infer_ty.to_str(), ty_err => ~"[type error]", - ty_param(param_ty {idx: id, _}) => { - ~"'" + str::from_bytes(~[('a' as u8) + (id as u8)]) + ty_param(param_ty {idx: id, def_id: did}) => { + 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_enum(did, ref substs) | ty_struct(did, ref substs) => { diff --git a/src/librustdoc/attr_pass.rs b/src/librustdoc/attr_pass.rs index f8fa3ccd3d9..f3826af6cae 100644 --- a/src/librustdoc/attr_pass.rs +++ b/src/librustdoc/attr_pass.rs @@ -166,20 +166,27 @@ fn fold_enum( doc::EnumDoc { variants: do par::map(doc.variants) |variant| { let variant = copy *variant; - let desc = do astsrv::exec(srv) |ctxt, copy variant| { - match ctxt.ast_map.get(doc_id) { - ast_map::node_item(@ast::item { - node: ast::item_enum(ref enum_definition, _), _ - }, _) => { - 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) - } - _ => fail fmt!("Enum variant %s has id that's not bound \ - to an enum item", variant.name) + let desc = { + let variant = copy variant; + do astsrv::exec(srv) |ctxt| { + match ctxt.ast_map.get(doc_id) { + ast_map::node_item(@ast::item { + node: ast::item_enum(ref enum_definition, _), _ + }, _) => { + 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) + } + _ => { + fail fmt!("Enum variant %s has id that's \ + not bound to an enum item", + variant.name) + } + } } }; diff --git a/src/librustdoc/desc_to_brief_pass.rs b/src/librustdoc/desc_to_brief_pass.rs index 9cb74a522a7..6b04903601c 100644 --- a/src/librustdoc/desc_to_brief_pass.rs +++ b/src/librustdoc/desc_to_brief_pass.rs @@ -26,6 +26,7 @@ use pass::Pass; use core::str; use core::vec; +use core::util; use std::par; pub fn mk_pass() -> Pass { @@ -194,8 +195,8 @@ fn paragraphs(s: ~str) -> ~[~str] { } else { if whitespace_lines > 0 { if !accum.is_empty() { - res += ~[accum]; - accum = ~""; + let v = util::replace(&mut accum, ~""); + res.push(v); } } diff --git a/src/librustdoc/markdown_index_pass.rs b/src/librustdoc/markdown_index_pass.rs index 0c5a31ea7ed..c5db7685080 100644 --- a/src/librustdoc/markdown_index_pass.rs +++ b/src/librustdoc/markdown_index_pass.rs @@ -102,10 +102,11 @@ fn item_to_entry( let link = match doc { doc::ModTag(_) | doc::NmodTag(_) 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)) } }; diff --git a/src/librustdoc/markdown_pass.rs b/src/librustdoc/markdown_pass.rs index 6bd461d1f92..359d0097165 100644 --- a/src/librustdoc/markdown_pass.rs +++ b/src/librustdoc/markdown_pass.rs @@ -291,12 +291,13 @@ pub fn header_text(doc: doc::ItemTag) -> ~str { fmt!("of `%s` for `%s`", ImplDoc.trait_types[0], (&ImplDoc.self_ty).get()) }; - fmt!("%s %s", header_kind, desc) - } - _ => { - header_text_(header_kind(doc), header_name(doc)) + return fmt!("%s %s", header_kind, desc); } + _ => {} } + + header_text_(header_kind(copy doc), + header_name(doc)) } fn header_text_(kind: &str, name: &str) -> ~str { diff --git a/src/librustdoc/text_pass.rs b/src/librustdoc/text_pass.rs index f276854c4ec..d5c4dda6d95 100644 --- a/src/librustdoc/text_pass.rs +++ b/src/librustdoc/text_pass.rs @@ -73,7 +73,7 @@ fn fold_item( } fn apply_to_sections( - op: NominalOp<Op>, + +op: NominalOp<Op>, sections: ~[doc::Section] ) -> ~[doc::Section] { par::map(sections, |section, copy op| doc::Section { @@ -115,7 +115,8 @@ fn apply_to_methods( op: NominalOp<Op>, docs: ~[doc::MethodDoc] ) -> ~[doc::MethodDoc] { - do par::map(docs) |doc, copy op| { + let op = copy op; + do par::map(docs) |doc| { doc::MethodDoc { brief: maybe_apply_op(copy op, &doc.brief), desc: maybe_apply_op(copy op, &doc.desc), diff --git a/src/librustdoc/tystr_pass.rs b/src/librustdoc/tystr_pass.rs index f790c37e6ea..016c554be27 100644 --- a/src/librustdoc/tystr_pass.rs +++ b/src/librustdoc/tystr_pass.rs @@ -103,16 +103,18 @@ fn fold_const( let srv = fold.ctxt; doc::SimpleItemDoc { - sig: Some(do astsrv::exec(srv) |copy doc, ctxt| { - match ctxt.ast_map.get(doc.id()) { - ast_map::node_item(@ast::item { - node: ast::item_const(ty, _), _ - }, _) => { - pprust::ty_to_str(ty, extract::interner()) - } - _ => fail ~"fold_const: id not bound to a const item" - } - }), + sig: Some({ + let doc = copy doc; + do astsrv::exec(srv) |ctxt| { + match ctxt.ast_map.get(doc.id()) { + ast_map::node_item(@ast::item { + node: ast::item_const(ty, _), _ + }, _) => { + pprust::ty_to_str(ty, extract::interner()) + } + _ => fail ~"fold_const: id not bound to a const item" + } + }}), .. doc } } @@ -132,26 +134,29 @@ fn fold_enum( doc::EnumDoc { variants: do par::map(doc.variants) |variant| { - let variant = copy *variant; - let sig = do astsrv::exec(srv) |copy variant, ctxt| { - match ctxt.ast_map.get(doc_id) { - ast_map::node_item(@ast::item { - node: ast::item_enum(ref enum_definition, _), _ - }, _) => { - let ast_variant = - do vec::find(enum_definition.variants) |v| { - to_str(v.node.name) == variant.name - }.get(); - - pprust::variant_to_str(ast_variant, extract::interner()) - } - _ => fail ~"enum variant not bound to an enum item" + let sig = { + let variant = copy *variant; + do astsrv::exec(srv) |copy variant, ctxt| { + match ctxt.ast_map.get(doc_id) { + ast_map::node_item(@ast::item { + node: ast::item_enum(ref enum_definition, _), _ + }, _) => { + let ast_variant = + do vec::find(enum_definition.variants) |v| { + to_str(v.node.name) == variant.name + }.get(); + + pprust::variant_to_str( + ast_variant, extract::interner()) + } + _ => fail ~"enum variant not bound to an enum item" + } } }; doc::VariantDoc { sig: Some(sig), - .. variant + .. copy *variant } }, .. doc @@ -262,18 +267,22 @@ fn fold_impl( let srv = fold.ctxt; - let (trait_types, self_ty) = do astsrv::exec(srv) |copy doc, ctxt| { - match ctxt.ast_map.get(doc.id()) { - ast_map::node_item(@ast::item { - node: ast::item_impl(_, opt_trait_type, self_ty, _), _ - }, _) => { - let trait_types = opt_trait_type.map_default(~[], |p| { - ~[pprust::path_to_str(p.path, extract::interner())] - }); - (trait_types, Some(pprust::ty_to_str(self_ty, - extract::interner()))) - } - _ => fail ~"expected impl" + let (trait_types, self_ty) = { + let doc = copy doc; + do astsrv::exec(srv) |ctxt| { + match ctxt.ast_map.get(doc.id()) { + ast_map::node_item(@ast::item { + node: ast::item_impl(_, opt_trait_type, self_ty, _), _ + }, _) => { + let trait_types = opt_trait_type.map_default(~[], |p| { + ~[pprust::path_to_str(p.path, extract::interner())] + }); + (trait_types, + Some(pprust::ty_to_str( + self_ty, extract::interner()))) + } + _ => fail ~"expected impl" + } } }; @@ -318,20 +327,25 @@ fn fold_type( let srv = fold.ctxt; doc::SimpleItemDoc { - sig: do astsrv::exec(srv) |copy doc, ctxt| { - match ctxt.ast_map.get(doc.id()) { - ast_map::node_item(@ast::item { - ident: ident, - node: ast::item_ty(ty, ref params), _ - }, _) => { - Some(fmt!( - "type %s%s = %s", - to_str(ident), - pprust::typarams_to_str(*params, extract::interner()), - pprust::ty_to_str(ty, extract::interner()) - )) - } - _ => fail ~"expected type" + sig: { + let doc = copy doc; + do astsrv::exec(srv) |ctxt| { + match ctxt.ast_map.get(doc.id()) { + ast_map::node_item(@ast::item { + ident: ident, + node: ast::item_ty(ty, ref params), _ + }, _) => { + Some(fmt!( + "type %s%s = %s", + to_str(ident), + pprust::typarams_to_str(*params, + extract::interner()), + pprust::ty_to_str(ty, + extract::interner()) + )) + } + _ => fail ~"expected type" + } } }, .. doc @@ -351,14 +365,17 @@ fn fold_struct( let srv = fold.ctxt; doc::StructDoc { - sig: do astsrv::exec(srv) |copy doc, ctxt| { - match ctxt.ast_map.get(doc.id()) { - ast_map::node_item(item, _) => { - let item = strip_struct_extra_stuff(item); - Some(pprust::item_to_str(item, - extract::interner())) + sig: { + let doc = copy doc; + do astsrv::exec(srv) |ctxt| { + match ctxt.ast_map.get(doc.id()) { + ast_map::node_item(item, _) => { + let item = strip_struct_extra_stuff(item); + Some(pprust::item_to_str(item, + extract::interner())) + } + _ => fail ~"not an item" } - _ => fail ~"not an item" } }, .. doc diff --git a/src/libstd/flatpipes.rs b/src/libstd/flatpipes.rs index f032d19271d..e108643790e 100644 --- a/src/libstd/flatpipes.rs +++ b/src/libstd/flatpipes.rs @@ -788,13 +788,14 @@ mod test { // The main task will wait until the test is over to proceed 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 accept_chan = Cell(move accept_chan); // 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| { let iotask = &uv::global_loop::get(); let begin_connect_chan = begin_connect_chan.take(); @@ -821,7 +822,8 @@ mod test { } // 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| { // Wait for the server to start listening diff --git a/src/libstd/net_ip.rs b/src/libstd/net_ip.rs index 839d0d23a61..2447c2eb530 100644 --- a/src/libstd/net_ip.rs +++ b/src/libstd/net_ip.rs @@ -117,8 +117,9 @@ enum IpGetAddrErr { pub fn get_addr(node: &str, iotask: &iotask) -> result::Result<~[IpAddr], IpGetAddrErr> { 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| { + let output_ch = output_ch.swap_unwrap(); unsafe { log(debug, fmt!("slice len %?", len)); let handle = create_uv_getaddrinfo_t(); diff --git a/src/libstd/par.rs b/src/libstd/par.rs index 04d88af1100..779dda0ab29 100644 --- a/src/libstd/par.rs +++ b/src/libstd/par.rs @@ -59,7 +59,8 @@ fn map_slices<A: Copy Owned, B: Copy Owned>( let end = uint::min(len, base + items_per_task); do vec::as_imm_buf(xs) |p, _len| { let f = f(); - let f = do future_spawn() |move f, copy base| { + let base = base; + let f = do future_spawn() |move f| { unsafe { let len = end - 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>( xs: &[A], f: fn~(&A) -> B) -> ~[B] { 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)) } })) @@ -104,6 +106,7 @@ pub fn map<A: Copy Owned, B: Copy Owned>( pub fn mapi<A: Copy Owned, B: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> B) -> ~[B] { let slices = map_slices(xs, || { + let f = copy f; fn~(base: uint, slice : &[A], copy f) -> ~[B] { vec::mapi(slice, |i, 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. pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool { do vec::all(map_slices(xs, || { + let f = copy f; fn~(base: uint, slice : &[A], copy f) -> bool { vec::alli(slice, |i, 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. pub fn any<A: Copy Owned>(xs: &[A], f: fn~(&A) -> bool) -> bool { do vec::any(map_slices(xs, || { + let f = copy f; fn~(_base : uint, slice: &[A], copy f) -> bool { vec::any(slice, |x| f(x)) } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0b250c2e421..435e514df44 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -337,9 +337,9 @@ pub struct field_pat { #[auto_encode] #[auto_decode] +#[deriving_eq] pub enum binding_mode { - bind_by_value, - bind_by_move, + bind_by_copy, bind_by_ref(mutability), bind_infer } @@ -347,51 +347,17 @@ pub enum binding_mode { pub impl binding_mode : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { match *self { - bind_by_value => 0u8.iter_bytes(lsb0, f), - - bind_by_move => 1u8.iter_bytes(lsb0, f), + bind_by_copy => 0u8.iter_bytes(lsb0, f), 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 => - 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_decode] pub enum pat_ { @@ -603,7 +569,7 @@ pub impl<T:cmp::Eq> inferable<T> : cmp::Eq { // "resolved" mode: the real modes. #[auto_encode] #[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 { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { @@ -729,8 +695,8 @@ pub enum expr_ { (implicit) condition is always true. */ expr_loop(blk, Option<ident>), expr_match(@expr, ~[arm]), - expr_fn(Proto, fn_decl, blk, capture_clause), - expr_fn_block(fn_decl, blk, capture_clause), + expr_fn(Proto, fn_decl, blk), + expr_fn_block(fn_decl, blk), // 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 // the outside). @@ -740,7 +706,6 @@ pub enum expr_ { expr_block(blk), expr_copy(@expr), - expr_unary_move(@expr), expr_assign(@expr, @expr), expr_swap(@expr, @expr), expr_assign_op(binop, @expr, @expr), @@ -769,20 +734,6 @@ pub enum 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 // 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 diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 7b742019f0e..1ae23240404 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -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 { @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 } } @@ -503,11 +503,8 @@ pub fn id_visitor(vfn: fn@(node_id)) -> visit::vt<()> { vfn(m.self_id); for vec::each(tps) |tp| { vfn(tp.id); } } - visit::fk_anon(_, capture_clause) | - visit::fk_fn_block(capture_clause) => { - for vec::each(*capture_clause) |clause| { - vfn(clause.id); - } + visit::fk_anon(_) | + visit::fk_fn_block => { } } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 60b1b98915f..dd6a996b730 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -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 * comparison is performed structurally. */ -pub fn contains(haystack: ~[@ast::meta_item], needle: @ast::meta_item) - -> bool { +pub fn contains(haystack: &[@ast::meta_item], + needle: @ast::meta_item) -> bool { for haystack.each |item| { if eq(*item, needle) { return true; } } diff --git a/src/libsyntax/ext/auto_encode.rs b/src/libsyntax/ext/auto_encode.rs index e4ad518cc80..a2484e2d6df 100644 --- a/src/libsyntax/ext/auto_encode.rs +++ b/src/libsyntax/ext/auto_encode.rs @@ -569,7 +569,7 @@ fn mk_ser_method( pat: @ast::pat { id: cx.next_id(), node: ast::pat_ident( - ast::bind_by_value, + ast::bind_by_copy, ast_util::ident_to_path(span, cx.ident_of(~"__s")), None), span: span, @@ -633,7 +633,7 @@ fn mk_deser_method( pat: @ast::pat { id: cx.next_id(), node: ast::pat_ident( - ast::bind_by_value, + ast::bind_by_copy, ast_util::ident_to_path(span, cx.ident_of(~"__d")), None), span: span, @@ -1095,7 +1095,7 @@ fn mk_enum_deser_body( pat: @ast::pat { id: cx.next_id(), node: ast::pat_ident( - ast::bind_by_value, + ast::bind_by_copy, ast_util::ident_to_path(span, cx.ident_of(~"i")), None), span: span, @@ -1114,8 +1114,7 @@ fn mk_enum_deser_body( span, ast::expr_match(cx.expr_var(span, ~"i"), arms) ) - ), - @~[] + ) ) ); diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index d4da5a2034e..6d44a412742 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -203,7 +203,7 @@ pub fn mk_local(cx: ext_ctxt, sp: span, mutbl: bool, let pat = @ast::pat { id: cx.next_id(), node: ast::pat_ident( - ast::bind_by_value, + ast::bind_by_copy, mk_raw_path(sp, ~[ident]), None), 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, span: span, - ident: ast::ident) - -> @ast::pat { - mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_value) + ident: ast::ident) -> @ast::pat { + mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_copy) } pub fn mk_pat_ident_with_binding_mode(cx: ext_ctxt, span: span, diff --git a/src/libsyntax/ext/pipes/ast_builder.rs b/src/libsyntax/ext/pipes/ast_builder.rs index a85898390a4..8abca3d97f9 100644 --- a/src/libsyntax/ext/pipes/ast_builder.rs +++ b/src/libsyntax/ext/pipes/ast_builder.rs @@ -105,7 +105,6 @@ pub trait ext_ctxt_ast_builder { fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt; fn stmt_expr(e: @ast::expr) -> @ast::stmt; 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_infer() -> @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 { @spanned { node: ast::stmt_expr(e, self.next_id()), span: dummy_sp()} @@ -205,7 +195,7 @@ pub impl ext_ctxt: ext_ctxt_ast_builder { pat: @ast::pat { id: self.next_id(), node: ast::pat_ident( - ast::bind_by_value, + ast::bind_by_copy, ast_util::ident_to_path(dummy_sp(), name), None), span: dummy_sp(), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index e8557558f17..2c2ecb91e21 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -466,30 +466,17 @@ pub fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { expr_match(fld.fold_expr(expr), vec::map((*arms), |x| fld.fold_arm(*x))) } - expr_fn(proto, decl, ref body, captures) => { - let captures = do captures.map |cap_item| { - @ast::capture_item_ { - id: fld.new_id(cap_item.id), - ..**cap_item - } - }; - expr_fn(proto, fold_fn_decl(decl, fld), - fld.fold_block((*body)), - @captures) + expr_fn(proto, decl, ref body) => { + expr_fn(proto, + fold_fn_decl(decl, fld), + fld.fold_block(*body)) } - expr_fn_block(decl, ref body, captures) => { - let captures = do captures.map |cap_item| { - @ast::capture_item_ { - id: fld.new_id(cap_item.id), - ..**cap_item - } - }; - expr_fn_block(fold_fn_decl(decl, fld), fld.fold_block((*body)), - @captures) + expr_fn_block(decl, ref body) => { + expr_fn_block(fold_fn_decl(decl, fld), + fld.fold_block(*body)) } expr_block(ref blk) => expr_block(fld.fold_block((*blk))), 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(fld.fold_expr(el), fld.fold_expr(er)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d41fedebde3..81393310cda 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -13,9 +13,9 @@ use core::prelude::*; use ast::{ProtoBox, ProtoUniq, RegionTyParamBound, TraitTyParamBound}; use ast::{provided, public, pure_fn, purity, re_static}; 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::{blk_check_mode, box, by_copy, by_move, by_ref, by_val}; -use ast::{capture_clause, capture_item, crate, crate_cfg, decl, decl_item}; +use ast::{bind_by_copy, bitand, bitor, bitxor, blk}; +use ast::{blk_check_mode, box, by_copy, by_ref, by_val}; +use ast::{crate, crate_cfg, decl, decl_item}; 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::{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_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_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_mut_slice, expr_while, extern_fn, field, fn_decl}; use ast::{expr_vstore_uniq, TyFn, Onceness, Once, Many}; @@ -102,7 +102,7 @@ enum restriction { enum class_contents { dtor_decl(blk, ~[attribute], codemap::span), 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]>); pub enum item_or_view_item { @@ -401,7 +401,7 @@ pub impl Parser { 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 // names to be left off if there is a definition... either::Left(p.parse_arg_general(false)) @@ -651,7 +651,7 @@ pub impl Parser { fn parse_arg_mode() -> mode { if self.eat(token::BINOP(token::MINUS)) { - expl(by_move) + expl(by_copy) // NDM outdated syntax } else if self.eat(token::ANDAND) { expl(by_ref) } 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) - -> arg_or_capture_item { - - fn parse_capture_item(p:Parser, is_move: bool) -> capture_item { - let sp = mk_sp(p.span.lo, p.span.hi); - let ident = p.parse_ident(); - @ast::capture_item_ { - 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)) + -> arg_or_capture_item + { + if self.eat_keyword(~"move") || self.eat_keyword(~"copy") { + // XXX outdated syntax now that moves-based-on-type has gone in + self.parse_ident(); + either::Right(()) } else { parse_arg_fn(self) } @@ -1078,9 +1067,8 @@ pub impl Parser { ex = expr_copy(e); hi = e.span.hi; } else if self.eat_keyword(~"move") { - let e = self.parse_expr(); - ex = expr_unary_move(e); - hi = e.span.hi; + // XXX move keyword is no longer important, remove after snapshot + return self.parse_expr(); } else if self.token == token::MOD_SEP || is_ident(self.token) && !self.is_keyword(~"true") && !self.is_keyword(~"false") { @@ -1576,12 +1564,10 @@ pub impl Parser { // 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. - let (decl, capture_clause) = - self.parse_fn_decl(|p| p.parse_arg_or_capture_item()); + let decl = self.parse_fn_decl(|p| p.parse_arg_or_capture_item()); let body = self.parse_block(); - return self.mk_expr(lo, body.span.hi, - expr_fn(proto, decl, body, capture_clause)); + return self.mk_expr(lo, body.span.hi,expr_fn(proto, decl, body)); } // `|args| { ... }` like in `do` expressions @@ -1594,18 +1580,15 @@ pub impl Parser { } _ => { // No argument list - `do foo {` - ( - ast::fn_decl { - inputs: ~[], - output: @Ty { - id: self.get_id(), - node: ty_infer, - span: self.span - }, - cf: return_val - }, - @~[] - ) + ast::fn_decl { + inputs: ~[], + output: @Ty { + id: self.get_id(), + node: ty_infer, + span: self.span + }, + cf: return_val + } } } }, @@ -1621,10 +1604,10 @@ pub impl Parser { || 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 { let lo = self.last_span.lo; - let (decl, captures) = parse_decl(); + let decl = parse_decl(); let body = parse_body(); let fakeblock = ast::blk_ { view_items: ~[], @@ -1636,7 +1619,7 @@ pub impl Parser { let fakeblock = spanned(body.span.lo, body.span.hi, fakeblock); return self.mk_expr(lo, body.span.hi, - expr_fn_block(decl, fakeblock, captures)); + expr_fn_block(decl, fakeblock)); } fn parse_else_expr() -> @expr { @@ -2065,22 +2048,16 @@ pub impl Parser { let mutbl = self.parse_mutability(); pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl)); } else if self.eat_keyword(~"copy") { - pat = self.parse_pat_ident(refutable, bind_by_value); - } else if self.eat_keyword(~"move") { - pat = self.parse_pat_ident(refutable, bind_by_move); + pat = self.parse_pat_ident(refutable, bind_by_copy); } else { - let binding_mode; - // XXX: Aren't these two cases deadcode? -- bblum - 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; + if self.eat_keyword(~"move") { + /* XXX---remove move keyword */ } + // 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; match self.look_ahead(1) { 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_decl, capture_clause) { - + -> fn_decl + { let args_or_capture_items: ~[arg_or_capture_item] = self.parse_unspanned_seq( token::LPAREN, token::RPAREN, seq_sep_trailing_disallowed(token::COMMA), parse_arg_fn); 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(); - ( - ast::fn_decl { - inputs: inputs, - output: ret_ty, - cf: ret_style, - }, - capture_clause - ) + ast::fn_decl { + inputs: inputs, + output: ret_ty, + cf: ret_style, + } } fn is_self_ident() -> bool { @@ -2598,8 +2571,8 @@ pub impl Parser { } fn parse_fn_decl_with_self(parse_arg_fn: - fn(Parser) -> arg_or_capture_item) - -> (self_ty, fn_decl, capture_clause) { + fn(Parser) -> arg_or_capture_item) + -> (self_ty, fn_decl) { fn maybe_parse_self_ty(cnstr: fn(+v: mutability) -> ast::self_ty_, p: Parser) -> ast::self_ty_ { @@ -2675,7 +2648,6 @@ pub impl Parser { let hi = self.span.hi; 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 fn_decl = ast::fn_decl { @@ -2684,10 +2656,10 @@ pub impl Parser { 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 = { if self.eat(token::OROR) { ~[] @@ -2704,14 +2676,11 @@ pub impl Parser { @Ty { id: self.get_id(), node: ty_infer, span: self.span } }; - ( - ast::fn_decl { - inputs: either::lefts(inputs_captures), - output: output, - cf: return_val, - }, - @either::rights(inputs_captures) - ) + ast::fn_decl { + inputs: either::lefts(inputs_captures), + output: output, + cf: return_val, + } } fn parse_fn_header() -> {ident: ident, tps: ~[ty_param]} { @@ -2733,7 +2702,7 @@ pub impl Parser { fn parse_item_fn(purity: purity) -> item_info { 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); (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 ident = self.parse_method_name(); 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() }; // XXX: interaction between staticness, self_ty is broken now @@ -3262,7 +3231,7 @@ pub impl Parser { let vis = self.parse_visibility(); let purity = self.parse_fn_purity(); 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; self.expect(token::SEMI); @ast::foreign_item { ident: t.ident, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 7f03158a4df..39fb98aea26 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1305,24 +1305,24 @@ pub fn print_expr(s: ps, &&expr: @ast::expr) { } 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 } cbox(s, indent_unit); // head-box, will be closed by print-block at start ibox(s, 0u); print_fn_header_info(s, None, None, ast::Many, 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); 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 // argument list, but at this point we don't know which // we are inside. // // if !decl.inputs.is_empty() { - print_fn_block_args(s, decl, *cap_clause); + print_fn_block_args(s, decl); space(s.s); // } assert (*body).node.stmts.is_empty(); @@ -1357,10 +1357,6 @@ pub fn print_expr(s: ps, &&expr: @ast::expr) { print_block(s, (*blk)); } 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) => { print_expr(s, lhs); space(s.s); @@ -1554,10 +1550,7 @@ pub fn print_pat(s: ps, &&pat: @ast::pat, refutable: bool) { word_nbsp(s, ~"ref"); print_mutability(s, mutbl); } - ast::bind_by_move => { - word_nbsp(s, ~"move"); - } - ast::bind_by_value => { + ast::bind_by_copy => { word_nbsp(s, ~"copy"); } ast::bind_infer => {} @@ -1693,16 +1686,14 @@ pub fn print_fn(s: ps, nbsp(s); print_ident(s, name); 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, - cap_items: ~[ast::capture_item], - opt_self_ty: Option<ast::self_ty_>) { - // It is unfortunate to duplicate the commasep logic, but we - // we want the self type, the args, and the capture clauses all - // in the same box. - box(s, 0, inconsistent); + opt_self_ty: Option<ast::self_ty_>) { + // It is unfortunate to duplicate the commasep logic, but we we want the + // self type and the args all in the same box. + box(s, 0u, inconsistent); let mut first = true; for opt_self_ty.each |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); } - 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); } 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_>) { popen(s); - print_fn_args(s, decl, cap_items, opt_self_ty); + print_fn_args(s, decl, opt_self_ty); pclose(s); 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, - cap_items: ~[ast::capture_item]) { +pub fn print_fn_block_args(s: ps, decl: ast::fn_decl) { word(s.s, ~"|"); - print_fn_args(s, decl, cap_items, None); + print_fn_args(s, decl, None); word(s.s, ~"|"); 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 { match m { - ast::expl(ast::by_move) => ~"-", ast::expl(ast::by_ref) => ~"&&", - ast::expl(ast::by_val) => ~"++", ast::expl(ast::by_copy) => ~"+", + ast::expl(ast::by_val) => ~"++", ast::infer(_) => ~"" } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 196ee349b79..dd7f274b5ba 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -32,8 +32,8 @@ pub enum vt<E> { mk_vt(visitor<E>), } pub enum fn_kind { fk_item_fn(ident, ~[ty_param], purity), //< an item declared with fn() fk_method(ident, ~[ty_param], @method), - fk_anon(Proto, capture_clause), //< an anonymous function like fn@(...) - fk_fn_block(capture_clause), //< a block {||...} + fk_anon(Proto), //< an anonymous function like fn@(...) + fk_fn_block, //< a block {||...} fk_dtor(~[ty_param], ~[attribute], node_id /* self id */, 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); for (*arms).each |a| { (v.visit_arm)(*a, e, v); } } - expr_fn(proto, decl, ref body, cap_clause) => { - (v.visit_fn)(fk_anon(proto, cap_clause), decl, (*body), + expr_fn(proto, decl, ref body) => { + (v.visit_fn)(fk_anon(proto), decl, (*body), ex.span, ex.id, e, v); } - expr_fn_block(decl, ref body, cap_clause) => { - (v.visit_fn)(fk_fn_block(cap_clause), decl, (*body), + expr_fn_block(decl, ref body) => { + (v.visit_fn)(fk_fn_block, decl, (*body), ex.span, ex.id, 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); } 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_assign_op(_, a, b) => { (v.visit_expr)(b, e, v); diff --git a/src/test/bench/graph500-bfs.rs b/src/test/bench/graph500-bfs.rs index 5ae2996f39e..9667c1d82d8 100644 --- a/src/test/bench/graph500-bfs.rs +++ b/src/test/bench/graph500-bfs.rs @@ -377,7 +377,8 @@ fn validate(edges: ~[(node_id, node_id)], 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; if *v == -1i64 || u == root { true diff --git a/src/test/compile-fail/alt-vec-tail-move.rs b/src/test/compile-fail/alt-vec-tail-move.rs deleted file mode 100644 index cd85cb19778..00000000000 --- a/src/test/compile-fail/alt-vec-tail-move.rs +++ /dev/null @@ -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 -} diff --git a/src/test/compile-fail/block-deinitializes-upvar.rs b/src/test/compile-fail/block-deinitializes-upvar.rs deleted file mode 100644 index 366cf5a48ea..00000000000 --- a/src/test/compile-fail/block-deinitializes-upvar.rs +++ /dev/null @@ -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 ); -} diff --git a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs index 0b236b986e1..50bedc57340 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs @@ -13,18 +13,18 @@ fn borrow(v: &int, f: fn(x: &int)) { } fn box_imm() { - let mut v = ~3; - let _w = &mut v; //~ NOTE loan of mutable local variable granted here - do task::spawn |move v| { - //~^ ERROR moving out of mutable local variable prohibited due to outstanding loan + let v = ~3; + let _w = &v; //~ NOTE loan of immutable local variable granted here + do task::spawn { debug!("v=%d", *v); + //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan } - let mut v = ~3; - let _w = &mut v; //~ NOTE loan of mutable local variable granted here - task::spawn(fn~(move v) { - //~^ ERROR moving out of mutable local variable prohibited due to outstanding loan + let v = ~3; + let _w = &v; //~ NOTE loan of immutable local variable granted here + task::spawn(fn~() { debug!("v=%d", *v); + //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan }); } diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs new file mode 100644 index 00000000000..d80c56eb64f --- /dev/null +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -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 + }; +} diff --git a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs new file mode 100644 index 00000000000..941883f80f9 --- /dev/null +++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs @@ -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 +} diff --git a/src/test/compile-fail/cap-clause-both-copy-and-move.rs b/src/test/compile-fail/cap-clause-both-copy-and-move.rs deleted file mode 100644 index 174715807ec..00000000000 --- a/src/test/compile-fail/cap-clause-both-copy-and-move.rs +++ /dev/null @@ -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 }; -} diff --git a/src/test/compile-fail/cap-clause-double-copy.rs b/src/test/compile-fail/cap-clause-double-copy.rs deleted file mode 100644 index 5c083821538..00000000000 --- a/src/test/compile-fail/cap-clause-double-copy.rs +++ /dev/null @@ -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 }; -} diff --git a/src/test/compile-fail/cap-clause-double-move.rs b/src/test/compile-fail/cap-clause-double-move.rs deleted file mode 100644 index d87d0804e99..00000000000 --- a/src/test/compile-fail/cap-clause-double-move.rs +++ /dev/null @@ -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 }; -} diff --git a/src/test/compile-fail/cap-clause-illegal-cap.rs b/src/test/compile-fail/cap-clause-illegal-cap.rs deleted file mode 100644 index 46a858a7cb2..00000000000 --- a/src/test/compile-fail/cap-clause-illegal-cap.rs +++ /dev/null @@ -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() { -} diff --git a/src/test/compile-fail/cap-clause-move-upvar.rs b/src/test/compile-fail/cap-clause-move-upvar.rs deleted file mode 100644 index 255a17ac426..00000000000 --- a/src/test/compile-fail/cap-clause-move-upvar.rs +++ /dev/null @@ -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 - }; -} diff --git a/src/test/compile-fail/cap-clause-unresolved-copy.rs b/src/test/compile-fail/cap-clause-unresolved-copy.rs deleted file mode 100644 index 3d38008c675..00000000000 --- a/src/test/compile-fail/cap-clause-unresolved-copy.rs +++ /dev/null @@ -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) { - }; -} diff --git a/src/test/compile-fail/cap-clause-unresolved-move.rs b/src/test/compile-fail/cap-clause-unresolved-move.rs deleted file mode 100644 index ada8b0d001b..00000000000 --- a/src/test/compile-fail/cap-clause-unresolved-move.rs +++ /dev/null @@ -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) { - }; -} diff --git a/src/test/compile-fail/cap-clause-use-after-move.rs b/src/test/compile-fail/cap-clause-use-after-move.rs deleted file mode 100644 index 097158843a2..00000000000 --- a/src/test/compile-fail/cap-clause-use-after-move.rs +++ /dev/null @@ -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` -} diff --git a/src/test/compile-fail/cap-clause-with-stack-closure.rs b/src/test/compile-fail/cap-clause-with-stack-closure.rs deleted file mode 100644 index 13d91eabcde..00000000000 --- a/src/test/compile-fail/cap-clause-with-stack-closure.rs +++ /dev/null @@ -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 -} - diff --git a/src/test/compile-fail/copy-into-closure.rs b/src/test/compile-fail/copy-into-closure.rs deleted file mode 100644 index 21d4fa8eab8..00000000000 --- a/src/test/compile-fail/copy-into-closure.rs +++ /dev/null @@ -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() { -} diff --git a/src/test/compile-fail/functional-struct-update.rs b/src/test/compile-fail/functional-struct-update.rs index 98627ce73c0..c0430a6a8bb 100644 --- a/src/test/compile-fail/functional-struct-update.rs +++ b/src/test/compile-fail/functional-struct-update.rs @@ -25,7 +25,7 @@ struct Foo { fn main() { 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)); } diff --git a/src/test/compile-fail/issue-1965.rs b/src/test/compile-fail/issue-1965.rs deleted file mode 100644 index a93b74461a1..00000000000 --- a/src/test/compile-fail/issue-1965.rs +++ /dev/null @@ -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)} -} diff --git a/src/test/compile-fail/kindck-implicit-close-over-mut-var.rs b/src/test/compile-fail/kindck-implicit-close-over-mut-var.rs index 5a0a964b717..c2732d383ab 100644 --- a/src/test/compile-fail/kindck-implicit-close-over-mut-var.rs +++ b/src/test/compile-fail/kindck-implicit-close-over-mut-var.rs @@ -31,11 +31,14 @@ fn bar() { } fn car() { - // Here, i is mutable, but *explicitly* copied: + // Here, i is mutable, but *explicitly* shadowed copied: let mut i = 0; while i < 10 { - do task::spawn |copy i| { - user(i); + { + let i = i; + do task::spawn { + user(i); + } } i += 1; } diff --git a/src/test/compile-fail/unary-move.rs b/src/test/compile-fail/liveness-move-call-arg.rs index 2afecee6e1c..ae1c086b340 100644 --- a/src/test/compile-fail/unary-move.rs +++ b/src/test/compile-fail/liveness-move-call-arg.rs @@ -8,11 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: use of moved value +fn take(_x: ~int) {} 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` + } +} diff --git a/src/test/compile-fail/liveness-move-from-args.rs b/src/test/compile-fail/liveness-move-from-args.rs index 7a44cd901b8..d6c0be9f183 100644 --- a/src/test/compile-fail/liveness-move-from-args.rs +++ b/src/test/compile-fail/liveness-move-from-args.rs @@ -8,22 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn take(-_x: int) { } +fn take(_x: ~int) { } -fn from_by_value_arg(++x: int) { - take(move x); //~ ERROR illegal move from argument `x`, which is not copy or move mode +fn from_by_value_arg(++x: ~int) { + take(x); //~ ERROR illegal move from argument `x`, which is not copy or move mode } -fn from_by_ref_arg(&&x: int) { - take(move x); //~ ERROR illegal move from argument `x`, which is not copy or move mode +fn from_by_ref_arg(&&x: ~int) { + take(x); //~ ERROR illegal move from argument `x`, which is not copy or move mode } -fn from_copy_arg(+x: int) { - take(move x); -} - -fn from_move_arg(-x: int) { - take(move x); +fn from_copy_arg(+x: ~int) { + take(x); } fn main() { diff --git a/src/test/compile-fail/liveness-move-in-loop.rs b/src/test/compile-fail/liveness-move-in-loop.rs index 0935d429966..cb9d028809e 100644 --- a/src/test/compile-fail/liveness-move-in-loop.rs +++ b/src/test/compile-fail/liveness-move-in-loop.rs @@ -9,19 +9,16 @@ // except according to those terms. fn main() { - - let y: int = 42; - let mut x: int; + let y: ~int = ~42; + let mut x: ~int; loop { log(debug, y); loop { loop { loop { // tjc: Not sure why it prints the same error twice - x = move y; //~ ERROR use of moved value - //~^ NOTE move of value occurred here - //~^^ ERROR use of moved value - //~^^^ NOTE move of value occurred here + x = y; //~ ERROR use of moved value + //~^ ERROR use of moved value copy x; } diff --git a/src/test/compile-fail/liveness-move-in-while.rs b/src/test/compile-fail/liveness-move-in-while.rs index 9f261fdb2d1..fa8ce00fb08 100644 --- a/src/test/compile-fail/liveness-move-in-while.rs +++ b/src/test/compile-fail/liveness-move-in-while.rs @@ -10,15 +10,13 @@ fn main() { - let y: int = 42; - let mut x: int; + let y: ~int = ~42; + let mut x: ~int; loop { log(debug, y); // tjc: not sure why it prints the same error twice - while true { while true { while true { x = move y; copy x; } } } + while true { while true { while true { x = y; copy x; } } } //~^ ERROR use of moved value: `y` - //~^^ NOTE move of value occurred here - //~^^^ ERROR use of moved value: `y` - //~^^^^ NOTE move of value occurred here + //~^^ ERROR use of moved value: `y` } } diff --git a/src/test/compile-fail/liveness-use-after-move.rs b/src/test/compile-fail/liveness-use-after-move.rs index d48cbc23e15..16518261009 100644 --- a/src/test/compile-fail/liveness-use-after-move.rs +++ b/src/test/compile-fail/liveness-use-after-move.rs @@ -9,8 +9,8 @@ // except according to those terms. fn main() { - let x = @5; - let y = move x; //~ NOTE move of value occurred here + let x = ~5; + let y = x; log(debug, *x); //~ ERROR use of moved value: `x` copy y; } diff --git a/src/test/compile-fail/liveness-use-after-send.rs b/src/test/compile-fail/liveness-use-after-send.rs index 2a1e486d133..2746c5a304b 100644 --- a/src/test/compile-fail/liveness-use-after-send.rs +++ b/src/test/compile-fail/liveness-use-after-send.rs @@ -18,8 +18,8 @@ enum _chan<T> = int; // Tests that "log(debug, message);" is flagged as using // message after the send deinitializes it -fn test00_start(ch: _chan<int>, message: int, _count: int) { - send(ch, move message); //~ NOTE move of value occurred here +fn test00_start(ch: _chan<~int>, message: ~int, _count: ~int) { + send(ch, message); log(debug, message); //~ ERROR use of moved value: `message` } diff --git a/src/test/compile-fail/moves-based-on-type-access-to-field.rs b/src/test/compile-fail/moves-based-on-type-access-to-field.rs new file mode 100644 index 00000000000..663b615816d --- /dev/null +++ b/src/test/compile-fail/moves-based-on-type-access-to-field.rs @@ -0,0 +1,20 @@ +// Tests that if you move from `x.f` or `x[0]`, `x` is inaccessible. +// Also tests that we give a more specific error message. + +struct Foo { f: ~str, y: int } +fn consume(_s: ~str) {} +fn touch<A>(_a: &A) {} + +fn f10() { + let x = Foo { f: ~"hi", y: 3 }; + consume(x.f); //~ NOTE field of `x` moved here + touch(&x.y); //~ ERROR use of partially moved value: `x` +} + +fn f20() { + let x = ~[~"hi"]; + consume(x[0]); //~ NOTE element of `x` moved here + touch(&x[0]); //~ ERROR use of partially moved value: `x` +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs b/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs new file mode 100644 index 00000000000..db52a2bbe1e --- /dev/null +++ b/src/test/compile-fail/moves-based-on-type-distribute-copy-over-paren.rs @@ -0,0 +1,43 @@ +// Tests that references to move-by-default values trigger moves when +// they occur as part of various kinds of expressions. + +struct Foo<A> { f: A } +fn touch<A>(_a: &A) {} + +fn f00() { + let x = ~"hi"; + let _y = Foo { f:x }; //~ NOTE `x` moved here + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f05() { + let x = ~"hi"; + let _y = Foo { f:(((x))) }; //~ NOTE `x` moved here + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f10() { + let x = ~"hi"; + let _y = Foo { f:copy x }; + touch(&x); +} + +fn f20() { + let x = ~"hi"; + let _y = Foo { f:copy (x) }; + touch(&x); +} + +fn f30() { + let x = ~"hi"; + let _y = Foo { f:copy ((x)) }; + touch(&x); +} + +fn f40() { + let x = ~"hi"; + let _y = Foo { f:(((((copy ((x))))))) }; + touch(&x); +} + +fn main() {} diff --git a/src/test/compile-fail/moves-based-on-type-exprs.rs b/src/test/compile-fail/moves-based-on-type-exprs.rs new file mode 100644 index 00000000000..07fe8318836 --- /dev/null +++ b/src/test/compile-fail/moves-based-on-type-exprs.rs @@ -0,0 +1,95 @@ +// Tests that references to move-by-default values trigger moves when +// they occur as part of various kinds of expressions. + +struct Foo<A> { f: A } +fn guard(_s: ~str) -> bool {fail} +fn touch<A>(_a: &A) {} + +fn f10() { + let x = ~"hi"; + let _y = Foo { f:x }; + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f20() { + let x = ~"hi"; + let _y = (x, 3); + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f21() { + let x = ~[1, 2, 3]; + let _y = (x[0], 3); + touch(&x); +} + +fn f30(cond: bool) { + let x = ~"hi", y = ~"ho"; + let _y = if cond { + x + } else { + y + }; + touch(&x); //~ ERROR use of moved value: `x` + touch(&y); //~ ERROR use of moved value: `y` +} + +fn f40(cond: bool) { + let x = ~"hi", y = ~"ho"; + let _y = match cond { + true => x, + false => y + }; + touch(&x); //~ ERROR use of moved value: `x` + touch(&y); //~ ERROR use of moved value: `y` +} + +fn f50(cond: bool) { + let x = ~"hi", y = ~"ho"; + let _y = match cond { + _ if guard(x) => 10, + true => 10, + false => 20, + }; + touch(&x); //~ ERROR use of moved value: `x` + touch(&y); +} + +fn f70() { + let x = ~"hi"; + let _y = [x]; + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f80() { + let x = ~"hi"; + let _y = ~[x]; + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f90() { + let x = ~"hi"; + let _y = @[x]; + touch(&x); //~ ERROR use of moved value: `x` +} + +fn f100() { + let x = ~[~"hi"]; + let _y = x[0]; + touch(&x); //~ ERROR use of partially moved value: `x` +} + +fn f110() { + let x = ~[~"hi"]; + let _y = [x[0], ..1]; + touch(&x); //~ ERROR use of partially moved value: `x` +} + +fn f120() { + let x = ~[~"hi", ~"ho"]; + x[0] <-> x[1]; + touch(&x[0]); + touch(&x[1]); +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/liveness-move-from-mode.rs b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs index 52a59334d5c..3c15047a296 100644 --- a/src/test/compile-fail/liveness-move-from-mode.rs +++ b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs @@ -8,13 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn take(-_x: int) {} +fn test(_x: ~uint) {} fn main() { - - let x: int = 25; - loop { - take(move x); //~ ERROR use of moved value: `x` - //~^ NOTE move of value occurred here + let i = ~3; + for uint::range(0, 10) |_x| { + test(i); //~ ERROR moving out of captured outer immutable variable in a stack closure } } diff --git a/src/test/compile-fail/no-reuse-move-arc.rs b/src/test/compile-fail/no-reuse-move-arc.rs index 1de8362e23a..8c33e026026 100644 --- a/src/test/compile-fail/no-reuse-move-arc.rs +++ b/src/test/compile-fail/no-reuse-move-arc.rs @@ -15,7 +15,7 @@ fn main() { let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let arc_v = arc::ARC(v); - do task::spawn() |move arc_v| { //~ NOTE move of value occurred here + do task::spawn() { //~ NOTE `arc_v` moved into closure environment here let v = *arc::get(&arc_v); assert v[3] == 4; }; diff --git a/src/test/compile-fail/no-send-res-ports.rs b/src/test/compile-fail/no-send-res-ports.rs index 080f7fe3c6e..9a16ebda59d 100644 --- a/src/test/compile-fail/no-send-res-ports.rs +++ b/src/test/compile-fail/no-send-res-ports.rs @@ -24,12 +24,12 @@ fn main() { _x: x } } - + let x = ~mut Some(foo(Port(@()))); - do task::spawn |move x| { //~ ERROR not a sendable value + do task::spawn { let mut y = None; - *x <-> y; + *x <-> y; //~ ERROR not a sendable value log(error, y); } } diff --git a/src/test/compile-fail/use-after-move-self.rs b/src/test/compile-fail/use-after-move-self.rs index 8ba58cf6f66..7ad41b518a1 100644 --- a/src/test/compile-fail/use-after-move-self.rs +++ b/src/test/compile-fail/use-after-move-self.rs @@ -1,18 +1,18 @@ struct S { - x: int + x: ~int } impl S { fn foo(self) -> int { - (move self).bar(); - return self.x; //~ ERROR use of moved value + self.bar(); + return *self.x; //~ ERROR use of moved value } fn bar(self) {} } fn main() { - let x = S { x: 1 }; + let x = S { x: ~1 }; io::println(x.foo().to_str()); } diff --git a/src/test/pretty/cap-clause.rs b/src/test/pretty/cap-clause.rs deleted file mode 100644 index d8ade41257a..00000000000 --- a/src/test/pretty/cap-clause.rs +++ /dev/null @@ -1,27 +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. - -// pp-exact - -fn main() { - let x = 1; - let y = 2; - let z = 3; - let l1 = fn@(w: int, copy x) -> int { w + x + y }; - let l2 = fn@(w: int, copy x, move y) -> int { w + x + y }; - let l3 = fn@(w: int, move z) -> int { w + z }; - - let x = 1; - let y = 2; - let z = 3; - let s1 = fn~(copy x) -> int { x + y }; - let s2 = fn~(copy x, move y) -> int { x + y }; - let s3 = fn~(move z) -> int { z }; -} diff --git a/src/test/run-fail/unwind-lambda.rs b/src/test/run-fail/unwind-lambda.rs index aee6f4e480c..f2a95728237 100644 --- a/src/test/run-fail/unwind-lambda.rs +++ b/src/test/run-fail/unwind-lambda.rs @@ -18,6 +18,7 @@ fn main() { macerate(copy *tasties); } (carrots, |food| { let mush = food + cheese; + let cheese = copy cheese; let f = fn@() { let chew = mush + cheese; fail ~"so yummy" diff --git a/src/test/run-pass/assignability-trait.rs b/src/test/run-pass/assignability-trait.rs index 28c97587898..2724684dea4 100644 --- a/src/test/run-pass/assignability-trait.rs +++ b/src/test/run-pass/assignability-trait.rs @@ -43,7 +43,7 @@ fn main() { // Call a method for x.iterate() |y| { assert x[*y] == *y; } // Call a parameterized function - assert length(x) == vec::len(x); + assert length(copy x) == vec::len(x); // Call a parameterized function, with type arguments that require // a borrow assert length::<int, &[int]>(x) == vec::len(x); diff --git a/src/test/run-pass/borrowck-borrow-from-expr-block.rs b/src/test/run-pass/borrowck-borrow-from-expr-block.rs index 93ab2ba2c41..f13d6eef136 100644 --- a/src/test/run-pass/borrowck-borrow-from-expr-block.rs +++ b/src/test/run-pass/borrowck-borrow-from-expr-block.rs @@ -13,8 +13,7 @@ fn borrow(x: &int, f: fn(x: &int)) { } fn test1(x: @~int) { - // Right now, at least, this induces a copy of the unique pointer: - do borrow({*x}) |p| { + do borrow(copy *x) |p| { let x_a = ptr::addr_of(&(**x)); assert (x_a as uint) != ptr::to_uint(p); assert unsafe{*x_a} == *p; diff --git a/src/test/run-pass/cap-clause-move.rs b/src/test/run-pass/cap-clause-move.rs index 5ed27d92d79..a196593743f 100644 --- a/src/test/run-pass/cap-clause-move.rs +++ b/src/test/run-pass/cap-clause-move.rs @@ -11,29 +11,21 @@ fn main() { let x = ~1; let y = ptr::addr_of(&(*x)) as uint; - let lam_copy = fn@(copy x) -> uint { ptr::addr_of(&(*x)) as uint }; let lam_move = fn@(move x) -> uint { ptr::addr_of(&(*x)) as uint }; - assert lam_copy() != y; assert lam_move() == y; let x = ~2; let y = ptr::addr_of(&(*x)) as uint; - let lam_copy: fn@() -> uint = |copy x| ptr::addr_of(&(*x)) as uint; let lam_move: fn@() -> uint = |move x| ptr::addr_of(&(*x)) as uint; - assert lam_copy() != y; assert lam_move() == y; let x = ~3; let y = ptr::addr_of(&(*x)) as uint; - let snd_copy = fn~(copy x) -> uint { ptr::addr_of(&(*x)) as uint }; let snd_move = fn~(move x) -> uint { ptr::addr_of(&(*x)) as uint }; - assert snd_copy() != y; assert snd_move() == y; let x = ~4; let y = ptr::addr_of(&(*x)) as uint; - let lam_copy: fn~() -> uint = |copy x| ptr::addr_of(&(*x)) as uint; let lam_move: fn~() -> uint = |move x| ptr::addr_of(&(*x)) as uint; - assert lam_copy() != y; assert lam_move() == y; } diff --git a/src/test/run-pass/last-use-corner-cases.rs b/src/test/run-pass/last-use-corner-cases.rs deleted file mode 100644 index 190177e53cb..00000000000 --- a/src/test/run-pass/last-use-corner-cases.rs +++ /dev/null @@ -1,44 +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. - -// xfail-fast -#[legacy_modes]; - -fn main() { - // Make sure closing over can be a last use - let q = ~10; - let addr = ptr::addr_of(&(*q)); - let f = fn@(move q) -> *int { ptr::addr_of(&(*q)) }; - assert addr == f(); - - // But only when it really is the last use - let q = ~20; - let f = fn@(copy q) -> *int { ptr::addr_of(&(*q)) }; - assert ptr::addr_of(&(*q)) != f(); - - // Ensure function arguments and box arguments interact sanely. - fn call_me(x: fn() -> int, y: ~int) { assert x() == *y; } - let q = ~30; - call_me(|| *q, q); - - // Check that no false positives are found in loops. - let mut q = ~40, p = 10; - loop { - let i = copy q; - p += *i; - if p > 100 { break; } - } - - // Verify that blocks can't interfere with each other. - fn two_blocks(a: fn(), b: fn()) { a(); b(); a(); b(); } - let q = ~50; - two_blocks(|| { let a = copy q; assert *a == 50;}, - || { let a = copy q; assert *a == 50;}); -} diff --git a/src/test/run-pass/move-1-unique.rs b/src/test/run-pass/move-1-unique.rs index e477442376e..c5d7e9783e4 100644 --- a/src/test/run-pass/move-1-unique.rs +++ b/src/test/run-pass/move-1-unique.rs @@ -19,8 +19,8 @@ fn test(x: bool, foo: ~Triple) -> int { fn main() { let x = ~Triple{x: 1, y: 2, z: 3}; - assert (test(true, x) == 2); - assert (test(true, x) == 2); - assert (test(true, x) == 2); + assert (test(true, copy x) == 2); + assert (test(true, copy x) == 2); + assert (test(true, copy x) == 2); assert (test(false, x) == 5); } diff --git a/src/test/run-pass/move-3-unique.rs b/src/test/run-pass/move-3-unique.rs index e16955f31e8..5bf5654320a 100644 --- a/src/test/run-pass/move-3-unique.rs +++ b/src/test/run-pass/move-3-unique.rs @@ -22,7 +22,7 @@ fn test(x: bool, foo: ~Triple) -> int { fn main() { let x = ~Triple{x: 1, y: 2, z: 3}; for uint::range(0u, 10000u) |_i| { - assert (test(true, x) == 2); + assert (test(true, copy x) == 2); } assert (test(false, x) == 5); } diff --git a/src/test/run-pass/newtype-polymorphic.rs b/src/test/run-pass/newtype-polymorphic.rs index eef241be3d2..6eb2fcdd520 100644 --- a/src/test/run-pass/newtype-polymorphic.rs +++ b/src/test/run-pass/newtype-polymorphic.rs @@ -16,7 +16,7 @@ fn myvec_elt<X: Copy>(mv: myvec<X>) -> X { return mv[0]; } fn main() { let mv = myvec(~[1, 2, 3]); - assert (myvec_deref(mv)[1] == 2); - assert (myvec_elt(mv) == 1); + assert (myvec_deref(copy mv)[1] == 2); + assert (myvec_elt(copy mv) == 1); assert (mv[2] == 3); } diff --git a/src/test/run-pass/task-comm-14.rs b/src/test/run-pass/task-comm-14.rs index 62415ca438d..f49a095c334 100644 --- a/src/test/run-pass/task-comm-14.rs +++ b/src/test/run-pass/task-comm-14.rs @@ -20,7 +20,7 @@ fn main() { log(debug, i); let (p, ch) = pipes::stream(); po.add(move p); - task::spawn(|move ch, copy i| child(i, ch) ); + task::spawn({let i = i; |move ch| child(i, ch)}); i = i - 1; } diff --git a/src/test/run-pass/task-comm-3.rs b/src/test/run-pass/task-comm-3.rs index 237e5da0e9e..ebbac2d1a0a 100644 --- a/src/test/run-pass/task-comm-3.rs +++ b/src/test/run-pass/task-comm-3.rs @@ -43,11 +43,12 @@ fn test00() { let mut results = ~[]; while i < number_of_tasks { let ch = po.chan(); - do task::task().future_result(|+r| { + task::task().future_result(|+r| { results.push(move r); - }).spawn |move ch, copy i| { - test00_start(ch, i, number_of_messages) - } + }).spawn({ + let i = i; + |move ch| test00_start(ch, i, number_of_messages) + }); i = i + 1; } diff --git a/src/test/run-pass/task-spawn-move-and-copy.rs b/src/test/run-pass/task-spawn-move-and-copy.rs index ef54d3c8ddb..e68d6dd7483 100644 --- a/src/test/run-pass/task-spawn-move-and-copy.rs +++ b/src/test/run-pass/task-spawn-move-and-copy.rs @@ -16,22 +16,11 @@ fn main() { let x = ~1; let x_in_parent = ptr::addr_of(&(*x)) as uint; - let y = ~2; - let y_in_parent = ptr::addr_of(&(*y)) as uint; - - task::spawn(fn~(copy y, move x) { + task::spawn(fn~() { let x_in_child = ptr::addr_of(&(*x)) as uint; ch.send(x_in_child); - - let y_in_child = ptr::addr_of(&(*y)) as uint; - ch.send(y_in_child); }); - // Ensure last-use analysis doesn't move y to child. - let _q = y; let x_in_child = p.recv(); assert x_in_parent == x_in_child; - - let y_in_child = p.recv(); - assert y_in_parent != y_in_child; } diff --git a/src/test/run-pass/threads.rs b/src/test/run-pass/threads.rs index 66e14859079..02dcebb1132 100644 --- a/src/test/run-pass/threads.rs +++ b/src/test/run-pass/threads.rs @@ -14,7 +14,7 @@ extern mod std; fn main() { let mut i = 10; - while i > 0 { task::spawn(|copy i| child(i) ); i = i - 1; } + while i > 0 { task::spawn({let i = i; || child(i)}); i = i - 1; } debug!("main thread exiting"); } |
