mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-20 19:04:21 +00:00
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:
parent
42b462e076
commit
0682ad0eb9
@ -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 }
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) };
|
||||
|
@ -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;
|
||||
|
@ -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, ());
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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('#')
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
},
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
815
src/librustc/middle/moves.rs
Normal file
815
src/librustc/middle/moves.rs
Normal 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}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, ());
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 => ()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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(),
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
@ -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] = ~[];
|
||||
|
@ -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);
|
||||
}
|
||||
_ => ()
|
||||
|
@ -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) |
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
|
@ -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(
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 => {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
),
|
||||
@~[]
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(_) => ~""
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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 );
|
||||
}
|
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
18
src/test/compile-fail/borrowck-move-by-capture.rs
Normal file
18
src/test/compile-fail/borrowck-move-by-capture.rs
Normal 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
|
||||
};
|
||||
}
|
8
src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
Normal file
8
src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
Normal 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
|
||||
}
|
@ -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 };
|
||||
}
|
@ -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 };
|
||||
}
|
@ -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 };
|
||||
}
|
@ -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() {
|
||||
}
|
@ -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
|
||||
};
|
||||
}
|
@ -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) {
|
||||
};
|
||||
}
|
@ -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) {
|
||||
};
|
||||
}
|
@ -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`
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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() {
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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)}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user