Finalize moves-based-on-type implementation.

Changes:

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

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

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

Def ids in params:

Move captures into a map instead of recomputing.

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

View File

@ -1234,7 +1234,7 @@ For example:
~~~~
trait Num {
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 }

View File

@ -440,8 +440,8 @@ Finally, tasks can be configured to not propagate failure to each
other at all, using `task::spawn_unlinked` for _isolated failure_.
~~~
# 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 {

View File

@ -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")

View File

@ -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);
}

View File

@ -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) };

View File

@ -836,12 +836,20 @@ fn test_run_basic() {
po.recv();
}
#[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;

View File

@ -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, ());
}

View File

@ -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));

View File

@ -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;

View File

@ -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
};

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -165,7 +165,8 @@ pub fn note_linkage_attrs(intr: @ident_interner, diag: span_handler,
}
}
fn crate_matches(crate_data: @~[u8], +metas: ~[@ast::meta_item],
fn crate_matches(crate_data: @~[u8],
metas: &[@ast::meta_item],
hash: ~str) -> bool {
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));

View File

@ -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,

View File

@ -342,7 +342,6 @@ pub fn enc_arg(w: io::Writer, cx: @ctxt, arg: ty::arg) {
pub fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
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('#')

View File

@ -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> {
@ -513,6 +515,30 @@ impl freevar_entry: tr {
}
}
// ______________________________________________________________________
// Encoding and decoding of CaptureVar information
trait capture_var_helper {
fn read_capture_var(xcx: extended_decode_ctxt) -> moves::CaptureVar;
}
impl reader::Decoder : capture_var_helper {
fn read_capture_var(xcx: extended_decode_ctxt) -> moves::CaptureVar {
let cvar: moves::CaptureVar = Decodable::decode(&self);
cvar.tr(xcx)
}
}
impl moves::CaptureVar : tr {
fn tr(xcx: extended_decode_ctxt) -> moves::CaptureVar {
moves::CaptureVar {
def: self.def.tr(xcx),
span: self.span.tr(xcx),
mode: self.mode
}
}
}
// ______________________________________________________________________
// Encoding and decoding of method_map_entry
@ -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));

View File

@ -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.
}
_ => { }
}

View File

@ -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.
}
}
}

View File

@ -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,

View File

@ -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,
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 enum borrowck_ctxt {
borrowck_ctxt_(@borrowck_ctxt_)
// 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)
}

View File

@ -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
};
}
impl PreserveCtxt {
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
priv impl &preserve_ctxt {
fn tcx() -> 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 {

View File

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

View File

@ -32,13 +32,13 @@ pub fn check_crate(tcx: ty::ctxt, crate: @crate) {
expr_loop(ref b, _) => {
(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);

View File

@ -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)
}
},

View File

@ -413,8 +413,8 @@ pub fn lit_to_const(lit: @lit) -> const_val {
}
pub fn compare_const_vals(a: const_val, b: const_val) -> int {
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 {

View File

@ -48,7 +48,7 @@ fn collect_freevars(def_map: resolve::DefMap, blk: ast::blk)
let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
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;
}

View File

@ -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; }
let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id);
if !check_send(cx, var_t, fv.span) { return; }
// copied in data must be copyable, but moved in data can be anything
let is_implicit = fv.is_some();
if !is_move {
check_copy(cx, id, var_t, sp, is_implicit,
Some(("non-copyable value cannot be copied into a \
~fn closure",
"to copy values into a ~fn closure, use a \
capture clause: `fn~(copy x)` or `|copy x|`")));
}
// check that only immutable variables are implicitly copied in
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; }
let id = ast_util::def_id_of_def(fv.def).node;
let var_t = ty::node_id_to_type(cx.tcx, id);
if !check_durable(cx.tcx, var_t, fv.span) { return; }
// copied in data must be copyable, but moved in data can be anything
let is_implicit = fv.is_some();
if !is_move {
check_copy(cx, id, var_t, sp, is_implicit,
Some(("non-copyable value cannot be copied into a \
@fn closure",
"to copy values into a @fn closure, use a \
capture clause: `fn~(copy x)` or `|copy x|`")));
}
// check that only immutable variables are implicitly copied in
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:
fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {
// Check kinds on free variables:
do with_appropriate_checker(cx, fn_id) |chk| {
// Begin by checking the variables in the capture clause, if any.
// Here we slightly abuse the map function to both check and report
// errors and produce a list of the def id's for all capture
// variables. This list is used below to avoid checking and reporting
// on a given variable twice.
let cap_clause = match fk {
visit::fk_anon(_, cc) | visit::fk_fn_block(cc) => cc,
visit::fk_item_fn(*) | visit::fk_method(*) |
visit::fk_dtor(*) => @~[]
};
let captured_vars = do (*cap_clause).map |cap_item| {
let cap_def = cx.tcx.def_map.get(cap_item.id);
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
chk(cx, fn_id, None, cap_item.is_move, ty, cap_item.span);
cap_def_id
};
// Iterate over any free variables that may not have appeared in the
// capture list. Ensure that they too are of the appropriate kind.
for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) |fv| {
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_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_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_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_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_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");
}
}
}
expr_field(lhs, _, _) => {
// If this is a method call with a by-val argument, we need
// to check the copy
match cx.method_map.find(e.id) {
Some(ref mme) => {
match ty::arg_mode(cx.tcx, mme.self_arg) {
by_copy => maybe_copy(cx, lhs,
Some(("method call takes its self argument by copy",
""))),
by_ref | by_val | by_move => ()
}
}
_ => ()
}
}
expr_repeat(element, count_expr, _) => {
let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span);
if count == 1 {
maybe_copy(cx, element, Some(("trivial repeat takes its element \
by copy", "")));
} else {
let element_ty = ty::expr_ty(cx.tcx, element);
check_copy(cx, element.id, element_ty, element.span, true,
Some(("repeat takes its elements by copy", "")));
}
}
_ => { }
_ => {}
}
visit::visit_expr(e, cx, v);
}
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", ""))),
_ => {}
}
}
}
_ => {}
}
visit::visit_stmt(stmt, 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()
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_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));
}
}
}
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));
}
}

View File

@ -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.
}

View File

@ -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

View File

@ -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)}
}

View File

@ -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,

View File

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

View File

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

View File

@ -11,7 +11,6 @@
use core::prelude::*;
use 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;
}

View File

@ -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, ());
}

View File

@ -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,

View File

@ -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 {

View File

@ -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 =

View File

@ -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
};

View File

@ -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

View File

@ -15,12 +15,12 @@ use back::link::{mangle_internal_name_by_path_and_seq};
use back::link::{mangle_internal_name_by_path};
use 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,

View File

@ -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 => ()
}

View File

@ -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 {

View File

@ -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(),

View File

@ -12,45 +12,53 @@
# Translation of expressions.
## User's guide
## Recommended entry point
If you wish to translate an expression, there are two basic modes:
If you wish to translate an expression, the preferred way to do
so is to use:
1. `trans_into(block, expr, Dest) -> block`
2. `trans_to_datum(block, expr) -> DatumBlock`
expr::trans_into(block, expr, Dest) -> block
`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.
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.
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).
## Translation to a datum
## More specific functions
In some cases, `trans_into()` is too narrow of an interface.
Generally this occurs either when you know that the result value is
going to be a scalar, or when you need to evaluate the expression into
some memory location so you can go and inspect it (e.g., assignments,
`match` expressions, the `&` operator).
The two functions above are the most general and can handle any
situation, but there are a few other functions that are useful
in specific scenarios:
In such cases, you want the following function:
- `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.
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 {

View File

@ -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

View File

@ -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
}
};

View File

@ -24,7 +24,7 @@ pub fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef {
let llty = type_of(ccx, arg.ty);
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] = ~[];

View File

@ -72,7 +72,7 @@ pub fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
ty::ty_fn(ref fn_ty) => {
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);
}
_ => ()

View File

@ -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) |

View File

@ -381,7 +381,7 @@ pub fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
demand::eqtype(fcx, pat.span, region_ty, typ);
}
// 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);
}
}

View File

@ -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)),

View File

@ -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(

View File

@ -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 \

View File

@ -165,7 +165,7 @@ fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
resolve_method_map_entry(wbcx.fcx, e.span, e.id);
resolve_method_map_entry(wbcx.fcx, e.span, e.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);

View File

@ -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));
}

View File

@ -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)))
}

View File

@ -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);
}

View File

@ -421,8 +421,15 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
}
ty_infer(infer_ty) => infer_ty.to_str(),
ty_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) => {

View File

@ -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
}));
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)
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)
}
}
}
};

View File

@ -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);
}
}

View File

@ -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))
}
};

View File

@ -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 {

View File

@ -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),

View File

@ -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();
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"
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

View File

@ -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

View File

@ -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();

View File

@ -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))
}

View File

@ -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

View File

@ -256,7 +256,7 @@ pub fn ident_to_path(s: span, +i: ident) -> @path {
pub fn ident_to_pat(id: node_id, s: span, +i: ident) -> @pat {
@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 => {
}
}

View File

@ -173,8 +173,8 @@ pub fn find_meta_items_by_name(metas: &[@ast::meta_item], name: &str) ->
* Returns true if a list of meta items contains another meta item. The
* 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; }
}

View File

@ -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)
)
),
@~[]
)
)
);

View File

@ -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,

View File

@ -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(),

View File

@ -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))
}

View File

@ -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,

View File

@ -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(_) => ~""
}
}

View File

@ -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);

View File

@ -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

View File

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

View File

@ -1,18 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:moving out of captured outer immutable variable in a stack closure
fn force(f: fn()) { f(); }
fn main() {
let mut x = @{x: 17, y: 2};
let y = @{x: 5, y: 5};
force(|| x = move y );
}

View File

@ -13,18 +13,18 @@ fn borrow(v: &int, f: fn(x: &int)) {
}
fn box_imm() {
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
});
}

View File

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

View File

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

View File

@ -1,15 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:variable `x` captured more than once
fn main() {
let x = 5;
let y = fn~(move x, copy x) -> int { x };
}

View File

@ -1,15 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:variable `x` captured more than once
fn main() {
let x = 5;
let y = fn~(copy x, copy x) -> int { x };
}

View File

@ -1,15 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:variable `x` captured more than once
fn main() {
let x = 5;
let y = fn~(move x, move x) -> int { x };
}

View File

@ -1,32 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: copying a noncopyable value
struct foo { x: int, }
impl foo : Drop {
fn finalize(&self) {}
}
fn foo(x: int) -> foo {
foo {
x: x
}
}
fn to_lambda2(b: foo) -> fn@(uint) -> uint {
// test case where copy clause specifies a value that is not used
// in fn@ body, but value is illegal to copy:
return fn@(u: uint, copy b) -> uint { 22u };
}
fn main() {
}

View File

@ -1,17 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
let x = 5;
let _y = fn~(move x) -> int {
let _z = fn~(move x) -> int { x }; //~ ERROR moving out of captured outer variable in a heap closure
22
};
}

View File

@ -1,16 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:unresolved name
fn main() {
let x = 5;
let y = fn~(copy z, copy x) {
};
}

View File

@ -1,16 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:unresolved name
fn main() {
let x = 5;
let y = fn~(move z, move x) {
};
}

View File

@ -1,15 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
let x = 5;
let _y = fn~(move x) { }; //~ WARNING captured variable `x` not used in closure
let _z = x; //~ ERROR use of moved value: `x`
}

View File

@ -1,24 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo(_f: fn()) {}
fn bar(_f: @int) {}
fn main() {
let x = @3;
foo(|| bar(x) );
let x = @3;
foo(|copy x| bar(x) ); //~ ERROR cannot capture values explicitly with a block closure
let x = @3;
foo(|move x| bar(x) ); //~ ERROR cannot capture values explicitly with a block closure
}

View File

@ -1,30 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn closure2(+x: core::util::NonCopyable)
-> (core::util::NonCopyable, fn@() -> core::util::NonCopyable) {
let f = fn@(copy x) -> core::util::NonCopyable {
//~^ ERROR copying a noncopyable value
//~^^ NOTE non-copyable value cannot be copied into a @fn closure
copy x
//~^ ERROR copying a noncopyable value
};
(move x,f)
}
fn closure3(+x: core::util::NonCopyable) {
do task::spawn |copy x| {
//~^ ERROR copying a noncopyable value
//~^^ NOTE non-copyable value cannot be copied into a ~fn closure
error!("%?", x);
}
error!("%?", x);
}
fn main() {
}

View File

@ -25,7 +25,7 @@ struct Foo {
fn main() {
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));
}

View File

@ -1,17 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:moving out of captured outer immutable variable in a stack closure
fn test(-x: uint) {}
fn main() {
let i = 3;
for uint::range(0, 10) |_x| {test(move i)}
}

View File

@ -31,11 +31,14 @@ fn bar() {
}
fn car() {
// 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;
}

View File

@ -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`
}
}

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