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
125 changed files with 2674 additions and 2633 deletions
|
@ -1234,7 +1234,7 @@ For example:
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
trait Num {
|
trait Num {
|
||||||
static pure fn from_int(n: int) -> self;
|
static pure fn from_int(n: int) -> Self;
|
||||||
}
|
}
|
||||||
impl float: Num {
|
impl float: Num {
|
||||||
static pure fn from_int(n: int) -> float { n as float }
|
static pure fn from_int(n: int) -> float { n as float }
|
||||||
|
|
|
@ -440,8 +440,8 @@ Finally, tasks can be configured to not propagate failure to each
|
||||||
other at all, using `task::spawn_unlinked` for _isolated failure_.
|
other at all, using `task::spawn_unlinked` for _isolated failure_.
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
# fn random() -> int { 100 }
|
# fn random() -> uint { 100 }
|
||||||
# fn sleep_for(i: int) { for i.times { task::yield() } }
|
# fn sleep_for(i: uint) { for i.times { task::yield() } }
|
||||||
# do task::try::<()> {
|
# do task::try::<()> {
|
||||||
let (time1, time2) = (random(), random());
|
let (time1, time2) = (random(), random());
|
||||||
do task::spawn_unlinked {
|
do task::spawn_unlinked {
|
||||||
|
|
|
@ -51,7 +51,7 @@ try:
|
||||||
report_err("TODO is deprecated; use FIXME")
|
report_err("TODO is deprecated; use FIXME")
|
||||||
idx = line.find("// NOTE")
|
idx = line.find("// NOTE")
|
||||||
if idx != -1:
|
if idx != -1:
|
||||||
report_warn("NOTE:" + line[idx + len("// NOTE"):])
|
report_warn("NOTE" + line[idx + len("// NOTE"):])
|
||||||
if (line.find('\t') != -1 and
|
if (line.find('\t') != -1 and
|
||||||
fileinput.filename().find("Makefile") == -1):
|
fileinput.filename().find("Makefile") == -1):
|
||||||
report_err("tab character")
|
report_err("tab character")
|
||||||
|
|
|
@ -1965,19 +1965,17 @@ pub fn main() {
|
||||||
c = configure(o);
|
c = configure(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
let c = &move c;
|
|
||||||
|
|
||||||
match o.free[1] {
|
match o.free[1] {
|
||||||
~"init" => cmd_init(c),
|
~"init" => cmd_init(&c),
|
||||||
~"install" => cmd_install(c),
|
~"install" => cmd_install(&c),
|
||||||
~"uninstall" => cmd_uninstall(c),
|
~"uninstall" => cmd_uninstall(&c),
|
||||||
~"list" => cmd_list(c),
|
~"list" => cmd_list(&c),
|
||||||
~"search" => cmd_search(c),
|
~"search" => cmd_search(&c),
|
||||||
~"sources" => cmd_sources(c),
|
~"sources" => cmd_sources(&c),
|
||||||
_ => cmd_usage()
|
_ => cmd_usage()
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_cache(c);
|
dump_cache(&c);
|
||||||
dump_sources(c);
|
dump_sources(&c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,8 +177,10 @@ fn get_global_state() -> Exclusive<GlobalState> {
|
||||||
let state = ~exclusive(state);
|
let state = ~exclusive(state);
|
||||||
|
|
||||||
// Convert it to an integer
|
// Convert it to an integer
|
||||||
let state_ptr: &Exclusive<GlobalState> = state;
|
let state_i: int = unsafe {
|
||||||
let state_i: int = unsafe { transmute(state_ptr) };
|
let state_ptr: &Exclusive<GlobalState> = state;
|
||||||
|
transmute(state_ptr)
|
||||||
|
};
|
||||||
|
|
||||||
// Swap our structure into the global pointer
|
// Swap our structure into the global pointer
|
||||||
let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) };
|
let prev_i = unsafe { atomic_cxchg(&mut *global_ptr, 0, state_i) };
|
||||||
|
|
|
@ -836,12 +836,20 @@ fn test_run_basic() {
|
||||||
po.recv();
|
po.recv();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
struct Wrapper {
|
||||||
|
mut f: Option<Chan<()>>
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_wrapper() {
|
fn test_add_wrapper() {
|
||||||
let (po, ch) = stream::<()>();
|
let (po, ch) = stream::<()>();
|
||||||
let b0 = task();
|
let b0 = task();
|
||||||
|
let ch = Wrapper { f: Some(ch) };
|
||||||
let b1 = do b0.add_wrapper |body| {
|
let b1 = do b0.add_wrapper |body| {
|
||||||
|
let ch = Wrapper { f: Some(ch.f.swap_unwrap()) };
|
||||||
fn~(move body) {
|
fn~(move body) {
|
||||||
|
let ch = ch.f.swap_unwrap();
|
||||||
body();
|
body();
|
||||||
ch.send(());
|
ch.send(());
|
||||||
}
|
}
|
||||||
|
@ -929,9 +937,12 @@ fn test_spawn_sched_childs_on_default_sched() {
|
||||||
// Assuming tests run on the default scheduler
|
// Assuming tests run on the default scheduler
|
||||||
let default_id = unsafe { rt::rust_get_sched_id() };
|
let default_id = unsafe { rt::rust_get_sched_id() };
|
||||||
|
|
||||||
|
let ch = Wrapper { f: Some(ch) };
|
||||||
do spawn_sched(SingleThreaded) {
|
do spawn_sched(SingleThreaded) {
|
||||||
let parent_sched_id = unsafe { rt::rust_get_sched_id() };
|
let parent_sched_id = unsafe { rt::rust_get_sched_id() };
|
||||||
|
let ch = Wrapper { f: Some(ch.f.swap_unwrap()) };
|
||||||
do spawn {
|
do spawn {
|
||||||
|
let ch = ch.f.swap_unwrap();
|
||||||
let child_sched_id = unsafe { rt::rust_get_sched_id() };
|
let child_sched_id = unsafe { rt::rust_get_sched_id() };
|
||||||
assert parent_sched_id != child_sched_id;
|
assert parent_sched_id != child_sched_id;
|
||||||
assert child_sched_id == default_id;
|
assert child_sched_id == default_id;
|
||||||
|
|
|
@ -191,7 +191,7 @@ pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] {
|
||||||
let mut minimized = ~[];
|
let mut minimized = ~[];
|
||||||
for rpaths.each |rpath| {
|
for rpaths.each |rpath| {
|
||||||
let s = rpath.to_str();
|
let s = rpath.to_str();
|
||||||
if !set.contains_key(s) {
|
if !set.contains_key_ref(&s) {
|
||||||
minimized.push(/*bad*/copy *rpath);
|
minimized.push(/*bad*/copy *rpath);
|
||||||
set.insert(s, ());
|
set.insert(s, ());
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use front;
|
||||||
use lib::llvm::llvm;
|
use lib::llvm::llvm;
|
||||||
use metadata::{creader, cstore, filesearch};
|
use metadata::{creader, cstore, filesearch};
|
||||||
use metadata;
|
use metadata;
|
||||||
use middle::{trans, freevars, kind, ty, typeck, lint};
|
use middle::{trans, freevars, kind, ty, typeck, lint, astencode};
|
||||||
use middle;
|
use middle;
|
||||||
use session::{Session, Session_, OptLevel, No, Less, Default, Aggressive};
|
use session::{Session, Session_, OptLevel, No, Less, Default, Aggressive};
|
||||||
use session;
|
use session;
|
||||||
|
@ -281,20 +281,26 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
|
||||||
time(time_passes, ~"loop checking", ||
|
time(time_passes, ~"loop checking", ||
|
||||||
middle::check_loop::check_crate(ty_cx, crate));
|
middle::check_loop::check_crate(ty_cx, crate));
|
||||||
|
|
||||||
time(time_passes, ~"mode computation", ||
|
let middle::moves::MoveMaps {moves_map, variable_moves_map,
|
||||||
middle::mode::compute_modes(ty_cx, method_map, crate));
|
capture_map} =
|
||||||
|
time(time_passes, ~"compute moves", ||
|
||||||
|
middle::moves::compute_moves(ty_cx, method_map, crate));
|
||||||
|
|
||||||
time(time_passes, ~"match checking", ||
|
time(time_passes, ~"match checking", ||
|
||||||
middle::check_match::check_crate(ty_cx, method_map, crate));
|
middle::check_match::check_crate(ty_cx, method_map,
|
||||||
|
moves_map, crate));
|
||||||
|
|
||||||
let last_use_map =
|
let last_use_map =
|
||||||
time(time_passes, ~"liveness checking", ||
|
time(time_passes, ~"liveness checking", ||
|
||||||
middle::liveness::check_crate(ty_cx, method_map, crate));
|
middle::liveness::check_crate(ty_cx, method_map,
|
||||||
|
variable_moves_map,
|
||||||
|
capture_map, crate));
|
||||||
|
|
||||||
let (root_map, mutbl_map, write_guard_map) =
|
let (root_map, mutbl_map, write_guard_map) =
|
||||||
time(time_passes, ~"borrow checking", ||
|
time(time_passes, ~"borrow checking", ||
|
||||||
middle::borrowck::check_crate(ty_cx, method_map,
|
middle::borrowck::check_crate(ty_cx, method_map,
|
||||||
last_use_map, crate));
|
moves_map, capture_map,
|
||||||
|
crate));
|
||||||
|
|
||||||
time(time_passes, ~"kind checking", ||
|
time(time_passes, ~"kind checking", ||
|
||||||
kind::check_crate(ty_cx, method_map, last_use_map, crate));
|
kind::check_crate(ty_cx, method_map, last_use_map, crate));
|
||||||
|
@ -304,12 +310,16 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
|
||||||
|
|
||||||
if upto == cu_no_trans { return {crate: crate, tcx: Some(ty_cx)}; }
|
if upto == cu_no_trans { return {crate: crate, tcx: Some(ty_cx)}; }
|
||||||
|
|
||||||
let maps = {mutbl_map: mutbl_map,
|
let maps = astencode::Maps {
|
||||||
root_map: root_map,
|
mutbl_map: mutbl_map,
|
||||||
last_use_map: last_use_map,
|
root_map: root_map,
|
||||||
method_map: method_map,
|
last_use_map: last_use_map,
|
||||||
vtable_map: vtable_map,
|
method_map: method_map,
|
||||||
write_guard_map: write_guard_map};
|
vtable_map: vtable_map,
|
||||||
|
write_guard_map: write_guard_map,
|
||||||
|
moves_map: moves_map,
|
||||||
|
capture_map: capture_map
|
||||||
|
};
|
||||||
|
|
||||||
time(time_passes, ~"translation", ||
|
time(time_passes, ~"translation", ||
|
||||||
trans::base::trans_crate(sess, crate, ty_cx,
|
trans::base::trans_crate(sess, crate, ty_cx,
|
||||||
|
@ -528,7 +538,7 @@ pub fn build_session_options(+binary: ~str,
|
||||||
getopts::opt_strs(matches, level_name));
|
getopts::opt_strs(matches, level_name));
|
||||||
for flags.each |lint_name| {
|
for flags.each |lint_name| {
|
||||||
let lint_name = str::replace(*lint_name, ~"-", ~"_");
|
let lint_name = str::replace(*lint_name, ~"-", ~"_");
|
||||||
match lint_dict.find(lint_name) {
|
match lint_dict.find(/*bad*/ copy lint_name) {
|
||||||
None => {
|
None => {
|
||||||
early_error(demitter, fmt!("unknown %s flag: %s",
|
early_error(demitter, fmt!("unknown %s flag: %s",
|
||||||
level_name, lint_name));
|
level_name, lint_name));
|
||||||
|
|
|
@ -190,7 +190,7 @@ pub fn metas_in_cfg(cfg: ast::crate_cfg,
|
||||||
if !has_cfg_metas { return true; }
|
if !has_cfg_metas { return true; }
|
||||||
|
|
||||||
for cfg_metas.each |cfg_mi| {
|
for cfg_metas.each |cfg_mi| {
|
||||||
if attr::contains(/*bad*/copy cfg, *cfg_mi) { return true; }
|
if attr::contains(cfg, *cfg_mi) { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub fn modify_for_testing(sess: session::Session,
|
||||||
// We generate the test harness when building in the 'test'
|
// We generate the test harness when building in the 'test'
|
||||||
// configuration, either with the '--test' or '--cfg test'
|
// configuration, either with the '--test' or '--cfg test'
|
||||||
// command line options.
|
// command line options.
|
||||||
let should_test = attr::contains(/*bad*/copy crate.node.config,
|
let should_test = attr::contains(crate.node.config,
|
||||||
attr::mk_word_item(~"test"));
|
attr::mk_word_item(~"test"));
|
||||||
|
|
||||||
if should_test {
|
if should_test {
|
||||||
|
@ -510,7 +510,7 @@ fn mk_test_wrapper(cx: test_ctxt,
|
||||||
let wrapper_expr = ast::expr {
|
let wrapper_expr = ast::expr {
|
||||||
id: cx.sess.next_node_id(),
|
id: cx.sess.next_node_id(),
|
||||||
callee_id: cx.sess.next_node_id(),
|
callee_id: cx.sess.next_node_id(),
|
||||||
node: ast::expr_fn(ast::ProtoBare, wrapper_decl, wrapper_body, @~[]),
|
node: ast::expr_fn(ast::ProtoBare, wrapper_decl, wrapper_body),
|
||||||
span: span
|
span: span
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1344,10 +1344,10 @@ pub fn mk_type_names() -> type_names {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_to_str(names: type_names, ty: TypeRef) -> @str {
|
pub fn type_to_str(names: type_names, ty: TypeRef) -> @str {
|
||||||
return type_to_str_inner(names, ~[], ty);
|
return type_to_str_inner(names, [], ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_to_str_inner(names: type_names, +outer0: ~[TypeRef], ty: TypeRef)
|
pub fn type_to_str_inner(names: type_names, +outer0: &[TypeRef], ty: TypeRef)
|
||||||
-> @str {
|
-> @str {
|
||||||
unsafe {
|
unsafe {
|
||||||
match type_has_name(names, ty) {
|
match type_has_name(names, ty) {
|
||||||
|
@ -1355,12 +1355,11 @@ pub fn type_to_str_inner(names: type_names, +outer0: ~[TypeRef], ty: TypeRef)
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME #2543: Bad copy.
|
let outer = vec::append_one(outer0.to_vec(), ty);
|
||||||
let outer = vec::append_one(copy outer0, ty);
|
|
||||||
|
|
||||||
let kind = llvm::LLVMGetTypeKind(ty);
|
let kind = llvm::LLVMGetTypeKind(ty);
|
||||||
|
|
||||||
fn tys_str(names: type_names, outer: ~[TypeRef],
|
fn tys_str(names: type_names, outer: &[TypeRef],
|
||||||
tys: ~[TypeRef]) -> @str {
|
tys: ~[TypeRef]) -> @str {
|
||||||
let mut s = ~"";
|
let mut s = ~"";
|
||||||
let mut first: bool = true;
|
let mut first: bool = true;
|
||||||
|
|
|
@ -133,7 +133,8 @@ pub enum astencode_tag { // Reserves 0x50 -- 0x6f
|
||||||
tag_table_vtable_map = 0x61,
|
tag_table_vtable_map = 0x61,
|
||||||
tag_table_adjustments = 0x62,
|
tag_table_adjustments = 0x62,
|
||||||
tag_table_legacy_boxed_trait = 0x63,
|
tag_table_legacy_boxed_trait = 0x63,
|
||||||
tag_table_value_mode = 0x64
|
tag_table_moves_map = 0x64,
|
||||||
|
tag_table_capture_map = 0x65
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const tag_item_trait_method_sort: uint = 0x70;
|
pub const tag_item_trait_method_sort: uint = 0x70;
|
||||||
|
|
|
@ -163,8 +163,8 @@ fn visit_item(e: env, i: @ast::item) {
|
||||||
None => /*bad*/copy *e.intr.get(i.ident)
|
None => /*bad*/copy *e.intr.get(i.ident)
|
||||||
};
|
};
|
||||||
if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
|
if attr::find_attrs_by_name(i.attrs, ~"nolink").is_empty() {
|
||||||
already_added = !cstore::add_used_library(cstore,
|
already_added =
|
||||||
foreign_name);
|
!cstore::add_used_library(cstore, copy foreign_name);
|
||||||
}
|
}
|
||||||
if !link_args.is_empty() && already_added {
|
if !link_args.is_empty() && already_added {
|
||||||
e.diag.span_fatal(i.span, ~"library '" + foreign_name +
|
e.diag.span_fatal(i.span, ~"library '" + foreign_name +
|
||||||
|
@ -281,7 +281,8 @@ fn resolve_crate_deps(e: env, cdata: @~[u8]) -> cstore::cnum_map {
|
||||||
let cmetas = metas_with(/*bad*/copy dep.vers, ~"vers", ~[]);
|
let cmetas = metas_with(/*bad*/copy dep.vers, ~"vers", ~[]);
|
||||||
debug!("resolving dep crate %s ver: %s hash: %s",
|
debug!("resolving dep crate %s ver: %s hash: %s",
|
||||||
*e.intr.get(dep.name), dep.vers, dep.hash);
|
*e.intr.get(dep.name), dep.vers, dep.hash);
|
||||||
match existing_match(e, metas_with_ident(*e.intr.get(cname), cmetas),
|
match existing_match(e, metas_with_ident(copy *e.intr.get(cname),
|
||||||
|
copy cmetas),
|
||||||
dep.hash) {
|
dep.hash) {
|
||||||
Some(local_cnum) => {
|
Some(local_cnum) => {
|
||||||
debug!("already have it");
|
debug!("already have it");
|
||||||
|
|
|
@ -648,14 +648,22 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: writer::Encoder,
|
||||||
/* Encode the dtor */
|
/* Encode the dtor */
|
||||||
do struct_def.dtor.iter |dtor| {
|
do struct_def.dtor.iter |dtor| {
|
||||||
index.push({val: dtor.node.id, pos: ebml_w.writer.tell()});
|
index.push({val: dtor.node.id, pos: ebml_w.writer.tell()});
|
||||||
encode_info_for_ctor(ecx, ebml_w, dtor.node.id,
|
encode_info_for_ctor(ecx,
|
||||||
|
ebml_w,
|
||||||
|
dtor.node.id,
|
||||||
ecx.tcx.sess.ident_of(
|
ecx.tcx.sess.ident_of(
|
||||||
ecx.tcx.sess.str_of(item.ident) +
|
ecx.tcx.sess.str_of(item.ident) +
|
||||||
~"_dtor"),
|
~"_dtor"),
|
||||||
path, if tps.len() > 0u {
|
path,
|
||||||
Some(ii_dtor(*dtor, item.ident, tps,
|
if tps.len() > 0u {
|
||||||
|
Some(ii_dtor(copy *dtor,
|
||||||
|
item.ident,
|
||||||
|
copy tps,
|
||||||
local_def(item.id))) }
|
local_def(item.id))) }
|
||||||
else { None }, tps);
|
else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
tps);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Index the class*/
|
/* Index the class*/
|
||||||
|
@ -869,27 +877,35 @@ fn encode_info_for_items(ecx: @encode_ctxt, ebml_w: writer::Encoder,
|
||||||
syntax::parse::token::special_idents::invalid);
|
syntax::parse::token::special_idents::invalid);
|
||||||
visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor {
|
visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor {
|
||||||
visit_expr: |_e, _cx, _v| { },
|
visit_expr: |_e, _cx, _v| { },
|
||||||
visit_item: |i, cx, v, copy ebml_w| {
|
visit_item: {
|
||||||
visit::visit_item(i, cx, v);
|
let ebml_w = copy ebml_w;
|
||||||
match ecx.tcx.items.get(i.id) {
|
|i, cx, v| {
|
||||||
ast_map::node_item(_, pt) => {
|
visit::visit_item(i, cx, v);
|
||||||
encode_info_for_item(ecx, ebml_w, i, index, *pt);
|
match ecx.tcx.items.get(i.id) {
|
||||||
}
|
ast_map::node_item(_, pt) => {
|
||||||
_ => fail ~"bad item"
|
encode_info_for_item(ecx, ebml_w, i,
|
||||||
|
index, *pt);
|
||||||
|
}
|
||||||
|
_ => fail ~"bad item"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visit_foreign_item: |ni, cx, v, copy ebml_w| {
|
visit_foreign_item: {
|
||||||
visit::visit_foreign_item(ni, cx, v);
|
let ebml_w = copy ebml_w;
|
||||||
match ecx.tcx.items.get(ni.id) {
|
|ni, cx, v| {
|
||||||
ast_map::node_foreign_item(_, abi, pt) => {
|
visit::visit_foreign_item(ni, cx, v);
|
||||||
encode_info_for_foreign_item(ecx, ebml_w, ni,
|
match ecx.tcx.items.get(ni.id) {
|
||||||
index, /*bad*/copy *pt, abi);
|
ast_map::node_foreign_item(_, abi, pt) => {
|
||||||
}
|
encode_info_for_foreign_item(ecx, ebml_w, ni,
|
||||||
// case for separate item and foreign-item tables
|
index, /*bad*/copy *pt,
|
||||||
_ => fail ~"bad foreign item"
|
abi);
|
||||||
|
}
|
||||||
|
// case for separate item and foreign-item tables
|
||||||
|
_ => fail ~"bad foreign item"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
,.. *visit::default_visitor()
|
..*visit::default_visitor()
|
||||||
}));
|
}));
|
||||||
ebml_w.end_tag();
|
ebml_w.end_tag();
|
||||||
return /*bad*/copy *index;
|
return /*bad*/copy *index;
|
||||||
|
|
|
@ -165,7 +165,8 @@ pub fn note_linkage_attrs(intr: @ident_interner, diag: span_handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crate_matches(crate_data: @~[u8], +metas: ~[@ast::meta_item],
|
fn crate_matches(crate_data: @~[u8],
|
||||||
|
metas: &[@ast::meta_item],
|
||||||
hash: ~str) -> bool {
|
hash: ~str) -> bool {
|
||||||
let attrs = decoder::get_crate_attributes(crate_data);
|
let attrs = decoder::get_crate_attributes(crate_data);
|
||||||
let linkage_metas = attr::find_linkage_metas(attrs);
|
let linkage_metas = attr::find_linkage_metas(attrs);
|
||||||
|
@ -177,7 +178,7 @@ fn crate_matches(crate_data: @~[u8], +metas: ~[@ast::meta_item],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata_matches(extern_metas: ~[@ast::meta_item],
|
pub fn metadata_matches(extern_metas: ~[@ast::meta_item],
|
||||||
local_metas: ~[@ast::meta_item]) -> bool {
|
local_metas: &[@ast::meta_item]) -> bool {
|
||||||
|
|
||||||
debug!("matching %u metadata requirements against %u items",
|
debug!("matching %u metadata requirements against %u items",
|
||||||
vec::len(local_metas), vec::len(extern_metas));
|
vec::len(local_metas), vec::len(extern_metas));
|
||||||
|
|
|
@ -275,6 +275,7 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t {
|
||||||
}
|
}
|
||||||
'p' => {
|
'p' => {
|
||||||
let did = parse_def(st, TypeParameter, conv);
|
let did = parse_def(st, TypeParameter, conv);
|
||||||
|
debug!("parsed ty_param: did=%?", did);
|
||||||
return ty::mk_param(st.tcx, parse_int(st) as uint, did);
|
return ty::mk_param(st.tcx, parse_int(st) as uint, did);
|
||||||
}
|
}
|
||||||
's' => {
|
's' => {
|
||||||
|
@ -422,7 +423,6 @@ fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
|
||||||
|
|
||||||
fn parse_mode(st: @pstate) -> ast::mode {
|
fn parse_mode(st: @pstate) -> ast::mode {
|
||||||
let m = ast::expl(match next(st) {
|
let m = ast::expl(match next(st) {
|
||||||
'-' => ast::by_move,
|
|
||||||
'+' => ast::by_copy,
|
'+' => ast::by_copy,
|
||||||
'=' => ast::by_ref,
|
'=' => ast::by_ref,
|
||||||
'#' => ast::by_val,
|
'#' => ast::by_val,
|
||||||
|
|
|
@ -342,7 +342,6 @@ pub fn enc_arg(w: io::Writer, cx: @ctxt, arg: ty::arg) {
|
||||||
|
|
||||||
pub fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
|
pub fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
|
||||||
match ty::resolved_mode(cx.tcx, m) {
|
match ty::resolved_mode(cx.tcx, m) {
|
||||||
by_move => w.write_char('-'),
|
|
||||||
by_copy => w.write_char('+'),
|
by_copy => w.write_char('+'),
|
||||||
by_ref => w.write_char('='),
|
by_ref => w.write_char('='),
|
||||||
by_val => w.write_char('#')
|
by_val => w.write_char('#')
|
||||||
|
|
|
@ -22,7 +22,7 @@ use metadata::tyencode;
|
||||||
use middle::freevars::freevar_entry;
|
use middle::freevars::freevar_entry;
|
||||||
use middle::typeck::{method_origin, method_map_entry, vtable_res};
|
use middle::typeck::{method_origin, method_map_entry, vtable_res};
|
||||||
use middle::typeck::{vtable_origin};
|
use middle::typeck::{vtable_origin};
|
||||||
use middle::{ty, typeck};
|
use middle::{ty, typeck, moves};
|
||||||
use middle;
|
use middle;
|
||||||
use util::ppaux::ty_to_str;
|
use util::ppaux::ty_to_str;
|
||||||
|
|
||||||
|
@ -51,19 +51,21 @@ use syntax;
|
||||||
use writer = std::ebml::writer;
|
use writer = std::ebml::writer;
|
||||||
|
|
||||||
// Auxiliary maps of things to be encoded
|
// Auxiliary maps of things to be encoded
|
||||||
pub type maps = {
|
pub struct Maps {
|
||||||
mutbl_map: middle::borrowck::mutbl_map,
|
mutbl_map: middle::borrowck::mutbl_map,
|
||||||
root_map: middle::borrowck::root_map,
|
root_map: middle::borrowck::root_map,
|
||||||
last_use_map: middle::liveness::last_use_map,
|
last_use_map: middle::liveness::last_use_map,
|
||||||
method_map: middle::typeck::method_map,
|
method_map: middle::typeck::method_map,
|
||||||
vtable_map: middle::typeck::vtable_map,
|
vtable_map: middle::typeck::vtable_map,
|
||||||
write_guard_map: middle::borrowck::write_guard_map,
|
write_guard_map: middle::borrowck::write_guard_map,
|
||||||
};
|
moves_map: middle::moves::MovesMap,
|
||||||
|
capture_map: middle::moves::CaptureMap,
|
||||||
|
}
|
||||||
|
|
||||||
type decode_ctxt = @{
|
type decode_ctxt = @{
|
||||||
cdata: cstore::crate_metadata,
|
cdata: cstore::crate_metadata,
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
maps: maps
|
maps: Maps
|
||||||
};
|
};
|
||||||
|
|
||||||
type extended_decode_ctxt_ = {
|
type extended_decode_ctxt_ = {
|
||||||
|
@ -91,7 +93,7 @@ pub fn encode_inlined_item(ecx: @e::encode_ctxt,
|
||||||
ebml_w: writer::Encoder,
|
ebml_w: writer::Encoder,
|
||||||
path: &[ast_map::path_elt],
|
path: &[ast_map::path_elt],
|
||||||
ii: ast::inlined_item,
|
ii: ast::inlined_item,
|
||||||
maps: maps) {
|
maps: Maps) {
|
||||||
debug!("> Encoding inlined item: %s::%s (%u)",
|
debug!("> Encoding inlined item: %s::%s (%u)",
|
||||||
ast_map::path_to_str(path, ecx.tcx.sess.parse_sess.interner),
|
ast_map::path_to_str(path, ecx.tcx.sess.parse_sess.interner),
|
||||||
ecx.tcx.sess.str_of(ii.ident()),
|
ecx.tcx.sess.str_of(ii.ident()),
|
||||||
|
@ -112,7 +114,7 @@ pub fn encode_inlined_item(ecx: @e::encode_ctxt,
|
||||||
|
|
||||||
pub fn decode_inlined_item(cdata: cstore::crate_metadata,
|
pub fn decode_inlined_item(cdata: cstore::crate_metadata,
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
maps: maps,
|
maps: Maps,
|
||||||
+path: ast_map::path,
|
+path: ast_map::path,
|
||||||
par_doc: ebml::Doc)
|
par_doc: ebml::Doc)
|
||||||
-> Option<ast::inlined_item> {
|
-> Option<ast::inlined_item> {
|
||||||
|
@ -513,6 +515,30 @@ impl freevar_entry: tr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ______________________________________________________________________
|
||||||
|
// Encoding and decoding of CaptureVar information
|
||||||
|
|
||||||
|
trait capture_var_helper {
|
||||||
|
fn read_capture_var(xcx: extended_decode_ctxt) -> moves::CaptureVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl reader::Decoder : capture_var_helper {
|
||||||
|
fn read_capture_var(xcx: extended_decode_ctxt) -> moves::CaptureVar {
|
||||||
|
let cvar: moves::CaptureVar = Decodable::decode(&self);
|
||||||
|
cvar.tr(xcx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl moves::CaptureVar : tr {
|
||||||
|
fn tr(xcx: extended_decode_ctxt) -> moves::CaptureVar {
|
||||||
|
moves::CaptureVar {
|
||||||
|
def: self.def.tr(xcx),
|
||||||
|
span: self.span.tr(xcx),
|
||||||
|
mode: self.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ______________________________________________________________________
|
// ______________________________________________________________________
|
||||||
// Encoding and decoding of method_map_entry
|
// Encoding and decoding of method_map_entry
|
||||||
|
|
||||||
|
@ -788,10 +814,11 @@ impl writer::Encoder: write_tag_and_id {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_side_tables_for_ii(ecx: @e::encode_ctxt,
|
fn encode_side_tables_for_ii(ecx: @e::encode_ctxt,
|
||||||
maps: maps,
|
maps: Maps,
|
||||||
ebml_w: writer::Encoder,
|
ebml_w: writer::Encoder,
|
||||||
ii: ast::inlined_item) {
|
ii: ast::inlined_item) {
|
||||||
do ebml_w.wr_tag(c::tag_table as uint) {
|
do ebml_w.wr_tag(c::tag_table as uint) {
|
||||||
|
let ebml_w = copy ebml_w;
|
||||||
ast_util::visit_ids_for_inlined_item(
|
ast_util::visit_ids_for_inlined_item(
|
||||||
ii,
|
ii,
|
||||||
fn@(id: ast::node_id, copy ebml_w) {
|
fn@(id: ast::node_id, copy ebml_w) {
|
||||||
|
@ -804,7 +831,7 @@ fn encode_side_tables_for_ii(ecx: @e::encode_ctxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
||||||
maps: maps,
|
maps: Maps,
|
||||||
ebml_w: writer::Encoder,
|
ebml_w: writer::Encoder,
|
||||||
id: ast::node_id) {
|
id: ast::node_id) {
|
||||||
let tcx = ecx.tcx;
|
let tcx = ecx.tcx;
|
||||||
|
@ -931,11 +958,19 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do option::iter(&tcx.value_modes.find(id)) |vm| {
|
for maps.moves_map.find(id).each |_| {
|
||||||
do ebml_w.tag(c::tag_table_value_mode) {
|
do ebml_w.tag(c::tag_table_moves_map) {
|
||||||
|
ebml_w.id(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for maps.capture_map.find(id).each |cap_vars| {
|
||||||
|
do ebml_w.tag(c::tag_table_capture_map) {
|
||||||
ebml_w.id(id);
|
ebml_w.id(id);
|
||||||
do ebml_w.tag(c::tag_table_val) {
|
do ebml_w.tag(c::tag_table_val) {
|
||||||
(*vm).encode(&ebml_w)
|
do ebml_w.emit_from_vec(*cap_vars) |cap_var| {
|
||||||
|
cap_var.encode(&ebml_w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -980,10 +1015,24 @@ impl reader::Decoder: ebml_decoder_decoder_helpers {
|
||||||
// context. However, we do not bother, because region types
|
// context. However, we do not bother, because region types
|
||||||
// are not used during trans.
|
// are not used during trans.
|
||||||
|
|
||||||
do self.read_opaque |doc| {
|
return do self.read_opaque |doc| {
|
||||||
tydecode::parse_ty_data(
|
|
||||||
|
let ty = tydecode::parse_ty_data(
|
||||||
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|
doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
|
||||||
|s, a| self.convert_def_id(xcx, s, a))
|
|s, a| self.convert_def_id(xcx, s, a));
|
||||||
|
|
||||||
|
debug!("read_ty(%s) = %s",
|
||||||
|
type_string(doc), ty_to_str(xcx.dcx.tcx, ty));
|
||||||
|
|
||||||
|
ty
|
||||||
|
};
|
||||||
|
|
||||||
|
fn type_string(doc: ebml::Doc) -> ~str {
|
||||||
|
let mut str = ~"";
|
||||||
|
for uint::range(doc.start, doc.end) |i| {
|
||||||
|
str::push_char(&mut str, doc.data[i] as char);
|
||||||
|
}
|
||||||
|
str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1034,10 +1083,12 @@ impl reader::Decoder: ebml_decoder_decoder_helpers {
|
||||||
* to refer to the new, cloned copy of the type parameter.
|
* to refer to the new, cloned copy of the type parameter.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
match source {
|
let r = match source {
|
||||||
NominalType | TypeWithId => xcx.tr_def_id(did),
|
NominalType | TypeWithId => xcx.tr_def_id(did),
|
||||||
TypeParameter => xcx.tr_intern_def_id(did)
|
TypeParameter => xcx.tr_intern_def_id(did)
|
||||||
}
|
};
|
||||||
|
debug!("convert_def_id(source=%?, did=%?)=%?", source, did, r);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1057,6 +1108,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
||||||
dcx.maps.mutbl_map.insert(id, ());
|
dcx.maps.mutbl_map.insert(id, ());
|
||||||
} else if tag == (c::tag_table_legacy_boxed_trait as uint) {
|
} else if tag == (c::tag_table_legacy_boxed_trait as uint) {
|
||||||
dcx.tcx.legacy_boxed_traits.insert(id, ());
|
dcx.tcx.legacy_boxed_traits.insert(id, ());
|
||||||
|
} else if tag == (c::tag_table_moves_map as uint) {
|
||||||
|
dcx.maps.moves_map.insert(id, ());
|
||||||
} else {
|
} else {
|
||||||
let val_doc = entry_doc[c::tag_table_val as uint];
|
let val_doc = entry_doc[c::tag_table_val as uint];
|
||||||
let val_dsr = &reader::Decoder(val_doc);
|
let val_dsr = &reader::Decoder(val_doc);
|
||||||
|
@ -1065,6 +1118,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
||||||
dcx.tcx.def_map.insert(id, def);
|
dcx.tcx.def_map.insert(id, def);
|
||||||
} else if tag == (c::tag_table_node_type as uint) {
|
} else if tag == (c::tag_table_node_type as uint) {
|
||||||
let ty = val_dsr.read_ty(xcx);
|
let ty = val_dsr.read_ty(xcx);
|
||||||
|
debug!("inserting ty for node %?: %s",
|
||||||
|
id, ty_to_str(dcx.tcx, ty));
|
||||||
(*dcx.tcx.node_types).insert(id as uint, ty);
|
(*dcx.tcx.node_types).insert(id as uint, ty);
|
||||||
} else if tag == (c::tag_table_node_type_subst as uint) {
|
} else if tag == (c::tag_table_node_type_subst as uint) {
|
||||||
let tys = val_dsr.read_tys(xcx);
|
let tys = val_dsr.read_tys(xcx);
|
||||||
|
@ -1098,9 +1153,12 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
||||||
let adj: @ty::AutoAdjustment = @Decodable::decode(val_dsr);
|
let adj: @ty::AutoAdjustment = @Decodable::decode(val_dsr);
|
||||||
adj.tr(xcx);
|
adj.tr(xcx);
|
||||||
dcx.tcx.adjustments.insert(id, adj);
|
dcx.tcx.adjustments.insert(id, adj);
|
||||||
} else if tag == (c::tag_table_value_mode as uint) {
|
} else if tag == (c::tag_table_capture_map as uint) {
|
||||||
let vm: ty::ValueMode = Decodable::decode(val_dsr);
|
let cvars =
|
||||||
dcx.tcx.value_modes.insert(id, vm);
|
at_vec::from_owned(
|
||||||
|
val_dsr.read_to_vec(
|
||||||
|
|| val_dsr.read_capture_var(xcx)));
|
||||||
|
dcx.maps.capture_map.insert(id, cvars);
|
||||||
} else {
|
} else {
|
||||||
xcx.dcx.tcx.sess.bug(
|
xcx.dcx.tcx.sess.bug(
|
||||||
fmt!("unknown tag found in side tables: %x", tag));
|
fmt!("unknown tag found in side tables: %x", tag));
|
||||||
|
|
|
@ -19,13 +19,15 @@
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use middle::borrowck::{Loan, bckerr, borrowck_ctxt, inherent_mutability};
|
use middle::moves;
|
||||||
|
use middle::borrowck::{Loan, bckerr, BorrowckCtxt, inherent_mutability};
|
||||||
use middle::borrowck::{req_maps, root_map_key, save_and_restore};
|
use middle::borrowck::{req_maps, root_map_key, save_and_restore};
|
||||||
|
use middle::borrowck::{MoveError, MoveOk, MoveFromIllegalCmt};
|
||||||
|
use middle::borrowck::{MoveWhileBorrowed};
|
||||||
use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
|
use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
|
||||||
use middle::mem_categorization::{cat_local, cat_rvalue, cat_self};
|
use middle::mem_categorization::{cat_local, cat_rvalue, cat_self};
|
||||||
use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg};
|
use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg};
|
||||||
use middle::mem_categorization::{lp_comp, lp_deref, lp_local};
|
use middle::mem_categorization::{lp_comp, lp_deref, lp_local};
|
||||||
use middle::ty::{CopyValue, MoveValue, ReadValue};
|
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use util::ppaux::ty_to_str;
|
use util::ppaux::ty_to_str;
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ use syntax::print::pprust;
|
||||||
use syntax::visit;
|
use syntax::visit;
|
||||||
|
|
||||||
enum check_loan_ctxt = @{
|
enum check_loan_ctxt = @{
|
||||||
bccx: borrowck_ctxt,
|
bccx: @BorrowckCtxt,
|
||||||
req_maps: req_maps,
|
req_maps: req_maps,
|
||||||
|
|
||||||
reported: HashMap<ast::node_id, ()>,
|
reported: HashMap<ast::node_id, ()>,
|
||||||
|
@ -63,9 +65,9 @@ enum purity_cause {
|
||||||
pc_cmt(bckerr)
|
pc_cmt(bckerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_loans(bccx: borrowck_ctxt,
|
pub fn check_loans(bccx: @BorrowckCtxt,
|
||||||
req_maps: req_maps,
|
req_maps: req_maps,
|
||||||
crate: @ast::crate) {
|
crate: @ast::crate) {
|
||||||
let clcx = check_loan_ctxt(@{bccx: bccx,
|
let clcx = check_loan_ctxt(@{bccx: bccx,
|
||||||
req_maps: req_maps,
|
req_maps: req_maps,
|
||||||
reported: HashMap(),
|
reported: HashMap(),
|
||||||
|
@ -471,12 +473,40 @@ impl check_loan_ctxt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_move_out(ex: @ast::expr) {
|
fn check_move_out_from_expr(ex: @ast::expr) {
|
||||||
let cmt = self.bccx.cat_expr(ex);
|
match ex.node {
|
||||||
self.check_move_out_from_cmt(cmt);
|
ast::expr_paren(*) => {
|
||||||
|
/* In the case of an expr_paren(), the expression inside
|
||||||
|
* the parens will also be marked as being moved. Ignore
|
||||||
|
* the parents then so as not to report duplicate errors. */
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let cmt = self.bccx.cat_expr(ex);
|
||||||
|
match self.analyze_move_out_from_cmt(cmt) {
|
||||||
|
MoveOk => {}
|
||||||
|
MoveFromIllegalCmt(_) => {
|
||||||
|
self.bccx.span_err(
|
||||||
|
cmt.span,
|
||||||
|
fmt!("moving out of %s",
|
||||||
|
self.bccx.cmt_to_str(cmt)));
|
||||||
|
}
|
||||||
|
MoveWhileBorrowed(_, loan_cmt) => {
|
||||||
|
self.bccx.span_err(
|
||||||
|
cmt.span,
|
||||||
|
fmt!("moving out of %s prohibited \
|
||||||
|
due to outstanding loan",
|
||||||
|
self.bccx.cmt_to_str(cmt)));
|
||||||
|
self.bccx.span_note(
|
||||||
|
loan_cmt.span,
|
||||||
|
fmt!("loan of %s granted here",
|
||||||
|
self.bccx.cmt_to_str(loan_cmt)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_move_out_from_cmt(cmt: cmt) {
|
fn analyze_move_out_from_cmt(cmt: cmt) -> MoveError {
|
||||||
debug!("check_move_out_from_cmt(cmt=%s)",
|
debug!("check_move_out_from_cmt(cmt=%s)",
|
||||||
self.bccx.cmt_to_repr(cmt));
|
self.bccx.cmt_to_repr(cmt));
|
||||||
|
|
||||||
|
@ -493,59 +523,27 @@ impl check_loan_ctxt {
|
||||||
|
|
||||||
// Nothing else.
|
// Nothing else.
|
||||||
_ => {
|
_ => {
|
||||||
self.bccx.span_err(
|
return MoveFromIllegalCmt(cmt);
|
||||||
cmt.span,
|
|
||||||
fmt!("moving out of %s", self.bccx.cmt_to_str(cmt)));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.bccx.add_to_mutbl_map(cmt);
|
self.bccx.add_to_mutbl_map(cmt);
|
||||||
|
|
||||||
// check for a conflicting loan:
|
// check for a conflicting loan:
|
||||||
let lp = match cmt.lp {
|
for cmt.lp.each |lp| {
|
||||||
None => return,
|
for self.walk_loans_of(cmt.id, *lp) |loan| {
|
||||||
Some(lp) => lp
|
return MoveWhileBorrowed(cmt, loan.cmt);
|
||||||
};
|
|
||||||
for self.walk_loans_of(cmt.id, lp) |loan| {
|
|
||||||
self.bccx.span_err(
|
|
||||||
cmt.span,
|
|
||||||
fmt!("moving out of %s prohibited due to outstanding loan",
|
|
||||||
self.bccx.cmt_to_str(cmt)));
|
|
||||||
self.bccx.span_note(
|
|
||||||
loan.cmt.span,
|
|
||||||
fmt!("loan of %s granted here",
|
|
||||||
self.bccx.cmt_to_str(loan.cmt)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Very subtle (#2633): liveness can mark options as last_use even
|
|
||||||
// when there is an outstanding loan. In that case, it is not
|
|
||||||
// safe to consider the use a last_use.
|
|
||||||
fn check_last_use(expr: @ast::expr) {
|
|
||||||
debug!("Checking last use of expr %?", expr.id);
|
|
||||||
let cmt = self.bccx.cat_expr(expr);
|
|
||||||
let lp = match cmt.lp {
|
|
||||||
None => {
|
|
||||||
debug!("Not a loanable expression");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Some(lp) => lp
|
|
||||||
};
|
|
||||||
for self.walk_loans_of(cmt.id, lp) |_loan| {
|
|
||||||
debug!("Removing last use entry %? due to outstanding loan",
|
|
||||||
expr.id);
|
|
||||||
self.bccx.last_use_map.remove(expr.id);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return MoveOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_call(expr: @ast::expr,
|
fn check_call(expr: @ast::expr,
|
||||||
callee: Option<@ast::expr>,
|
callee: Option<@ast::expr>,
|
||||||
callee_id: ast::node_id,
|
callee_id: ast::node_id,
|
||||||
callee_span: span,
|
callee_span: span,
|
||||||
args: ~[@ast::expr]) {
|
args: &[@ast::expr]) {
|
||||||
match self.purity(expr.id) {
|
match self.purity(expr.id) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(ref pc) => {
|
Some(ref pc) => {
|
||||||
|
@ -557,35 +555,26 @@ impl check_loan_ctxt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let arg_tys =
|
|
||||||
ty::ty_fn_args(
|
|
||||||
ty::node_id_to_type(self.tcx(), callee_id));
|
|
||||||
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
|
||||||
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
|
||||||
ast::by_move => {
|
|
||||||
self.check_move_out(*arg);
|
|
||||||
}
|
|
||||||
ast::by_ref |
|
|
||||||
ast::by_copy | ast::by_val => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
|
fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
|
||||||
sp: span, id: ast::node_id, &&self: check_loan_ctxt,
|
sp: span, id: ast::node_id, &&self: check_loan_ctxt,
|
||||||
visitor: visit::vt<check_loan_ctxt>) {
|
visitor: visit::vt<check_loan_ctxt>)
|
||||||
|
{
|
||||||
|
let is_stack_closure = self.is_stack_closure(id);
|
||||||
|
let fty = ty::node_id_to_type(self.tcx(), id);
|
||||||
|
let fty_proto = ty::ty_fn_proto(fty);
|
||||||
|
|
||||||
|
check_moves_from_captured_variables(self, id, fty_proto);
|
||||||
|
|
||||||
debug!("purity on entry=%?", copy self.declared_purity);
|
debug!("purity on entry=%?", copy self.declared_purity);
|
||||||
do save_and_restore(&mut(self.declared_purity)) {
|
do save_and_restore(&mut(self.declared_purity)) {
|
||||||
do save_and_restore(&mut(self.fn_args)) {
|
do save_and_restore(&mut(self.fn_args)) {
|
||||||
let is_stack_closure = self.is_stack_closure(id);
|
|
||||||
let fty = ty::node_id_to_type(self.tcx(), id);
|
|
||||||
self.declared_purity = ty::determine_inherited_purity(
|
self.declared_purity = ty::determine_inherited_purity(
|
||||||
copy self.declared_purity,
|
copy self.declared_purity,
|
||||||
ty::ty_fn_purity(fty),
|
ty::ty_fn_purity(fty),
|
||||||
ty::ty_fn_proto(fty));
|
fty_proto);
|
||||||
|
|
||||||
match fk {
|
match fk {
|
||||||
visit::fk_anon(*) |
|
visit::fk_anon(*) |
|
||||||
|
@ -616,6 +605,50 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("purity on exit=%?", copy self.declared_purity);
|
debug!("purity on exit=%?", copy self.declared_purity);
|
||||||
|
|
||||||
|
fn check_moves_from_captured_variables(&&self: check_loan_ctxt,
|
||||||
|
id: ast::node_id,
|
||||||
|
fty_proto: ast::Proto)
|
||||||
|
{
|
||||||
|
match fty_proto {
|
||||||
|
ast::ProtoBox | ast::ProtoUniq => {
|
||||||
|
let cap_vars = self.bccx.capture_map.get(id);
|
||||||
|
for cap_vars.each |cap_var| {
|
||||||
|
match cap_var.mode {
|
||||||
|
moves::CapRef | moves::CapCopy => { loop; }
|
||||||
|
moves::CapMove => { }
|
||||||
|
}
|
||||||
|
let def_id = ast_util::def_id_of_def(cap_var.def).node;
|
||||||
|
let ty = ty::node_id_to_type(self.tcx(), def_id);
|
||||||
|
let cmt = self.bccx.cat_def(id, cap_var.span,
|
||||||
|
ty, cap_var.def);
|
||||||
|
let move_err = self.analyze_move_out_from_cmt(cmt);
|
||||||
|
match move_err {
|
||||||
|
MoveOk => {}
|
||||||
|
MoveFromIllegalCmt(move_cmt) => {
|
||||||
|
self.bccx.span_err(
|
||||||
|
cap_var.span,
|
||||||
|
fmt!("illegal by-move capture of %s",
|
||||||
|
self.bccx.cmt_to_str(move_cmt)));
|
||||||
|
}
|
||||||
|
MoveWhileBorrowed(move_cmt, loan_cmt) => {
|
||||||
|
self.bccx.span_err(
|
||||||
|
cap_var.span,
|
||||||
|
fmt!("by-move capture of %s prohibited \
|
||||||
|
due to outstanding loan",
|
||||||
|
self.bccx.cmt_to_str(move_cmt)));
|
||||||
|
self.bccx.span_note(
|
||||||
|
loan_cmt.span,
|
||||||
|
fmt!("loan of %s granted here",
|
||||||
|
self.bccx.cmt_to_str(loan_cmt)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::ProtoBorrowed | ast::ProtoBare => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_loans_in_local(local: @ast::local,
|
fn check_loans_in_local(local: @ast::local,
|
||||||
|
@ -632,48 +665,24 @@ fn check_loans_in_expr(expr: @ast::expr,
|
||||||
|
|
||||||
self.check_for_conflicting_loans(expr.id);
|
self.check_for_conflicting_loans(expr.id);
|
||||||
|
|
||||||
// If this is a move, check it.
|
if self.bccx.moves_map.contains_key(expr.id) {
|
||||||
match self.tcx().value_modes.find(expr.id) {
|
self.check_move_out_from_expr(expr);
|
||||||
Some(MoveValue) => self.check_move_out(expr),
|
|
||||||
Some(ReadValue) | Some(CopyValue) | None => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match /*bad*/copy expr.node {
|
match expr.node {
|
||||||
ast::expr_path(*) if self.bccx.last_use_map.contains_key(expr.id) => {
|
|
||||||
self.check_last_use(expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
ast::expr_swap(l, r) => {
|
ast::expr_swap(l, r) => {
|
||||||
self.check_assignment(at_swap, l);
|
self.check_assignment(at_swap, l);
|
||||||
self.check_assignment(at_swap, r);
|
self.check_assignment(at_swap, r);
|
||||||
}
|
}
|
||||||
ast::expr_unary_move(src) => {
|
|
||||||
self.check_move_out(src);
|
|
||||||
}
|
|
||||||
ast::expr_assign(dest, _) |
|
ast::expr_assign(dest, _) |
|
||||||
ast::expr_assign_op(_, dest, _) => {
|
ast::expr_assign_op(_, dest, _) => {
|
||||||
self.check_assignment(at_straight_up, dest);
|
self.check_assignment(at_straight_up, dest);
|
||||||
}
|
}
|
||||||
ast::expr_fn(_, _, _, cap_clause) |
|
ast::expr_call(f, ref args, _) => {
|
||||||
ast::expr_fn_block(_, _, cap_clause) => {
|
self.check_call(expr, Some(f), f.id, f.span, *args);
|
||||||
for (*cap_clause).each |cap_item| {
|
|
||||||
if cap_item.is_move {
|
|
||||||
let def = self.tcx().def_map.get(cap_item.id);
|
|
||||||
|
|
||||||
// Hack: the type that is used in the cmt doesn't actually
|
|
||||||
// matter here, so just subst nil instead of looking up
|
|
||||||
// the type of the def that is referred to
|
|
||||||
let cmt = self.bccx.cat_def(cap_item.id, cap_item.span,
|
|
||||||
ty::mk_nil(self.tcx()), def);
|
|
||||||
self.check_move_out_from_cmt(cmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast::expr_call(f, args, _) => {
|
ast::expr_method_call(_, _, _, ref args, _) => {
|
||||||
self.check_call(expr, Some(f), f.id, f.span, args);
|
self.check_call(expr, None, expr.callee_id, expr.span, *args);
|
||||||
}
|
|
||||||
ast::expr_method_call(_, _, _, args, _) => {
|
|
||||||
self.check_call(expr, None, expr.callee_id, expr.span, args);
|
|
||||||
}
|
}
|
||||||
ast::expr_index(_, rval) |
|
ast::expr_index(_, rval) |
|
||||||
ast::expr_binary(_, _, rval)
|
ast::expr_binary(_, _, rval)
|
||||||
|
@ -692,6 +701,18 @@ fn check_loans_in_expr(expr: @ast::expr,
|
||||||
expr.span,
|
expr.span,
|
||||||
~[]);
|
~[]);
|
||||||
}
|
}
|
||||||
|
ast::expr_match(*) => {
|
||||||
|
// Note: moves out of pattern bindings are not checked by
|
||||||
|
// the borrow checker, at least not directly. What happens
|
||||||
|
// is that if there are any moved bindings, the discriminant
|
||||||
|
// will be considered a move, and this will be checked as
|
||||||
|
// normal. Then, in `middle::check_match`, we will check
|
||||||
|
// that no move occurs in a binding that is underneath an
|
||||||
|
// `@` or `&`. Together these give the same guarantees as
|
||||||
|
// `check_move_out_from_expr()` without requiring us to
|
||||||
|
// rewalk the patterns and rebuild the pattern
|
||||||
|
// categorizations.
|
||||||
|
}
|
||||||
_ => { }
|
_ => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use middle::borrowck::preserve::{preserve_condition, pc_ok, pc_if_pure};
|
use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure};
|
||||||
use middle::borrowck::{Loan, bckerr, bckres, borrowck_ctxt, err_mutbl};
|
use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
|
||||||
use middle::borrowck::{req_maps};
|
use middle::borrowck::{req_maps};
|
||||||
use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant};
|
use middle::mem_categorization::{cat_binding, cat_discr, cmt, comp_variant};
|
||||||
use middle::mem_categorization::{mem_categorization_ctxt};
|
use middle::mem_categorization::{mem_categorization_ctxt};
|
||||||
|
@ -68,13 +68,13 @@ use syntax::visit;
|
||||||
/// No good. Instead what will happen is that `root_ub` will be set to the
|
/// No good. Instead what will happen is that `root_ub` will be set to the
|
||||||
/// body of the while loop and we will refuse to root the pointer `&*x`
|
/// body of the while loop and we will refuse to root the pointer `&*x`
|
||||||
/// because it would have to be rooted for a region greater than `root_ub`.
|
/// because it would have to be rooted for a region greater than `root_ub`.
|
||||||
enum gather_loan_ctxt = @{bccx: borrowck_ctxt,
|
enum gather_loan_ctxt = @{bccx: @BorrowckCtxt,
|
||||||
req_maps: req_maps,
|
req_maps: req_maps,
|
||||||
mut item_ub: ast::node_id,
|
mut item_ub: ast::node_id,
|
||||||
mut root_ub: ast::node_id,
|
mut root_ub: ast::node_id,
|
||||||
mut ignore_adjustments: LinearSet<ast::node_id>};
|
mut ignore_adjustments: LinearSet<ast::node_id>};
|
||||||
|
|
||||||
pub fn gather_loans(bccx: borrowck_ctxt, crate: @ast::crate) -> req_maps {
|
pub fn gather_loans(bccx: @BorrowckCtxt, crate: @ast::crate) -> req_maps {
|
||||||
let glcx = gather_loan_ctxt(@{bccx: bccx,
|
let glcx = gather_loan_ctxt(@{bccx: bccx,
|
||||||
req_maps: {req_loan_map: HashMap(),
|
req_maps: {req_loan_map: HashMap(),
|
||||||
pure_map: HashMap()},
|
pure_map: HashMap()},
|
||||||
|
@ -148,11 +148,11 @@ fn req_loans_in_expr(ex: @ast::expr,
|
||||||
let scope_r = ty::re_scope(ex.id);
|
let scope_r = ty::re_scope(ex.id);
|
||||||
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
||||||
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
||||||
ast::by_ref => {
|
ast::by_ref => {
|
||||||
let arg_cmt = self.bccx.cat_expr(*arg);
|
let arg_cmt = self.bccx.cat_expr(*arg);
|
||||||
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
||||||
}
|
}
|
||||||
ast::by_val | ast::by_move | ast::by_copy => {}
|
ast::by_val | ast::by_copy => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
visit::visit_expr(ex, self, vt);
|
visit::visit_expr(ex, self, vt);
|
||||||
|
@ -164,11 +164,11 @@ fn req_loans_in_expr(ex: @ast::expr,
|
||||||
let scope_r = ty::re_scope(ex.id);
|
let scope_r = ty::re_scope(ex.id);
|
||||||
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
||||||
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
||||||
ast::by_ref => {
|
ast::by_ref => {
|
||||||
let arg_cmt = self.bccx.cat_expr(*arg);
|
let arg_cmt = self.bccx.cat_expr(*arg);
|
||||||
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
||||||
}
|
}
|
||||||
ast::by_val | ast::by_move | ast::by_copy => {}
|
ast::by_val | ast::by_copy => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ impl gather_loan_ctxt {
|
||||||
// matches with the actual mutability (but if an immutable
|
// matches with the actual mutability (but if an immutable
|
||||||
// pointer is desired, that is ok as long as we are pure)
|
// pointer is desired, that is ok as long as we are pure)
|
||||||
None => {
|
None => {
|
||||||
let result: bckres<preserve_condition> = {
|
let result: bckres<PreserveCondition> = {
|
||||||
do self.check_mutbl(req_mutbl, cmt).chain |pc1| {
|
do self.check_mutbl(req_mutbl, cmt).chain |pc1| {
|
||||||
do self.bccx.preserve(cmt, scope_r,
|
do self.bccx.preserve(cmt, scope_r,
|
||||||
self.item_ub,
|
self.item_ub,
|
||||||
|
@ -385,16 +385,16 @@ impl gather_loan_ctxt {
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(pc_ok) => {
|
Ok(PcOk) => {
|
||||||
debug!("result of preserve: pc_ok");
|
debug!("result of preserve: PcOk");
|
||||||
|
|
||||||
// we were able guarantee the validity of the ptr,
|
// we were able guarantee the validity of the ptr,
|
||||||
// perhaps by rooting or because it is immutably
|
// perhaps by rooting or because it is immutably
|
||||||
// rooted. good.
|
// rooted. good.
|
||||||
self.bccx.stable_paths += 1;
|
self.bccx.stable_paths += 1;
|
||||||
}
|
}
|
||||||
Ok(pc_if_pure(ref e)) => {
|
Ok(PcIfPure(ref e)) => {
|
||||||
debug!("result of preserve: %?", pc_if_pure((*e)));
|
debug!("result of preserve: %?", PcIfPure((*e)));
|
||||||
|
|
||||||
// we are only able to guarantee the validity if
|
// we are only able to guarantee the validity if
|
||||||
// the scope is pure
|
// the scope is pure
|
||||||
|
@ -443,25 +443,25 @@ impl gather_loan_ctxt {
|
||||||
// mutable memory.
|
// mutable memory.
|
||||||
fn check_mutbl(&self,
|
fn check_mutbl(&self,
|
||||||
req_mutbl: ast::mutability,
|
req_mutbl: ast::mutability,
|
||||||
cmt: cmt) -> bckres<preserve_condition> {
|
cmt: cmt) -> bckres<PreserveCondition> {
|
||||||
debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)",
|
debug!("check_mutbl(req_mutbl=%?, cmt.mutbl=%?)",
|
||||||
req_mutbl, cmt.mutbl);
|
req_mutbl, cmt.mutbl);
|
||||||
|
|
||||||
if req_mutbl == m_const || req_mutbl == cmt.mutbl {
|
if req_mutbl == m_const || req_mutbl == cmt.mutbl {
|
||||||
debug!("required is const or they are the same");
|
debug!("required is const or they are the same");
|
||||||
Ok(pc_ok)
|
Ok(PcOk)
|
||||||
} else {
|
} else {
|
||||||
let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) };
|
let e = bckerr { cmt: cmt, code: err_mutbl(req_mutbl) };
|
||||||
if req_mutbl == m_imm {
|
if req_mutbl == m_imm {
|
||||||
// if this is an @mut box, then it's generally OK to borrow as
|
// if this is an @mut box, then it's generally OK to borrow as
|
||||||
// &imm; this will result in a write guard
|
// &imm; this will result in a write guard
|
||||||
if cmt.cat.is_mutable_box() {
|
if cmt.cat.is_mutable_box() {
|
||||||
Ok(pc_ok)
|
Ok(PcOk)
|
||||||
} else {
|
} else {
|
||||||
// you can treat mutable things as imm if you are pure
|
// you can treat mutable things as imm if you are pure
|
||||||
debug!("imm required, must be pure");
|
debug!("imm required, must be pure");
|
||||||
|
|
||||||
Ok(pc_if_pure(e))
|
Ok(PcIfPure(e))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(e)
|
Err(e)
|
||||||
|
@ -556,11 +556,6 @@ impl gather_loan_ctxt {
|
||||||
match pat.node {
|
match pat.node {
|
||||||
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
|
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
|
||||||
match bm {
|
match bm {
|
||||||
ast::bind_by_value | ast::bind_by_move => {
|
|
||||||
// copying does not borrow anything, so no check
|
|
||||||
// is required
|
|
||||||
// as for move, check::_match ensures it's from an rvalue.
|
|
||||||
}
|
|
||||||
ast::bind_by_ref(mutbl) => {
|
ast::bind_by_ref(mutbl) => {
|
||||||
// ref x or ref x @ p --- creates a ptr which must
|
// ref x or ref x @ p --- creates a ptr which must
|
||||||
// remain valid for the scope of the match
|
// remain valid for the scope of the match
|
||||||
|
@ -582,9 +577,9 @@ impl gather_loan_ctxt {
|
||||||
self.guarantee_valid(cmt, mutbl, scope_r);
|
self.guarantee_valid(cmt, mutbl, scope_r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::bind_infer => {
|
ast::bind_by_copy | ast::bind_infer => {
|
||||||
// Nothing to do here; this is either a copy or a move;
|
// Nothing to do here; neither copies nor moves induce
|
||||||
// thus either way there is nothing to check. Yay!
|
// borrows.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ XXX --- much more needed, don't have time to write this all up now
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use middle::borrowck::{Loan, bckerr, bckres, borrowck_ctxt, err_mutbl};
|
use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
|
||||||
use middle::borrowck::{err_out_of_scope};
|
use middle::borrowck::{err_out_of_scope};
|
||||||
use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp};
|
use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp};
|
||||||
use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self};
|
use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self};
|
||||||
|
@ -57,8 +57,9 @@ use core::result::{Err, Ok, Result};
|
||||||
use syntax::ast::{m_const, m_imm, m_mutbl};
|
use syntax::ast::{m_const, m_imm, m_mutbl};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
impl borrowck_ctxt {
|
impl BorrowckCtxt {
|
||||||
fn loan(cmt: cmt,
|
fn loan(&self,
|
||||||
|
cmt: cmt,
|
||||||
scope_region: ty::Region,
|
scope_region: ty::Region,
|
||||||
mutbl: ast::mutability) -> bckres<~[Loan]> {
|
mutbl: ast::mutability) -> bckres<~[Loan]> {
|
||||||
let lc = LoanContext {
|
let lc = LoanContext {
|
||||||
|
@ -77,7 +78,7 @@ impl borrowck_ctxt {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LoanContext {
|
struct LoanContext {
|
||||||
bccx: borrowck_ctxt,
|
bccx: &BorrowckCtxt,
|
||||||
|
|
||||||
// the region scope for which we must preserve the memory
|
// the region scope for which we must preserve the memory
|
||||||
scope_region: ty::Region,
|
scope_region: ty::Region,
|
||||||
|
|
|
@ -230,6 +230,7 @@ use middle::liveness;
|
||||||
use middle::mem_categorization::*;
|
use middle::mem_categorization::*;
|
||||||
use middle::region;
|
use middle::region;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
|
use middle::moves;
|
||||||
use util::common::{indenter, stmt_set};
|
use util::common::{indenter, stmt_set};
|
||||||
use util::ppaux::{expr_repr, note_and_explain_region};
|
use util::ppaux::{expr_repr, note_and_explain_region};
|
||||||
use util::ppaux::{ty_to_str, region_to_str, explain_region};
|
use util::ppaux::{ty_to_str, region_to_str, explain_region};
|
||||||
|
@ -254,15 +255,17 @@ pub mod gather_loans;
|
||||||
pub mod loan;
|
pub mod loan;
|
||||||
pub mod preserve;
|
pub mod preserve;
|
||||||
|
|
||||||
pub fn check_crate(tcx: ty::ctxt,
|
pub fn check_crate(
|
||||||
method_map: typeck::method_map,
|
tcx: ty::ctxt,
|
||||||
last_use_map: liveness::last_use_map,
|
method_map: typeck::method_map,
|
||||||
crate: @ast::crate)
|
moves_map: moves::MovesMap,
|
||||||
-> (root_map, mutbl_map, write_guard_map) {
|
capture_map: moves::CaptureMap,
|
||||||
|
crate: @ast::crate) -> (root_map, mutbl_map, write_guard_map)
|
||||||
let bccx = borrowck_ctxt_(@{tcx: tcx,
|
{
|
||||||
|
let bccx = @BorrowckCtxt {tcx: tcx,
|
||||||
method_map: method_map,
|
method_map: method_map,
|
||||||
last_use_map: last_use_map,
|
moves_map: moves_map,
|
||||||
|
capture_map: capture_map,
|
||||||
root_map: root_map(),
|
root_map: root_map(),
|
||||||
mutbl_map: HashMap(),
|
mutbl_map: HashMap(),
|
||||||
write_guard_map: HashMap(),
|
write_guard_map: HashMap(),
|
||||||
|
@ -271,7 +274,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
mut loaned_paths_imm: 0,
|
mut loaned_paths_imm: 0,
|
||||||
mut stable_paths: 0,
|
mut stable_paths: 0,
|
||||||
mut req_pure_paths: 0,
|
mut req_pure_paths: 0,
|
||||||
mut guaranteed_paths: 0});
|
mut guaranteed_paths: 0};
|
||||||
|
|
||||||
let req_maps = gather_loans::gather_loans(bccx, crate);
|
let req_maps = gather_loans::gather_loans(bccx, crate);
|
||||||
check_loans::check_loans(bccx, req_maps, crate);
|
check_loans::check_loans(bccx, req_maps, crate);
|
||||||
|
@ -292,7 +295,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
|
|
||||||
return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
|
return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
|
||||||
|
|
||||||
fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str {
|
fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str {
|
||||||
let stat_f = stat as float;
|
let stat_f = stat as float;
|
||||||
let total = bccx.guaranteed_paths as float;
|
let total = bccx.guaranteed_paths as float;
|
||||||
fmt!("%u (%.0f%%)", stat , stat_f * 100f / total)
|
fmt!("%u (%.0f%%)", stat , stat_f * 100f / total)
|
||||||
|
@ -302,23 +305,22 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Type definitions
|
// Type definitions
|
||||||
|
|
||||||
pub type borrowck_ctxt_ = {tcx: ty::ctxt,
|
pub struct BorrowckCtxt {
|
||||||
method_map: typeck::method_map,
|
tcx: ty::ctxt,
|
||||||
last_use_map: liveness::last_use_map,
|
method_map: typeck::method_map,
|
||||||
root_map: root_map,
|
moves_map: moves::MovesMap,
|
||||||
mutbl_map: mutbl_map,
|
capture_map: moves::CaptureMap,
|
||||||
write_guard_map: write_guard_map,
|
root_map: root_map,
|
||||||
stmt_map: stmt_set,
|
mutbl_map: mutbl_map,
|
||||||
|
write_guard_map: write_guard_map,
|
||||||
|
stmt_map: stmt_set,
|
||||||
|
|
||||||
// Statistics:
|
// Statistics:
|
||||||
mut loaned_paths_same: uint,
|
mut loaned_paths_same: uint,
|
||||||
mut loaned_paths_imm: uint,
|
mut loaned_paths_imm: uint,
|
||||||
mut stable_paths: uint,
|
mut stable_paths: uint,
|
||||||
mut req_pure_paths: uint,
|
mut req_pure_paths: uint,
|
||||||
mut guaranteed_paths: uint};
|
mut guaranteed_paths: uint
|
||||||
|
|
||||||
pub enum borrowck_ctxt {
|
|
||||||
borrowck_ctxt_(@borrowck_ctxt_)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RootInfo {
|
pub struct RootInfo {
|
||||||
|
@ -371,6 +373,12 @@ pub struct bckerr {
|
||||||
code: bckerr_code
|
code: bckerr_code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum MoveError {
|
||||||
|
MoveOk,
|
||||||
|
MoveFromIllegalCmt(cmt),
|
||||||
|
MoveWhileBorrowed(/*move*/ cmt, /*loan*/ cmt)
|
||||||
|
}
|
||||||
|
|
||||||
// shorthand for something that fails with `bckerr` or succeeds with `T`
|
// shorthand for something that fails with `bckerr` or succeeds with `T`
|
||||||
pub type bckres<T> = Result<T, bckerr>;
|
pub type bckres<T> = Result<T, bckerr>;
|
||||||
|
|
||||||
|
@ -411,60 +419,62 @@ pub fn root_map() -> root_map {
|
||||||
// ___________________________________________________________________________
|
// ___________________________________________________________________________
|
||||||
// Misc
|
// Misc
|
||||||
|
|
||||||
pub impl borrowck_ctxt {
|
pub impl BorrowckCtxt {
|
||||||
fn is_subregion_of(r_sub: ty::Region, r_sup: ty::Region) -> bool {
|
fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) -> bool {
|
||||||
region::is_subregion_of(self.tcx.region_map, r_sub, r_sup)
|
region::is_subregion_of(self.tcx.region_map, r_sub, r_sup)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_expr(expr: @ast::expr) -> cmt {
|
fn cat_expr(&self, expr: @ast::expr) -> cmt {
|
||||||
cat_expr(self.tcx, self.method_map, expr)
|
cat_expr(self.tcx, self.method_map, expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
|
fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt {
|
||||||
cat_expr_unadjusted(self.tcx, self.method_map, expr)
|
cat_expr_unadjusted(self.tcx, self.method_map, expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_expr_autoderefd(expr: @ast::expr,
|
fn cat_expr_autoderefd(&self, expr: @ast::expr,
|
||||||
adj: @ty::AutoAdjustment)
|
adj: @ty::AutoAdjustment)
|
||||||
-> cmt {
|
-> cmt {
|
||||||
cat_expr_autoderefd(self.tcx, self.method_map, expr, adj)
|
cat_expr_autoderefd(self.tcx, self.method_map, expr, adj)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_def(id: ast::node_id,
|
fn cat_def(&self,
|
||||||
|
id: ast::node_id,
|
||||||
span: span,
|
span: span,
|
||||||
ty: ty::t,
|
ty: ty::t,
|
||||||
def: ast::def) -> cmt {
|
def: ast::def) -> cmt {
|
||||||
cat_def(self.tcx, self.method_map, id, span, ty, def)
|
cat_def(self.tcx, self.method_map, id, span, ty, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_variant<N: ast_node>(arg: N,
|
fn cat_variant<N: ast_node>(&self,
|
||||||
|
arg: N,
|
||||||
enum_did: ast::def_id,
|
enum_did: ast::def_id,
|
||||||
cmt: cmt) -> cmt {
|
cmt: cmt) -> cmt {
|
||||||
cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
|
cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_discr(cmt: cmt, match_id: ast::node_id) -> cmt {
|
fn cat_discr(&self, cmt: cmt, match_id: ast::node_id) -> cmt {
|
||||||
return @cmt_ { cat: cat_discr(cmt, match_id),.. *cmt };
|
return @cmt_ {cat:cat_discr(cmt, match_id),.. *cmt};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mc_ctxt() -> mem_categorization_ctxt {
|
fn mc_ctxt(&self) -> mem_categorization_ctxt {
|
||||||
mem_categorization_ctxt {tcx: self.tcx,
|
mem_categorization_ctxt {tcx: self.tcx,
|
||||||
method_map: self.method_map}
|
method_map: self.method_map}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
|
fn cat_pattern(&self, cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
|
||||||
let mc = self.mc_ctxt();
|
let mc = self.mc_ctxt();
|
||||||
mc.cat_pattern(cmt, pat, op);
|
mc.cat_pattern(cmt, pat, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_if_err(bres: bckres<()>) {
|
fn report_if_err(&self, bres: bckres<()>) {
|
||||||
match bres {
|
match bres {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(ref e) => self.report((*e))
|
Err(ref e) => self.report((*e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report(err: bckerr) {
|
fn report(&self, err: bckerr) {
|
||||||
self.span_err(
|
self.span_err(
|
||||||
err.cmt.span,
|
err.cmt.span,
|
||||||
fmt!("illegal borrow: %s",
|
fmt!("illegal borrow: %s",
|
||||||
|
@ -472,15 +482,15 @@ pub impl borrowck_ctxt {
|
||||||
self.note_and_explain_bckerr(err);
|
self.note_and_explain_bckerr(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_err(s: span, +m: ~str) {
|
fn span_err(&self, s: span, +m: ~str) {
|
||||||
self.tcx.sess.span_err(s, m);
|
self.tcx.sess.span_err(s, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_note(s: span, +m: ~str) {
|
fn span_note(&self, s: span, +m: ~str) {
|
||||||
self.tcx.sess.span_note(s, m);
|
self.tcx.sess.span_note(s, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_to_mutbl_map(cmt: cmt) {
|
fn add_to_mutbl_map(&self, cmt: cmt) {
|
||||||
match cmt.cat {
|
match cmt.cat {
|
||||||
cat_local(id) | cat_arg(id) => {
|
cat_local(id) | cat_arg(id) => {
|
||||||
self.mutbl_map.insert(id, ());
|
self.mutbl_map.insert(id, ());
|
||||||
|
@ -492,7 +502,7 @@ pub impl borrowck_ctxt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bckerr_to_str(err: bckerr) -> ~str {
|
fn bckerr_to_str(&self, err: bckerr) -> ~str {
|
||||||
match err.code {
|
match err.code {
|
||||||
err_mutbl(req) => {
|
err_mutbl(req) => {
|
||||||
fmt!("creating %s alias to %s",
|
fmt!("creating %s alias to %s",
|
||||||
|
@ -520,7 +530,7 @@ pub impl borrowck_ctxt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn note_and_explain_bckerr(err: bckerr) {
|
fn note_and_explain_bckerr(&self, err: bckerr) {
|
||||||
let code = err.code;
|
let code = err.code;
|
||||||
match code {
|
match code {
|
||||||
err_mutbl(*) | err_mut_uniq | err_mut_variant |
|
err_mutbl(*) | err_mut_uniq | err_mut_variant |
|
||||||
|
@ -555,25 +565,25 @@ pub impl borrowck_ctxt {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn cmt_to_str(cmt: cmt) -> ~str {
|
fn cmt_to_str(&self, cmt: cmt) -> ~str {
|
||||||
let mc = &mem_categorization_ctxt {tcx: self.tcx,
|
let mc = &mem_categorization_ctxt {tcx: self.tcx,
|
||||||
method_map: self.method_map};
|
method_map: self.method_map};
|
||||||
mc.cmt_to_str(cmt)
|
mc.cmt_to_str(cmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmt_to_repr(cmt: cmt) -> ~str {
|
fn cmt_to_repr(&self, cmt: cmt) -> ~str {
|
||||||
let mc = &mem_categorization_ctxt {tcx: self.tcx,
|
let mc = &mem_categorization_ctxt {tcx: self.tcx,
|
||||||
method_map: self.method_map};
|
method_map: self.method_map};
|
||||||
mc.cmt_to_repr(cmt)
|
mc.cmt_to_repr(cmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mut_to_str(mutbl: ast::mutability) -> ~str {
|
fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
|
||||||
let mc = &mem_categorization_ctxt {tcx: self.tcx,
|
let mc = &mem_categorization_ctxt {tcx: self.tcx,
|
||||||
method_map: self.method_map};
|
method_map: self.method_map};
|
||||||
mc.mut_to_str(mutbl)
|
mc.mut_to_str(mutbl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loan_to_repr(loan: &Loan) -> ~str {
|
fn loan_to_repr(&self, loan: &Loan) -> ~str {
|
||||||
fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
|
fmt!("Loan(lp=%?, cmt=%s, mutbl=%?)",
|
||||||
loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
|
loan.lp, self.cmt_to_repr(loan.cmt), loan.mutbl)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, borrowck_ctxt};
|
use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, BorrowckCtxt};
|
||||||
use middle::borrowck::{err_mut_uniq, err_mut_variant};
|
use middle::borrowck::{err_mut_uniq, err_mut_variant};
|
||||||
use middle::borrowck::{err_out_of_root_scope, err_out_of_scope};
|
use middle::borrowck::{err_out_of_root_scope, err_out_of_scope};
|
||||||
use middle::borrowck::{err_root_not_permitted, root_map_key};
|
use middle::borrowck::{err_root_not_permitted, root_map_key};
|
||||||
|
@ -30,40 +30,42 @@ use util::common::indenter;
|
||||||
use syntax::ast::{m_const, m_imm, m_mutbl};
|
use syntax::ast::{m_const, m_imm, m_mutbl};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
pub enum preserve_condition {
|
pub enum PreserveCondition {
|
||||||
pc_ok,
|
PcOk,
|
||||||
pc_if_pure(bckerr)
|
PcIfPure(bckerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl preserve_condition {
|
impl PreserveCondition {
|
||||||
// combines two preservation conditions such that if either of
|
// combines two preservation conditions such that if either of
|
||||||
// them requires purity, the result requires purity
|
// them requires purity, the result requires purity
|
||||||
fn combine(pc: preserve_condition) -> preserve_condition {
|
fn combine(&self, pc: PreserveCondition) -> PreserveCondition {
|
||||||
match self {
|
match *self {
|
||||||
pc_ok => {pc}
|
PcOk => {pc}
|
||||||
pc_if_pure(_) => {self}
|
PcIfPure(_) => {*self}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl borrowck_ctxt {
|
impl BorrowckCtxt {
|
||||||
fn preserve(cmt: cmt,
|
fn preserve(&self,
|
||||||
|
cmt: cmt,
|
||||||
scope_region: ty::Region,
|
scope_region: ty::Region,
|
||||||
item_ub: ast::node_id,
|
item_ub: ast::node_id,
|
||||||
root_ub: ast::node_id)
|
root_ub: ast::node_id) -> bckres<PreserveCondition>
|
||||||
-> bckres<preserve_condition> {
|
{
|
||||||
|
let ctxt = PreserveCtxt {
|
||||||
let ctxt = preserve_ctxt({bccx: self,
|
bccx: self,
|
||||||
scope_region: scope_region,
|
scope_region: scope_region,
|
||||||
item_ub: item_ub,
|
item_ub: item_ub,
|
||||||
root_ub: root_ub,
|
root_ub: root_ub,
|
||||||
root_managed_data: true});
|
root_managed_data: true
|
||||||
(&ctxt).preserve(cmt)
|
};
|
||||||
|
ctxt.preserve(cmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum preserve_ctxt = {
|
struct PreserveCtxt {
|
||||||
bccx: borrowck_ctxt,
|
bccx: &BorrowckCtxt,
|
||||||
|
|
||||||
// the region scope for which we must preserve the memory
|
// the region scope for which we must preserve the memory
|
||||||
scope_region: ty::Region,
|
scope_region: ty::Region,
|
||||||
|
@ -76,13 +78,12 @@ enum preserve_ctxt = {
|
||||||
|
|
||||||
// if false, do not attempt to root managed data
|
// if false, do not attempt to root managed data
|
||||||
root_managed_data: bool
|
root_managed_data: bool
|
||||||
};
|
}
|
||||||
|
|
||||||
|
impl PreserveCtxt {
|
||||||
|
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
|
||||||
|
|
||||||
priv impl &preserve_ctxt {
|
fn preserve(&self, cmt: cmt) -> bckres<PreserveCondition> {
|
||||||
fn tcx() -> ty::ctxt { self.bccx.tcx }
|
|
||||||
|
|
||||||
fn preserve(cmt: cmt) -> bckres<preserve_condition> {
|
|
||||||
debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)",
|
debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)",
|
||||||
self.bccx.cmt_to_repr(cmt), self.root_ub,
|
self.bccx.cmt_to_repr(cmt), self.root_ub,
|
||||||
self.root_managed_data);
|
self.root_managed_data);
|
||||||
|
@ -94,7 +95,7 @@ priv impl &preserve_ctxt {
|
||||||
self.compare_scope(cmt, ty::re_scope(self.item_ub))
|
self.compare_scope(cmt, ty::re_scope(self.item_ub))
|
||||||
}
|
}
|
||||||
cat_special(sk_static_item) | cat_special(sk_method) => {
|
cat_special(sk_static_item) | cat_special(sk_method) => {
|
||||||
Ok(pc_ok)
|
Ok(PcOk)
|
||||||
}
|
}
|
||||||
cat_rvalue => {
|
cat_rvalue => {
|
||||||
// when we borrow an rvalue, we can keep it rooted but only
|
// when we borrow an rvalue, we can keep it rooted but only
|
||||||
|
@ -181,7 +182,7 @@ priv impl &preserve_ctxt {
|
||||||
}
|
}
|
||||||
cat_deref(_, _, unsafe_ptr) => {
|
cat_deref(_, _, unsafe_ptr) => {
|
||||||
// Unsafe pointers are the user's problem
|
// Unsafe pointers are the user's problem
|
||||||
Ok(pc_ok)
|
Ok(PcOk)
|
||||||
}
|
}
|
||||||
cat_deref(base, derefs, gc_ptr(*)) => {
|
cat_deref(base, derefs, gc_ptr(*)) => {
|
||||||
// GC'd pointers of type @MT: if this pointer lives in
|
// GC'd pointers of type @MT: if this pointer lives in
|
||||||
|
@ -193,13 +194,15 @@ priv impl &preserve_ctxt {
|
||||||
if cmt.cat.derefs_through_mutable_box() {
|
if cmt.cat.derefs_through_mutable_box() {
|
||||||
self.attempt_root(cmt, base, derefs)
|
self.attempt_root(cmt, base, derefs)
|
||||||
} else if base.mutbl == m_imm {
|
} else if base.mutbl == m_imm {
|
||||||
let non_rooting_ctxt =
|
let non_rooting_ctxt = PreserveCtxt {
|
||||||
preserve_ctxt({root_managed_data: false,.. **self});
|
root_managed_data: false,
|
||||||
match (&non_rooting_ctxt).preserve(base) {
|
..*self
|
||||||
Ok(pc_ok) => {
|
};
|
||||||
Ok(pc_ok)
|
match non_rooting_ctxt.preserve(base) {
|
||||||
|
Ok(PcOk) => {
|
||||||
|
Ok(PcOk)
|
||||||
}
|
}
|
||||||
Ok(pc_if_pure(_)) => {
|
Ok(PcIfPure(_)) => {
|
||||||
debug!("must root @T, otherwise purity req'd");
|
debug!("must root @T, otherwise purity req'd");
|
||||||
self.attempt_root(cmt, base, derefs)
|
self.attempt_root(cmt, base, derefs)
|
||||||
}
|
}
|
||||||
|
@ -267,10 +270,11 @@ priv impl &preserve_ctxt {
|
||||||
// node appears to draw the line between what will be rooted
|
// node appears to draw the line between what will be rooted
|
||||||
// in the *arm* vs the *match*.
|
// in the *arm* vs the *match*.
|
||||||
|
|
||||||
let match_rooting_ctxt =
|
let match_rooting_ctxt = PreserveCtxt {
|
||||||
preserve_ctxt({scope_region: ty::re_scope(match_id),
|
scope_region: ty::re_scope(match_id),
|
||||||
.. **self});
|
..*self
|
||||||
(&match_rooting_ctxt).preserve(base)
|
};
|
||||||
|
match_rooting_ctxt.preserve(base)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,28 +283,29 @@ priv impl &preserve_ctxt {
|
||||||
/// `base`) be found in an immutable location (that is, `base`
|
/// `base`) be found in an immutable location (that is, `base`
|
||||||
/// must be immutable). Also requires that `base` itself is
|
/// must be immutable). Also requires that `base` itself is
|
||||||
/// preserved.
|
/// preserved.
|
||||||
fn require_imm(cmt: cmt,
|
fn require_imm(&self,
|
||||||
|
cmt: cmt,
|
||||||
cmt_base: cmt,
|
cmt_base: cmt,
|
||||||
code: bckerr_code) -> bckres<preserve_condition> {
|
code: bckerr_code) -> bckres<PreserveCondition> {
|
||||||
// Variant contents and unique pointers: must be immutably
|
// Variant contents and unique pointers: must be immutably
|
||||||
// rooted to a preserved address.
|
// rooted to a preserved address.
|
||||||
match self.preserve(cmt_base) {
|
match self.preserve(cmt_base) {
|
||||||
// the base is preserved, but if we are not mutable then
|
// the base is preserved, but if we are not mutable then
|
||||||
// purity is required
|
// purity is required
|
||||||
Ok(pc_ok) => {
|
Ok(PcOk) => {
|
||||||
match cmt_base.mutbl {
|
match cmt_base.mutbl {
|
||||||
m_mutbl | m_const => {
|
m_mutbl | m_const => {
|
||||||
Ok(pc_if_pure(bckerr { cmt: cmt, code: code }))
|
Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
|
||||||
}
|
}
|
||||||
m_imm => {
|
m_imm => {
|
||||||
Ok(pc_ok)
|
Ok(PcOk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the base requires purity too, that's fine
|
// the base requires purity too, that's fine
|
||||||
Ok(pc_if_pure(ref e)) => {
|
Ok(PcIfPure(ref e)) => {
|
||||||
Ok(pc_if_pure((*e)))
|
Ok(PcIfPure((*e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// base is not stable, doesn't matter
|
// base is not stable, doesn't matter
|
||||||
|
@ -312,10 +317,11 @@ priv impl &preserve_ctxt {
|
||||||
|
|
||||||
/// Checks that the scope for which the value must be preserved
|
/// Checks that the scope for which the value must be preserved
|
||||||
/// is a subscope of `scope_ub`; if so, success.
|
/// is a subscope of `scope_ub`; if so, success.
|
||||||
fn compare_scope(cmt: cmt,
|
fn compare_scope(&self,
|
||||||
scope_ub: ty::Region) -> bckres<preserve_condition> {
|
cmt: cmt,
|
||||||
|
scope_ub: ty::Region) -> bckres<PreserveCondition> {
|
||||||
if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
|
if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
|
||||||
Ok(pc_ok)
|
Ok(PcOk)
|
||||||
} else {
|
} else {
|
||||||
Err(bckerr {
|
Err(bckerr {
|
||||||
cmt:cmt,
|
cmt:cmt,
|
||||||
|
@ -333,10 +339,8 @@ priv impl &preserve_ctxt {
|
||||||
/// value live for longer than the current fn or else potentially
|
/// value live for longer than the current fn or else potentially
|
||||||
/// require that an statically unbounded number of values be
|
/// require that an statically unbounded number of values be
|
||||||
/// rooted (if a loop exists).
|
/// rooted (if a loop exists).
|
||||||
fn attempt_root(cmt: cmt,
|
fn attempt_root(&self, cmt: cmt, base: cmt,
|
||||||
base: cmt,
|
derefs: uint) -> bckres<PreserveCondition> {
|
||||||
derefs: uint)
|
|
||||||
-> bckres<preserve_condition> {
|
|
||||||
if !self.root_managed_data {
|
if !self.root_managed_data {
|
||||||
// normally, there is a root_ub; the only time that this
|
// normally, there is a root_ub; the only time that this
|
||||||
// is none is when a boxed value is stored in an immutable
|
// is none is when a boxed value is stored in an immutable
|
||||||
|
@ -387,7 +391,7 @@ priv impl &preserve_ctxt {
|
||||||
scope: scope_to_use,
|
scope: scope_to_use,
|
||||||
freezes: cmt.cat.derefs_through_mutable_box()
|
freezes: cmt.cat.derefs_through_mutable_box()
|
||||||
});
|
});
|
||||||
return Ok(pc_ok);
|
return Ok(PcOk);
|
||||||
} else {
|
} else {
|
||||||
debug!("Unable to root");
|
debug!("Unable to root");
|
||||||
return Err(bckerr {
|
return Err(bckerr {
|
||||||
|
|
|
@ -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, _) => {
|
expr_loop(ref b, _) => {
|
||||||
(v.visit_block)((*b), {in_loop: true,.. cx}, v);
|
(v.visit_block)((*b), {in_loop: true,.. cx}, v);
|
||||||
}
|
}
|
||||||
expr_fn(_, _, _, _) => {
|
expr_fn(_, _, _) => {
|
||||||
visit::visit_expr(e, {in_loop: false, can_ret: true}, v);
|
visit::visit_expr(e, {in_loop: false, can_ret: true}, v);
|
||||||
}
|
}
|
||||||
expr_fn_block(_, ref b, _) => {
|
expr_fn_block(_, ref b) => {
|
||||||
(v.visit_block)((*b), {in_loop: false, can_ret: false}, v);
|
(v.visit_block)((*b), {in_loop: false, can_ret: false}, v);
|
||||||
}
|
}
|
||||||
expr_loop_body(@expr {node: expr_fn_block(_, ref b, _), _}) => {
|
expr_loop_body(@expr {node: expr_fn_block(_, ref b), _}) => {
|
||||||
let proto = ty::ty_fn_proto(ty::expr_ty(tcx, e));
|
let proto = ty::ty_fn_proto(ty::expr_ty(tcx, e));
|
||||||
let blk = (proto == ProtoBorrowed);
|
let blk = (proto == ProtoBorrowed);
|
||||||
(v.visit_block)((*b), {in_loop: true, can_ret: blk}, v);
|
(v.visit_block)((*b), {in_loop: true, can_ret: blk}, v);
|
||||||
|
|
|
@ -16,6 +16,7 @@ use middle::pat_util::*;
|
||||||
use middle::ty::*;
|
use middle::ty::*;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::method_map;
|
use middle::typeck::method_map;
|
||||||
|
use middle::moves;
|
||||||
use util::ppaux::ty_to_str;
|
use util::ppaux::ty_to_str;
|
||||||
|
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
|
@ -34,10 +35,16 @@ use syntax::visit;
|
||||||
pub struct MatchCheckCtxt {
|
pub struct MatchCheckCtxt {
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
method_map: method_map,
|
method_map: method_map,
|
||||||
|
moves_map: moves::MovesMap
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_crate(tcx: ty::ctxt, method_map: method_map, crate: @crate) {
|
pub fn check_crate(tcx: ty::ctxt,
|
||||||
let cx = @MatchCheckCtxt { tcx: tcx, method_map: method_map };
|
method_map: method_map,
|
||||||
|
moves_map: moves::MovesMap,
|
||||||
|
crate: @crate) {
|
||||||
|
let cx = @MatchCheckCtxt {tcx: tcx,
|
||||||
|
method_map: method_map,
|
||||||
|
moves_map: moves_map};
|
||||||
visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor {
|
visit::visit_crate(*crate, (), visit::mk_vt(@visit::Visitor {
|
||||||
visit_expr: |a,b,c| check_expr(cx, a, b, c),
|
visit_expr: |a,b,c| check_expr(cx, a, b, c),
|
||||||
visit_local: |a,b,c| check_local(cx, a, b, c),
|
visit_local: |a,b,c| check_local(cx, a, b, c),
|
||||||
|
@ -53,13 +60,7 @@ pub fn expr_is_non_moving_lvalue(cx: @MatchCheckCtxt, expr: @expr) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match cx.tcx.value_modes.find(expr.id) {
|
!cx.moves_map.contains_key(expr.id)
|
||||||
Some(MoveValue) => return false,
|
|
||||||
Some(CopyValue) | Some(ReadValue) => return true,
|
|
||||||
None => {
|
|
||||||
cx.tcx.sess.span_bug(expr.span, ~"no entry in value mode map");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_expr(cx: @MatchCheckCtxt, ex: @expr, &&s: (), v: visit::vt<()>) {
|
pub fn check_expr(cx: @MatchCheckCtxt, ex: @expr, &&s: (), v: visit::vt<()>) {
|
||||||
|
@ -113,7 +114,7 @@ pub fn check_arms(cx: @MatchCheckCtxt, arms: ~[arm]) {
|
||||||
for arms.each |arm| {
|
for arms.each |arm| {
|
||||||
for arm.pats.each |pat| {
|
for arm.pats.each |pat| {
|
||||||
let v = ~[*pat];
|
let v = ~[*pat];
|
||||||
match is_useful(cx, seen, v) {
|
match is_useful(cx, copy seen, v) {
|
||||||
not_useful => {
|
not_useful => {
|
||||||
cx.tcx.sess.span_err(pat.span, ~"unreachable pattern");
|
cx.tcx.sess.span_err(pat.span, ~"unreachable pattern");
|
||||||
}
|
}
|
||||||
|
@ -197,7 +198,7 @@ pub enum ctor {
|
||||||
|
|
||||||
// Note: is_useful doesn't work on empty types, as the paper notes.
|
// Note: is_useful doesn't work on empty types, as the paper notes.
|
||||||
// So it assumes that v is non-empty.
|
// So it assumes that v is non-empty.
|
||||||
pub fn is_useful(cx: @MatchCheckCtxt, +m: matrix, +v: ~[@pat]) -> useful {
|
pub fn is_useful(cx: @MatchCheckCtxt, +m: matrix, +v: &[@pat]) -> useful {
|
||||||
if m.len() == 0u { return useful_; }
|
if m.len() == 0u { return useful_; }
|
||||||
if m[0].len() == 0u { return not_useful; }
|
if m[0].len() == 0u { return not_useful; }
|
||||||
let real_pat = match vec::find(m, |r| r[0].id != 0) {
|
let real_pat = match vec::find(m, |r| r[0].id != 0) {
|
||||||
|
@ -272,12 +273,12 @@ pub fn is_useful(cx: @MatchCheckCtxt, +m: matrix, +v: ~[@pat]) -> useful {
|
||||||
|
|
||||||
pub fn is_useful_specialized(cx: @MatchCheckCtxt,
|
pub fn is_useful_specialized(cx: @MatchCheckCtxt,
|
||||||
m: matrix,
|
m: matrix,
|
||||||
+v: ~[@pat],
|
+v: &[@pat],
|
||||||
+ctor: ctor,
|
+ctor: ctor,
|
||||||
arity: uint,
|
arity: uint,
|
||||||
lty: ty::t)
|
lty: ty::t)
|
||||||
-> useful {
|
-> useful {
|
||||||
let ms = vec::filter_map(m, |r| specialize(cx, copy *r,
|
let ms = vec::filter_map(m, |r| specialize(cx, *r,
|
||||||
ctor, arity, lty));
|
ctor, arity, lty));
|
||||||
let could_be_useful = is_useful(
|
let could_be_useful = is_useful(
|
||||||
cx, ms, specialize(cx, v, ctor, arity, lty).get());
|
cx, ms, specialize(cx, v, ctor, arity, lty).get());
|
||||||
|
@ -467,7 +468,7 @@ pub fn wild() -> @pat {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn specialize(cx: @MatchCheckCtxt,
|
pub fn specialize(cx: @MatchCheckCtxt,
|
||||||
+r: ~[@pat],
|
+r: &[@pat],
|
||||||
ctor_id: ctor,
|
ctor_id: ctor,
|
||||||
arity: uint,
|
arity: uint,
|
||||||
left_ty: ty::t)
|
left_ty: ty::t)
|
||||||
|
@ -729,21 +730,13 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
|
||||||
for pats.each |pat| {
|
for pats.each |pat| {
|
||||||
do pat_bindings(def_map, *pat) |bm, id, span, _path| {
|
do pat_bindings(def_map, *pat) |bm, id, span, _path| {
|
||||||
match bm {
|
match bm {
|
||||||
|
bind_by_copy => {}
|
||||||
bind_by_ref(_) => {
|
bind_by_ref(_) => {
|
||||||
by_ref_span = Some(span);
|
by_ref_span = Some(span);
|
||||||
}
|
}
|
||||||
bind_by_move => {
|
|
||||||
any_by_move = true;
|
|
||||||
}
|
|
||||||
bind_by_value => {}
|
|
||||||
bind_infer => {
|
bind_infer => {
|
||||||
match cx.tcx.value_modes.find(id) {
|
if cx.moves_map.contains_key(id) {
|
||||||
Some(MoveValue) => any_by_move = true,
|
any_by_move = true;
|
||||||
Some(CopyValue) | Some(ReadValue) => {}
|
|
||||||
None => {
|
|
||||||
cx.tcx.sess.span_bug(span, ~"no mode for pat \
|
|
||||||
binding");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -781,18 +774,18 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
|
||||||
do walk_pat(*pat) |p| {
|
do walk_pat(*pat) |p| {
|
||||||
if pat_is_binding(def_map, p) {
|
if pat_is_binding(def_map, p) {
|
||||||
match p.node {
|
match p.node {
|
||||||
pat_ident(bind_by_move, _, sub) => check_move(p, sub),
|
pat_ident(_, _, sub) => {
|
||||||
pat_ident(bind_infer, _, sub) => {
|
if cx.moves_map.contains_key(p.id) {
|
||||||
match tcx.value_modes.find(p.id) {
|
check_move(p, sub);
|
||||||
Some(MoveValue) => check_move(p, sub),
|
|
||||||
Some(CopyValue) | Some(ReadValue) => {}
|
|
||||||
None => {
|
|
||||||
cx.tcx.sess.span_bug(
|
|
||||||
pat.span, ~"no mode for pat binding");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
p.span,
|
||||||
|
fmt!("Binding pattern %d is \
|
||||||
|
not an identifier: %?",
|
||||||
|
p.id, p.node));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -800,32 +793,23 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
|
||||||
// Now check to ensure that any move binding is not behind an @ or &.
|
// Now check to ensure that any move binding is not behind an @ or &.
|
||||||
// This is always illegal.
|
// This is always illegal.
|
||||||
let vt = visit::mk_vt(@visit::Visitor {
|
let vt = visit::mk_vt(@visit::Visitor {
|
||||||
visit_pat: |pat, behind_bad_pointer, v| {
|
visit_pat: |pat, behind_bad_pointer: bool, v| {
|
||||||
let error_out = || {
|
|
||||||
cx.tcx.sess.span_err(pat.span, ~"by-move pattern \
|
|
||||||
bindings may not occur \
|
|
||||||
behind @ or & bindings");
|
|
||||||
};
|
|
||||||
match pat.node {
|
match pat.node {
|
||||||
pat_ident(binding_mode, _, sub) => {
|
pat_ident(_, _, sub) => {
|
||||||
debug!("(check legality of move) checking pat \
|
debug!("(check legality of move) checking pat \
|
||||||
ident with behind_bad_pointer %?",
|
ident with behind_bad_pointer %?",
|
||||||
behind_bad_pointer);
|
behind_bad_pointer);
|
||||||
match binding_mode {
|
|
||||||
bind_by_move if behind_bad_pointer => error_out(),
|
if behind_bad_pointer &&
|
||||||
bind_infer if behind_bad_pointer => {
|
cx.moves_map.contains_key(pat.id)
|
||||||
match cx.tcx.value_modes.find(pat.id) {
|
{
|
||||||
Some(MoveValue) => error_out(),
|
cx.tcx.sess.span_err(
|
||||||
Some(CopyValue) |
|
pat.span,
|
||||||
Some(ReadValue) => {}
|
~"by-move pattern \
|
||||||
None => {
|
bindings may not occur \
|
||||||
cx.tcx.sess.span_bug(pat.span,
|
behind @ or & bindings");
|
||||||
~"no mode for pat binding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match sub {
|
match sub {
|
||||||
None => {}
|
None => {}
|
||||||
Some(subpat) => {
|
Some(subpat) => {
|
||||||
|
@ -833,9 +817,11 @@ pub fn check_legality_of_move_bindings(cx: @MatchCheckCtxt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pat_box(subpat) | pat_region(subpat) => {
|
pat_box(subpat) | pat_region(subpat) => {
|
||||||
(v.visit_pat)(subpat, true, v);
|
(v.visit_pat)(subpat, true, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => visit::visit_pat(pat, behind_bad_pointer, v)
|
_ => visit::visit_pat(pat, behind_bad_pointer, v)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -413,8 +413,8 @@ pub fn lit_to_const(lit: @lit) -> const_val {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_const_vals(a: const_val, b: const_val) -> int {
|
pub fn compare_const_vals(a: const_val, b: const_val) -> int {
|
||||||
match (a, b) {
|
match (&a, &b) {
|
||||||
(const_int(a), const_int(b)) => {
|
(&const_int(a), &const_int(b)) => {
|
||||||
if a == b {
|
if a == b {
|
||||||
0
|
0
|
||||||
} else if a < b {
|
} else if a < b {
|
||||||
|
@ -423,7 +423,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(const_uint(a), const_uint(b)) => {
|
(&const_uint(a), &const_uint(b)) => {
|
||||||
if a == b {
|
if a == b {
|
||||||
0
|
0
|
||||||
} else if a < b {
|
} else if a < b {
|
||||||
|
@ -432,7 +432,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(const_float(a), const_float(b)) => {
|
(&const_float(a), &const_float(b)) => {
|
||||||
if a == b {
|
if a == b {
|
||||||
0
|
0
|
||||||
} else if a < b {
|
} else if a < b {
|
||||||
|
@ -441,7 +441,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(const_str(ref a), const_str(ref b)) => {
|
(&const_str(ref a), &const_str(ref b)) => {
|
||||||
if (*a) == (*b) {
|
if (*a) == (*b) {
|
||||||
0
|
0
|
||||||
} else if (*a) < (*b) {
|
} else if (*a) < (*b) {
|
||||||
|
@ -450,7 +450,7 @@ pub fn compare_const_vals(a: const_val, b: const_val) -> int {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(const_bool(a), const_bool(b)) => {
|
(&const_bool(a), &const_bool(b)) => {
|
||||||
if a == b {
|
if a == b {
|
||||||
0
|
0
|
||||||
} else if a < b {
|
} else if a < b {
|
||||||
|
|
|
@ -48,7 +48,7 @@ fn collect_freevars(def_map: resolve::DefMap, blk: ast::blk)
|
||||||
|
|
||||||
let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
|
let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
ast::expr_fn(proto, _, _, _) => {
|
ast::expr_fn(proto, _, _) => {
|
||||||
if proto != ast::ProtoBare {
|
if proto != ast::ProtoBare {
|
||||||
visit::visit_expr(expr, depth + 1, v);
|
visit::visit_expr(expr, depth + 1, v);
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,7 @@ pub fn get_freevars(tcx: ty::ctxt, fid: ast::node_id) -> freevar_info {
|
||||||
Some(d) => return d
|
Some(d) => return d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool {
|
pub fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool {
|
||||||
return vec::len(*get_freevars(tcx, fid)) != 0u;
|
return vec::len(*get_freevars(tcx, fid)) != 0u;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ use middle::freevars;
|
||||||
use middle::lint::{non_implicitly_copyable_typarams, implicit_copies};
|
use middle::lint::{non_implicitly_copyable_typarams, implicit_copies};
|
||||||
use middle::liveness;
|
use middle::liveness;
|
||||||
use middle::pat_util;
|
use middle::pat_util;
|
||||||
use middle::ty::{CopyValue, MoveValue, ReadValue};
|
|
||||||
use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const};
|
use middle::ty::{Kind, kind_copyable, kind_noncopyable, kind_const};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck;
|
use middle::typeck;
|
||||||
|
@ -102,8 +101,6 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
let visit = visit::mk_vt(@visit::Visitor {
|
let visit = visit::mk_vt(@visit::Visitor {
|
||||||
visit_arm: check_arm,
|
visit_arm: check_arm,
|
||||||
visit_expr: check_expr,
|
visit_expr: check_expr,
|
||||||
visit_stmt: check_stmt,
|
|
||||||
visit_block: check_block,
|
|
||||||
visit_fn: check_fn,
|
visit_fn: check_fn,
|
||||||
visit_ty: check_ty,
|
visit_ty: check_ty,
|
||||||
visit_item: fn@(i: @item, cx: ctx, v: visit::vt<ctx>) {
|
visit_item: fn@(i: @item, cx: ctx, v: visit::vt<ctx>) {
|
||||||
|
@ -115,75 +112,41 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
tcx.sess.abort_if_errors();
|
tcx.sess.abort_if_errors();
|
||||||
}
|
}
|
||||||
|
|
||||||
// bool flag is only used for checking closures,
|
type check_fn = fn@(ctx, @freevar_entry);
|
||||||
// where it refers to whether a var is 'move' in the
|
|
||||||
// capture clause
|
|
||||||
pub type check_fn = fn@(ctx,
|
|
||||||
node_id,
|
|
||||||
Option<@freevar_entry>,
|
|
||||||
bool,
|
|
||||||
ty::t,
|
|
||||||
sp: span);
|
|
||||||
|
|
||||||
// Yields the appropriate function to check the kind of closed over
|
// Yields the appropriate function to check the kind of closed over
|
||||||
// variables. `id` is the node_id for some expression that creates the
|
// variables. `id` is the node_id for some expression that creates the
|
||||||
// closure.
|
// closure.
|
||||||
pub fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
|
fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
|
||||||
fn check_for_uniq(cx: ctx, id: node_id, fv: Option<@freevar_entry>,
|
fn check_for_uniq(cx: ctx, fv: @freevar_entry) {
|
||||||
is_move: bool, var_t: ty::t, sp: span) {
|
|
||||||
// all captured data must be sendable, regardless of whether it is
|
// all captured data must be sendable, regardless of whether it is
|
||||||
// moved in or copied in. Note that send implies owned.
|
// moved in or copied in. Note that send implies owned.
|
||||||
if !check_send(cx, var_t, sp) { return; }
|
let id = ast_util::def_id_of_def(fv.def).node;
|
||||||
|
let var_t = ty::node_id_to_type(cx.tcx, id);
|
||||||
|
if !check_send(cx, var_t, fv.span) { return; }
|
||||||
|
|
||||||
// copied in data must be copyable, but moved in data can be anything
|
|
||||||
let is_implicit = fv.is_some();
|
|
||||||
if !is_move {
|
|
||||||
check_copy(cx, id, var_t, sp, is_implicit,
|
|
||||||
Some(("non-copyable value cannot be copied into a \
|
|
||||||
~fn closure",
|
|
||||||
"to copy values into a ~fn closure, use a \
|
|
||||||
capture clause: `fn~(copy x)` or `|copy x|`")));
|
|
||||||
}
|
|
||||||
// check that only immutable variables are implicitly copied in
|
// check that only immutable variables are implicitly copied in
|
||||||
for fv.each |fv| {
|
check_imm_free_var(cx, fv.def, fv.span);
|
||||||
check_imm_free_var(cx, fv.def, fv.span);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_box(cx: ctx, id: node_id, fv: Option<@freevar_entry>,
|
fn check_for_box(cx: ctx, fv: @freevar_entry) {
|
||||||
is_move: bool, var_t: ty::t, sp: span) {
|
|
||||||
// all captured data must be owned
|
// all captured data must be owned
|
||||||
if !check_durable(cx.tcx, var_t, sp) { return; }
|
let id = ast_util::def_id_of_def(fv.def).node;
|
||||||
|
let var_t = ty::node_id_to_type(cx.tcx, id);
|
||||||
|
if !check_durable(cx.tcx, var_t, fv.span) { return; }
|
||||||
|
|
||||||
// copied in data must be copyable, but moved in data can be anything
|
|
||||||
let is_implicit = fv.is_some();
|
|
||||||
if !is_move {
|
|
||||||
check_copy(cx, id, var_t, sp, is_implicit,
|
|
||||||
Some(("non-copyable value cannot be copied into a \
|
|
||||||
@fn closure",
|
|
||||||
"to copy values into a @fn closure, use a \
|
|
||||||
capture clause: `fn~(copy x)` or `|copy x|`")));
|
|
||||||
}
|
|
||||||
// check that only immutable variables are implicitly copied in
|
// check that only immutable variables are implicitly copied in
|
||||||
for fv.each |fv| {
|
check_imm_free_var(cx, fv.def, fv.span);
|
||||||
check_imm_free_var(cx, fv.def, fv.span);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_block(cx: ctx, _id: node_id, fv: Option<@freevar_entry>,
|
fn check_for_block(_cx: ctx, _fv: @freevar_entry) {
|
||||||
_is_move: bool, _var_t: ty::t, sp: span) {
|
// no restrictions
|
||||||
// only restriction: no capture clauses (we would have to take
|
|
||||||
// ownership of the moved/copied in data).
|
|
||||||
if fv.is_none() {
|
|
||||||
cx.tcx.sess.span_err(
|
|
||||||
sp,
|
|
||||||
~"cannot capture values explicitly with a block closure");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_bare(cx: ctx, _id: node_id, _fv: Option<@freevar_entry>,
|
fn check_for_bare(cx: ctx, fv: @freevar_entry) {
|
||||||
_is_move: bool, _var_t: ty::t, sp: span) {
|
cx.tcx.sess.span_err(
|
||||||
cx.tcx.sess.span_err(sp, ~"attempted dynamic environment capture");
|
fv.span,
|
||||||
|
~"attempted dynamic environment capture");
|
||||||
}
|
}
|
||||||
|
|
||||||
let fty = ty::node_id_to_type(cx.tcx, id);
|
let fty = ty::node_id_to_type(cx.tcx, id);
|
||||||
|
@ -197,68 +160,26 @@ pub fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
|
||||||
|
|
||||||
// Check that the free variables used in a shared/sendable closure conform
|
// Check that the free variables used in a shared/sendable closure conform
|
||||||
// to the copy/move kind bounds. Then recursively check the function body.
|
// to the copy/move kind bounds. Then recursively check the function body.
|
||||||
pub fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
|
fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
|
||||||
fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {
|
fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {
|
||||||
// Find the check function that enforces the appropriate bounds for this
|
|
||||||
// kind of function:
|
// Check kinds on free variables:
|
||||||
do with_appropriate_checker(cx, fn_id) |chk| {
|
do with_appropriate_checker(cx, fn_id) |chk| {
|
||||||
|
|
||||||
// Begin by checking the variables in the capture clause, if any.
|
|
||||||
// Here we slightly abuse the map function to both check and report
|
|
||||||
// errors and produce a list of the def id's for all capture
|
|
||||||
// variables. This list is used below to avoid checking and reporting
|
|
||||||
// on a given variable twice.
|
|
||||||
let cap_clause = match fk {
|
|
||||||
visit::fk_anon(_, cc) | visit::fk_fn_block(cc) => cc,
|
|
||||||
visit::fk_item_fn(*) | visit::fk_method(*) |
|
|
||||||
visit::fk_dtor(*) => @~[]
|
|
||||||
};
|
|
||||||
let captured_vars = do (*cap_clause).map |cap_item| {
|
|
||||||
let cap_def = cx.tcx.def_map.get(cap_item.id);
|
|
||||||
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
|
|
||||||
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
|
|
||||||
chk(cx, fn_id, None, cap_item.is_move, ty, cap_item.span);
|
|
||||||
cap_def_id
|
|
||||||
};
|
|
||||||
|
|
||||||
// Iterate over any free variables that may not have appeared in the
|
|
||||||
// capture list. Ensure that they too are of the appropriate kind.
|
|
||||||
for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) |fv| {
|
for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) |fv| {
|
||||||
let id = ast_util::def_id_of_def(fv.def).node;
|
chk(cx, *fv);
|
||||||
|
|
||||||
// skip over free variables that appear in the cap clause
|
|
||||||
if captured_vars.contains(&id) { loop; }
|
|
||||||
|
|
||||||
let ty = ty::node_id_to_type(cx.tcx, id);
|
|
||||||
|
|
||||||
// is_move is true if this type implicitly moves and false
|
|
||||||
// otherwise.
|
|
||||||
let is_move = ty::type_implicitly_moves(cx.tcx, ty);
|
|
||||||
|
|
||||||
chk(cx, fn_id, Some(*fv), is_move, ty, fv.span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
|
visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
|
fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) {
|
||||||
match b.node.expr {
|
|
||||||
Some(ex) => maybe_copy(cx, ex,
|
|
||||||
Some(("Tail expressions in blocks must be copyable",
|
|
||||||
try_adding))),
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
visit::visit_block(b, cx, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) {
|
|
||||||
for vec::each(a.pats) |p| {
|
for vec::each(a.pats) |p| {
|
||||||
do pat_util::pat_bindings(cx.tcx.def_map, *p) |mode, id, span, _pth| {
|
do pat_util::pat_bindings(cx.tcx.def_map, *p) |mode, id, span, _pth| {
|
||||||
if mode == bind_by_value {
|
if mode == bind_by_copy {
|
||||||
let t = ty::node_id_to_type(cx.tcx, id);
|
let t = ty::node_id_to_type(cx.tcx, id);
|
||||||
let reason = "consider binding with `ref` or `move` instead";
|
let reason = "consider binding with `ref` or `move` instead";
|
||||||
check_copy(cx, id, t, span, false, Some((reason,reason)));
|
check_copy(cx, t, span, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,14 +188,14 @@ pub fn check_arm(a: arm, cx: ctx, v: visit::vt<ctx>) {
|
||||||
|
|
||||||
pub fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
pub fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||||
debug!("kind::check_expr(%s)", expr_to_str(e, cx.tcx.sess.intr()));
|
debug!("kind::check_expr(%s)", expr_to_str(e, cx.tcx.sess.intr()));
|
||||||
let id_to_use = match e.node {
|
|
||||||
|
// Handle any kind bounds on type parameters
|
||||||
|
let type_parameter_id = match e.node {
|
||||||
expr_index(*)|expr_assign_op(*)|
|
expr_index(*)|expr_assign_op(*)|
|
||||||
expr_unary(*)|expr_binary(*)|expr_method_call(*) => e.callee_id,
|
expr_unary(*)|expr_binary(*)|expr_method_call(*) => e.callee_id,
|
||||||
_ => e.id
|
_ => e.id
|
||||||
};
|
};
|
||||||
|
do option::iter(&cx.tcx.node_type_substs.find(type_parameter_id)) |ts| {
|
||||||
// Handle any kind bounds on type parameters
|
|
||||||
do option::iter(&cx.tcx.node_type_substs.find(id_to_use)) |ts| {
|
|
||||||
let bounds = match e.node {
|
let bounds = match e.node {
|
||||||
expr_path(_) => {
|
expr_path(_) => {
|
||||||
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
|
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
|
||||||
|
@ -299,137 +220,76 @@ pub fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||||
*bounds, (*bounds).len());
|
*bounds, (*bounds).len());
|
||||||
}
|
}
|
||||||
for vec::each2(*ts, *bounds) |ty, bound| {
|
for vec::each2(*ts, *bounds) |ty, bound| {
|
||||||
check_bounds(cx, id_to_use, e.span, *ty, *bound)
|
check_bounds(cx, type_parameter_id, e.span, *ty, *bound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match /*bad*/copy e.node {
|
match e.node {
|
||||||
expr_assign(_, ex) |
|
expr_cast(source, _) => {
|
||||||
expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
|
check_cast_for_escaping_regions(cx, source, e);
|
||||||
expr_ret(Some(ex)) => {
|
check_kind_bounds_of_cast(cx, source, e);
|
||||||
maybe_copy(cx, ex, Some(("returned values must be copyable",
|
}
|
||||||
try_adding)));
|
expr_copy(expr) => {
|
||||||
}
|
// Note: This is the only place where we must check whether the
|
||||||
expr_cast(source, _) => {
|
// argument is copyable. This is not because this is the only
|
||||||
maybe_copy(cx, source, Some(("casted values must be copyable",
|
// kind of expression that may copy things, but rather because all
|
||||||
try_adding)));
|
// other copies will have been converted to moves by by the
|
||||||
check_cast_for_escaping_regions(cx, source, e);
|
// `moves` pass if the value is not copyable.
|
||||||
check_kind_bounds_of_cast(cx, source, e);
|
check_copy(cx,
|
||||||
}
|
ty::expr_ty(cx.tcx, expr),
|
||||||
expr_copy(expr) => check_copy_ex(cx, expr, false,
|
expr.span,
|
||||||
Some(("explicit copy requires a copyable argument", ""))),
|
"explicit copy requires a copyable argument");
|
||||||
// Vector add copies, but not "implicitly"
|
}
|
||||||
expr_assign_op(_, _, ex) => check_copy_ex(cx, ex, false,
|
expr_rec(ref fields, def) | expr_struct(_, ref fields, def) => {
|
||||||
Some(("assignment with operation requires \
|
match def {
|
||||||
a copyable argument", ""))),
|
Some(ex) => {
|
||||||
expr_binary(add, ls, rs) => {
|
// All noncopyable fields must be overridden
|
||||||
let reason = Some(("binary operators require copyable arguments",
|
let t = ty::expr_ty(cx.tcx, ex);
|
||||||
""));
|
let ty_fields = match ty::get(t).sty {
|
||||||
check_copy_ex(cx, ls, false, reason);
|
ty::ty_rec(ref f) => {
|
||||||
check_copy_ex(cx, rs, false, reason);
|
copy *f
|
||||||
}
|
}
|
||||||
expr_rec(ref fields, def) | expr_struct(_, ref fields, def) => {
|
ty::ty_struct(did, ref substs) => {
|
||||||
for (*fields).each |field| { maybe_copy(cx, field.node.expr,
|
ty::struct_fields(cx.tcx, did, substs)
|
||||||
Some(("record or struct fields require \
|
}
|
||||||
copyable arguments", ""))); }
|
_ => {
|
||||||
match def {
|
cx.tcx.sess.span_bug(
|
||||||
Some(ex) => {
|
ex.span,
|
||||||
// All noncopyable fields must be overridden
|
~"bad base expr type in record")
|
||||||
let t = ty::expr_ty(cx.tcx, ex);
|
}
|
||||||
let ty_fields = match /*bad*/copy ty::get(t).sty {
|
};
|
||||||
ty::ty_rec(f) => f,
|
for ty_fields.each |tf| {
|
||||||
ty::ty_struct(did, ref substs) =>
|
// If this field would not be copied, ok.
|
||||||
ty::struct_fields(cx.tcx, did, &(*substs)),
|
if fields.any(|f| f.node.ident == tf.ident) { loop; }
|
||||||
_ => cx.tcx.sess.span_bug(ex.span,
|
|
||||||
~"bad base expr type in record")
|
// If this field is copyable, ok.
|
||||||
};
|
let kind = ty::type_kind(cx.tcx, tf.mt.ty);
|
||||||
for ty_fields.each |tf| {
|
if ty::kind_can_be_copied(kind) { loop; }
|
||||||
if !vec::any((*fields), |f| f.node.ident == tf.ident ) &&
|
|
||||||
!ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) {
|
cx.tcx.sess.span_err(
|
||||||
cx.tcx.sess.span_err(e.span,
|
e.span,
|
||||||
~"copying a noncopyable value");
|
fmt!("cannot copy field `%s` of base expression, \
|
||||||
|
which has a noncopyable type",
|
||||||
|
*cx.tcx.sess.intr().get(tf.ident)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
_ => {}
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expr_tup(exprs) | expr_vec(exprs, _) => {
|
|
||||||
for exprs.each |expr| { maybe_copy(cx, *expr,
|
|
||||||
Some(("tuple or vec elements must be copyable", ""))); }
|
|
||||||
}
|
|
||||||
expr_call(f, args, _) => {
|
|
||||||
for ty::ty_fn_args(ty::expr_ty(cx.tcx, f)).eachi |i, arg_t| {
|
|
||||||
match ty::arg_mode(cx.tcx, *arg_t) {
|
|
||||||
by_copy => maybe_copy(cx, args[i],
|
|
||||||
Some(("function arguments must be copyable",
|
|
||||||
"try changing the function to take a reference \
|
|
||||||
instead"))),
|
|
||||||
by_ref | by_val | by_move => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
expr_repeat(element, count_expr, _) => {
|
||||||
expr_method_call(_, _, _, args, _) => {
|
let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span);
|
||||||
for ty::ty_fn_args(ty::node_id_to_type(cx.tcx, e.callee_id)).eachi
|
if count > 1 {
|
||||||
|i, arg_t| {
|
let element_ty = ty::expr_ty(cx.tcx, element);
|
||||||
match ty::arg_mode(cx.tcx, *arg_t) {
|
check_copy(cx, element_ty, element.span,
|
||||||
by_copy => maybe_copy(cx, args[i],
|
"repeated element will be copied");
|
||||||
Some(("function arguments must be copyable",
|
|
||||||
"try changing the function to take a \
|
|
||||||
reference instead"))),
|
|
||||||
by_ref | by_val | by_move => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_ => {}
|
||||||
expr_field(lhs, _, _) => {
|
|
||||||
// If this is a method call with a by-val argument, we need
|
|
||||||
// to check the copy
|
|
||||||
match cx.method_map.find(e.id) {
|
|
||||||
Some(ref mme) => {
|
|
||||||
match ty::arg_mode(cx.tcx, mme.self_arg) {
|
|
||||||
by_copy => maybe_copy(cx, lhs,
|
|
||||||
Some(("method call takes its self argument by copy",
|
|
||||||
""))),
|
|
||||||
by_ref | by_val | by_move => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expr_repeat(element, count_expr, _) => {
|
|
||||||
let count = ty::eval_repeat_count(cx.tcx, count_expr, e.span);
|
|
||||||
if count == 1 {
|
|
||||||
maybe_copy(cx, element, Some(("trivial repeat takes its element \
|
|
||||||
by copy", "")));
|
|
||||||
} else {
|
|
||||||
let element_ty = ty::expr_ty(cx.tcx, element);
|
|
||||||
check_copy(cx, element.id, element_ty, element.span, true,
|
|
||||||
Some(("repeat takes its elements by copy", "")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
visit::visit_expr(e, cx, v);
|
visit::visit_expr(e, cx, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
|
fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) {
|
||||||
match stmt.node {
|
|
||||||
stmt_decl(@spanned {node: decl_local(ref locals), _}, _) => {
|
|
||||||
for locals.each |local| {
|
|
||||||
match local.node.init {
|
|
||||||
Some(expr) =>
|
|
||||||
maybe_copy(cx, expr, Some(("initializer statement \
|
|
||||||
takes its right-hand side by copy", ""))),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
visit::visit_stmt(stmt, cx, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_ty(aty: @Ty, cx: ctx, v: visit::vt<ctx>) {
|
|
||||||
match aty.node {
|
match aty.node {
|
||||||
ty_path(_, id) => {
|
ty_path(_, id) => {
|
||||||
do option::iter(&cx.tcx.node_type_substs.find(id)) |ts| {
|
do option::iter(&cx.tcx.node_type_substs.find(id)) |ts| {
|
||||||
|
@ -471,11 +331,7 @@ pub fn check_bounds(cx: ctx, id: node_id, sp: span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maybe_copy(cx: ctx, ex: @expr, why: Option<(&str,&str)>) {
|
fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
|
||||||
check_copy_ex(cx, ex, true, why);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
|
|
||||||
match ex.node {
|
match ex.node {
|
||||||
expr_path(_) => {
|
expr_path(_) => {
|
||||||
match cx.tcx.def_map.get(ex.id) {
|
match cx.tcx.def_map.get(ex.id) {
|
||||||
|
@ -489,75 +345,31 @@ pub fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool,
|
fn check_imm_free_var(cx: ctx, def: def, sp: span) {
|
||||||
why: Option<(&str,&str)>) {
|
match def {
|
||||||
if ty::expr_is_lval(cx.tcx, cx.method_map, ex) &&
|
def_local(_, is_mutbl) => {
|
||||||
|
if is_mutbl {
|
||||||
// a reference to a constant like `none`... no need to warn
|
cx.tcx.sess.span_err(
|
||||||
// about *this* even if the type is Option<~int>
|
sp,
|
||||||
!is_nullary_variant(cx, ex) &&
|
~"mutable variables cannot be implicitly captured");
|
||||||
|
|
||||||
// borrowed unique value isn't really a copy
|
|
||||||
!is_autorefd(cx, ex)
|
|
||||||
{
|
|
||||||
match cx.tcx.value_modes.find(ex.id) {
|
|
||||||
None => cx.tcx.sess.span_bug(ex.span, ~"no value mode for lval"),
|
|
||||||
Some(MoveValue) | Some(ReadValue) => {} // Won't be a copy.
|
|
||||||
Some(CopyValue) => {
|
|
||||||
debug!("(kind checking) is a copy value: `%s`",
|
|
||||||
expr_to_str(ex, cx.tcx.sess.intr()));
|
|
||||||
let ty = ty::expr_ty(cx.tcx, ex);
|
|
||||||
check_copy(cx, ex.id, ty, ex.span, implicit_copy, why);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
def_arg(*) => { /* ok */ }
|
||||||
|
def_upvar(_, def1, _, _) => { check_imm_free_var(cx, *def1, sp); }
|
||||||
fn is_autorefd(cx: ctx, ex: @expr) -> bool {
|
def_binding(*) | def_self(*) => { /*ok*/ }
|
||||||
match cx.tcx.adjustments.find(ex.id) {
|
_ => {
|
||||||
None => false,
|
cx.tcx.sess.span_bug(
|
||||||
Some(ref adj) => adj.autoref.is_some()
|
sp,
|
||||||
|
fmt!("unknown def for free variable: %?", def));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_imm_free_var(cx: ctx, def: def, sp: span) {
|
fn check_copy(cx: ctx, ty: ty::t, sp: span, reason: &str) {
|
||||||
let msg = ~"mutable variables cannot be implicitly captured; \
|
|
||||||
use a capture clause";
|
|
||||||
match def {
|
|
||||||
def_local(_, is_mutbl) => {
|
|
||||||
if is_mutbl {
|
|
||||||
cx.tcx.sess.span_err(sp, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def_arg(*) => { /* ok */ }
|
|
||||||
def_upvar(_, def1, _, _) => {
|
|
||||||
check_imm_free_var(cx, *def1, sp);
|
|
||||||
}
|
|
||||||
def_binding(*) | def_self(*) => { /*ok*/ }
|
|
||||||
_ => {
|
|
||||||
cx.tcx.sess.span_bug(
|
|
||||||
sp,
|
|
||||||
fmt!("unknown def for free variable: %?", def));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_copy(cx: ctx, id: node_id, ty: ty::t, sp: span,
|
|
||||||
implicit_copy: bool, why: Option<(&str,&str)>) {
|
|
||||||
let k = ty::type_kind(cx.tcx, ty);
|
let k = ty::type_kind(cx.tcx, ty);
|
||||||
if !ty::kind_can_be_copied(k) {
|
if !ty::kind_can_be_copied(k) {
|
||||||
cx.tcx.sess.span_err(sp, ~"copying a noncopyable value");
|
cx.tcx.sess.span_err(sp, ~"copying a noncopyable value");
|
||||||
do why.map |reason| {
|
cx.tcx.sess.span_note(sp, fmt!("%s", reason));
|
||||||
cx.tcx.sess.span_note(sp, fmt!("%s", reason.first()));
|
|
||||||
};
|
|
||||||
} else if implicit_copy && !ty::kind_can_be_implicitly_copied(k) {
|
|
||||||
cx.tcx.sess.span_lint(
|
|
||||||
implicit_copies, id, cx.current_item,
|
|
||||||
sp,
|
|
||||||
~"implicitly copying a non-implicitly-copyable value");
|
|
||||||
do why.map |reason| {
|
|
||||||
cx.tcx.sess.span_note(sp, fmt!("%s", reason.second()));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -352,7 +352,7 @@ impl LanguageItemCollector {
|
||||||
return; // Didn't match.
|
return; // Didn't match.
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.item_refs.find(value) {
|
match self.item_refs.find(/*bad*/copy value) {
|
||||||
None => {
|
None => {
|
||||||
// Didn't match.
|
// Didn't match.
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,7 +344,7 @@ impl ctxt {
|
||||||
|
|
||||||
for triples.each |pair| {
|
for triples.each |pair| {
|
||||||
let (meta, level, lintname) = /*bad*/copy *pair;
|
let (meta, level, lintname) = /*bad*/copy *pair;
|
||||||
match self.dict.find(lintname) {
|
match self.dict.find(/*bad*/ copy lintname) {
|
||||||
None => {
|
None => {
|
||||||
self.span_lint(
|
self.span_lint(
|
||||||
new_ctxt.get_level(unrecognized_lint),
|
new_ctxt.get_level(unrecognized_lint),
|
||||||
|
@ -518,9 +518,9 @@ fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
|
||||||
|
|
||||||
fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr,
|
fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr,
|
||||||
r: &ast::expr) -> bool {
|
r: &ast::expr) -> bool {
|
||||||
let (lit, expr, swap) = match (l.node, r.node) {
|
let (lit, expr, swap) = match (&l.node, &r.node) {
|
||||||
(ast::expr_lit(_), _) => (l, r, true),
|
(&ast::expr_lit(_), _) => (l, r, true),
|
||||||
(_, ast::expr_lit(_)) => (r, l, false),
|
(_, &ast::expr_lit(_)) => (r, l, false),
|
||||||
_ => return true
|
_ => return true
|
||||||
};
|
};
|
||||||
// Normalize the binop so that the literal is always on the RHS in
|
// Normalize the binop so that the literal is always on the RHS in
|
||||||
|
|
|
@ -105,12 +105,11 @@
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use middle::capture::{cap_move, cap_drop, cap_copy, cap_ref};
|
|
||||||
use middle::capture;
|
|
||||||
use middle::pat_util;
|
use middle::pat_util;
|
||||||
use middle::ty::MoveValue;
|
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck;
|
use middle::typeck;
|
||||||
|
use middle::moves;
|
||||||
|
use util::ppaux::ty_to_str;
|
||||||
|
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::dvec::DVec;
|
use core::dvec::DVec;
|
||||||
|
@ -203,6 +202,8 @@ fn live_node_kind_to_str(lnk: LiveNodeKind, cx: ty::ctxt) -> ~str {
|
||||||
|
|
||||||
pub fn check_crate(tcx: ty::ctxt,
|
pub fn check_crate(tcx: ty::ctxt,
|
||||||
method_map: typeck::method_map,
|
method_map: typeck::method_map,
|
||||||
|
variable_moves_map: moves::VariableMovesMap,
|
||||||
|
capture_map: moves::CaptureMap,
|
||||||
crate: @crate) -> last_use_map {
|
crate: @crate) -> last_use_map {
|
||||||
let visitor = visit::mk_vt(@visit::Visitor {
|
let visitor = visit::mk_vt(@visit::Visitor {
|
||||||
visit_fn: visit_fn,
|
visit_fn: visit_fn,
|
||||||
|
@ -213,7 +214,8 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
});
|
});
|
||||||
|
|
||||||
let last_use_map = HashMap();
|
let last_use_map = HashMap();
|
||||||
let initial_maps = @IrMaps(tcx, method_map, last_use_map);
|
let initial_maps = @IrMaps(tcx, method_map, variable_moves_map,
|
||||||
|
capture_map, last_use_map);
|
||||||
visit::visit_crate(*crate, initial_maps, visitor);
|
visit::visit_crate(*crate, initial_maps, visitor);
|
||||||
tcx.sess.abort_if_errors();
|
tcx.sess.abort_if_errors();
|
||||||
return last_use_map;
|
return last_use_map;
|
||||||
|
@ -294,28 +296,35 @@ fn relevant_def(def: def) -> Option<node_id> {
|
||||||
struct IrMaps {
|
struct IrMaps {
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
method_map: typeck::method_map,
|
method_map: typeck::method_map,
|
||||||
|
variable_moves_map: moves::VariableMovesMap,
|
||||||
|
capture_map: moves::CaptureMap,
|
||||||
last_use_map: last_use_map,
|
last_use_map: last_use_map,
|
||||||
|
|
||||||
mut num_live_nodes: uint,
|
mut num_live_nodes: uint,
|
||||||
mut num_vars: uint,
|
mut num_vars: uint,
|
||||||
live_node_map: HashMap<node_id, LiveNode>,
|
live_node_map: HashMap<node_id, LiveNode>,
|
||||||
variable_map: HashMap<node_id, Variable>,
|
variable_map: HashMap<node_id, Variable>,
|
||||||
capture_map: HashMap<node_id, @~[CaptureInfo]>,
|
capture_info_map: HashMap<node_id, @~[CaptureInfo]>,
|
||||||
mut var_kinds: ~[VarKind],
|
mut var_kinds: ~[VarKind],
|
||||||
mut lnks: ~[LiveNodeKind],
|
mut lnks: ~[LiveNodeKind],
|
||||||
}
|
}
|
||||||
|
|
||||||
fn IrMaps(tcx: ty::ctxt, method_map: typeck::method_map,
|
fn IrMaps(tcx: ty::ctxt,
|
||||||
|
method_map: typeck::method_map,
|
||||||
|
variable_moves_map: moves::VariableMovesMap,
|
||||||
|
capture_map: moves::CaptureMap,
|
||||||
last_use_map: last_use_map) -> IrMaps {
|
last_use_map: last_use_map) -> IrMaps {
|
||||||
IrMaps {
|
IrMaps {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
method_map: method_map,
|
method_map: method_map,
|
||||||
|
variable_moves_map: variable_moves_map,
|
||||||
|
capture_map: capture_map,
|
||||||
last_use_map: last_use_map,
|
last_use_map: last_use_map,
|
||||||
num_live_nodes: 0,
|
num_live_nodes: 0,
|
||||||
num_vars: 0,
|
num_vars: 0,
|
||||||
live_node_map: HashMap(),
|
live_node_map: HashMap(),
|
||||||
variable_map: HashMap(),
|
variable_map: HashMap(),
|
||||||
capture_map: HashMap(),
|
capture_info_map: HashMap(),
|
||||||
var_kinds: ~[],
|
var_kinds: ~[],
|
||||||
lnks: ~[]
|
lnks: ~[]
|
||||||
}
|
}
|
||||||
|
@ -377,11 +386,11 @@ impl IrMaps {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_captures(node_id: node_id, +cs: ~[CaptureInfo]) {
|
fn set_captures(node_id: node_id, +cs: ~[CaptureInfo]) {
|
||||||
self.capture_map.insert(node_id, @cs);
|
self.capture_info_map.insert(node_id, @cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn captures(expr: @expr) -> @~[CaptureInfo] {
|
fn captures(expr: @expr) -> @~[CaptureInfo] {
|
||||||
match self.capture_map.find(expr.id) {
|
match self.capture_info_map.find(expr.id) {
|
||||||
Some(caps) => caps,
|
Some(caps) => caps,
|
||||||
None => {
|
None => {
|
||||||
self.tcx.sess.span_bug(expr.span, ~"no registered caps");
|
self.tcx.sess.span_bug(expr.span, ~"no registered caps");
|
||||||
|
@ -397,7 +406,6 @@ impl IrMaps {
|
||||||
let vk = self.var_kinds[*var];
|
let vk = self.var_kinds[*var];
|
||||||
debug!("Node %d is a last use of variable %?", expr_id, vk);
|
debug!("Node %d is a last use of variable %?", expr_id, vk);
|
||||||
match vk {
|
match vk {
|
||||||
Arg(id, _, by_move) |
|
|
||||||
Arg(id, _, by_copy) |
|
Arg(id, _, by_copy) |
|
||||||
Local(LocalInfo {id: id, kind: FromLetNoInitializer, _}) |
|
Local(LocalInfo {id: id, kind: FromLetNoInitializer, _}) |
|
||||||
Local(LocalInfo {id: id, kind: FromLetWithInitializer, _}) |
|
Local(LocalInfo {id: id, kind: FromLetWithInitializer, _}) |
|
||||||
|
@ -427,7 +435,10 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
||||||
let _i = ::util::common::indenter();
|
let _i = ::util::common::indenter();
|
||||||
|
|
||||||
// swap in a new set of IR maps for this function body:
|
// swap in a new set of IR maps for this function body:
|
||||||
let fn_maps = @IrMaps(self.tcx, self.method_map,
|
let fn_maps = @IrMaps(self.tcx,
|
||||||
|
self.method_map,
|
||||||
|
self.variable_moves_map,
|
||||||
|
self.capture_map,
|
||||||
self.last_use_map);
|
self.last_use_map);
|
||||||
|
|
||||||
debug!("creating fn_maps: %x", ptr::addr_of(&(*fn_maps)) as uint);
|
debug!("creating fn_maps: %x", ptr::addr_of(&(*fn_maps)) as uint);
|
||||||
|
@ -548,8 +559,8 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
|
||||||
}
|
}
|
||||||
visit::visit_expr(expr, self, vt);
|
visit::visit_expr(expr, self, vt);
|
||||||
}
|
}
|
||||||
expr_fn(_, _, _, cap_clause) |
|
expr_fn(_, _, _) |
|
||||||
expr_fn_block(_, _, cap_clause) => {
|
expr_fn_block(_, _) => {
|
||||||
// Interesting control flow (for loops can contain labeled
|
// Interesting control flow (for loops can contain labeled
|
||||||
// breaks or continues)
|
// breaks or continues)
|
||||||
self.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
self.add_live_node_for_node(expr.id, ExprNode(expr.span));
|
||||||
|
@ -558,17 +569,18 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
|
||||||
// being the location that the variable is used. This results
|
// being the location that the variable is used. This results
|
||||||
// in better error messages than just pointing at the closure
|
// in better error messages than just pointing at the closure
|
||||||
// construction site.
|
// construction site.
|
||||||
let proto = ty::ty_fn_proto(ty::expr_ty(self.tcx, expr));
|
let cvs = self.capture_map.get(expr.id);
|
||||||
let cvs = capture::compute_capture_vars(self.tcx, expr.id, proto,
|
|
||||||
cap_clause);
|
|
||||||
let mut call_caps = ~[];
|
let mut call_caps = ~[];
|
||||||
for cvs.each |cv| {
|
for cvs.each |cv| {
|
||||||
match relevant_def(cv.def) {
|
match relevant_def(cv.def) {
|
||||||
Some(rv) => {
|
Some(rv) => {
|
||||||
let cv_ln = self.add_live_node(FreeVarNode(cv.span));
|
let cv_ln = self.add_live_node(FreeVarNode(cv.span));
|
||||||
let is_move = match cv.mode {
|
let is_move = match cv.mode {
|
||||||
cap_move | cap_drop => true, // var must be dead afterwards
|
// var must be dead afterwards
|
||||||
cap_copy | cap_ref => false // var can still be used
|
moves::CapMove => true,
|
||||||
|
|
||||||
|
// var can stil be used
|
||||||
|
moves::CapCopy | moves::CapRef => false
|
||||||
};
|
};
|
||||||
call_caps.push(CaptureInfo {ln: cv_ln,
|
call_caps.push(CaptureInfo {ln: cv_ln,
|
||||||
is_move: is_move,
|
is_move: is_move,
|
||||||
|
@ -600,7 +612,7 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
|
||||||
expr_loop_body(*) | expr_do_body(*) | expr_cast(*) |
|
expr_loop_body(*) | expr_do_body(*) | expr_cast(*) |
|
||||||
expr_unary(*) | expr_fail(*) |
|
expr_unary(*) | expr_fail(*) |
|
||||||
expr_break(_) | expr_again(_) | expr_lit(_) | expr_ret(*) |
|
expr_break(_) | expr_again(_) | expr_lit(_) | expr_ret(*) |
|
||||||
expr_block(*) | expr_unary_move(*) | expr_assign(*) |
|
expr_block(*) | expr_assign(*) |
|
||||||
expr_swap(*) | expr_assign_op(*) | expr_mac(*) | expr_struct(*) |
|
expr_swap(*) | expr_assign_op(*) | expr_mac(*) | expr_struct(*) |
|
||||||
expr_repeat(*) | expr_paren(*) => {
|
expr_repeat(*) | expr_paren(*) => {
|
||||||
visit::visit_expr(expr, self, vt);
|
visit::visit_expr(expr, self, vt);
|
||||||
|
@ -700,7 +712,7 @@ impl Liveness {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable(node_id: node_id, span: span) -> Variable {
|
fn variable(node_id: node_id, span: span) -> Variable {
|
||||||
(*self.ir).variable(node_id, span)
|
self.ir.variable(node_id, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable_from_def_map(node_id: node_id,
|
fn variable_from_def_map(node_id: node_id,
|
||||||
|
@ -760,7 +772,7 @@ impl Liveness {
|
||||||
|
|
||||||
assert ln.is_valid();
|
assert ln.is_valid();
|
||||||
let reader = self.users[self.idx(ln, var)].reader;
|
let reader = self.users[self.idx(ln, var)].reader;
|
||||||
if reader.is_valid() {Some((*self.ir).lnk(reader))} else {None}
|
if reader.is_valid() {Some(self.ir.lnk(reader))} else {None}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -782,7 +794,7 @@ impl Liveness {
|
||||||
|
|
||||||
assert ln.is_valid();
|
assert ln.is_valid();
|
||||||
let writer = self.users[self.idx(ln, var)].writer;
|
let writer = self.users[self.idx(ln, var)].writer;
|
||||||
if writer.is_valid() {Some((*self.ir).lnk(writer))} else {None}
|
if writer.is_valid() {Some(self.ir.lnk(writer))} else {None}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assigned_on_exit(ln: LiveNode, var: Variable)
|
fn assigned_on_exit(ln: LiveNode, var: Variable)
|
||||||
|
@ -980,21 +992,22 @@ impl Liveness {
|
||||||
// inputs passed by & mode should be considered live on exit:
|
// inputs passed by & mode should be considered live on exit:
|
||||||
for decl.inputs.each |arg| {
|
for decl.inputs.each |arg| {
|
||||||
match ty::resolved_mode(self.tcx, arg.mode) {
|
match ty::resolved_mode(self.tcx, arg.mode) {
|
||||||
by_ref | by_val => {
|
by_val | by_ref => {
|
||||||
// These are "non-owned" modes, so register a read at
|
// By val and by ref do not own, so register a
|
||||||
// the end. This will prevent us from moving out of
|
// read at the end. This will prevent us from
|
||||||
// such variables but also prevent us from registering
|
// moving out of such variables but also prevent
|
||||||
// last uses and so forth.
|
// us from registering last uses and so forth.
|
||||||
do pat_util::pat_bindings(self.tcx.def_map, arg.pat)
|
do pat_util::pat_bindings(self.tcx.def_map, arg.pat)
|
||||||
|_bm, arg_id, _sp, _path| {
|
|_bm, arg_id, _sp, _path|
|
||||||
let var = self.variable(arg_id, blk.span);
|
{
|
||||||
self.acc(self.s.exit_ln, var, ACC_READ);
|
let var = self.variable(arg_id, blk.span);
|
||||||
|
self.acc(self.s.exit_ln, var, ACC_READ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
by_copy => {
|
||||||
|
// By copy is an owned mode. If we don't use the
|
||||||
|
// variable, nobody will.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
by_move | by_copy => {
|
|
||||||
// These are owned modes. If we don't use the
|
|
||||||
// variable, nobody will.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,7 +1105,7 @@ impl Liveness {
|
||||||
self.propagate_through_expr(e, succ)
|
self.propagate_through_expr(e, succ)
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_fn(_, _, ref blk, _) | expr_fn_block(_, ref blk, _) => {
|
expr_fn(_, _, ref blk) | expr_fn_block(_, ref blk) => {
|
||||||
debug!("%s is an expr_fn or expr_fn_block",
|
debug!("%s is an expr_fn or expr_fn_block",
|
||||||
expr_to_str(expr, self.tcx.sess.intr()));
|
expr_to_str(expr, self.tcx.sess.intr()));
|
||||||
|
|
||||||
|
@ -1105,8 +1118,8 @@ impl Liveness {
|
||||||
|
|
||||||
// the construction of a closure itself is not important,
|
// the construction of a closure itself is not important,
|
||||||
// but we have to consider the closed over variables.
|
// but we have to consider the closed over variables.
|
||||||
let caps = (*self.ir).captures(expr);
|
let caps = self.ir.captures(expr);
|
||||||
do (*caps).foldr(succ) |cap, succ| {
|
do caps.foldr(succ) |cap, succ| {
|
||||||
self.init_from_succ(cap.ln, succ);
|
self.init_from_succ(cap.ln, succ);
|
||||||
let var = self.variable(cap.var_nid, expr.span);
|
let var = self.variable(cap.var_nid, expr.span);
|
||||||
self.acc(cap.ln, var, ACC_READ | ACC_USE);
|
self.acc(cap.ln, var, ACC_READ | ACC_USE);
|
||||||
|
@ -1315,7 +1328,6 @@ impl Liveness {
|
||||||
expr_assert(e) |
|
expr_assert(e) |
|
||||||
expr_addr_of(_, e) |
|
expr_addr_of(_, e) |
|
||||||
expr_copy(e) |
|
expr_copy(e) |
|
||||||
expr_unary_move(e) |
|
|
||||||
expr_loop_body(e) |
|
expr_loop_body(e) |
|
||||||
expr_do_body(e) |
|
expr_do_body(e) |
|
||||||
expr_cast(e, _) |
|
expr_cast(e, _) |
|
||||||
|
@ -1541,26 +1553,6 @@ fn check_arm(arm: arm, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
visit::visit_arm(arm, self, vt);
|
visit::visit_arm(arm, self, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_call(args: &[@expr],
|
|
||||||
targs: &[ty::arg],
|
|
||||||
&&self: @Liveness) {
|
|
||||||
for vec::each2(args, targs) |arg_expr, arg_ty| {
|
|
||||||
match ty::resolved_mode(self.tcx, arg_ty.mode) {
|
|
||||||
by_val | by_copy | by_ref => {}
|
|
||||||
by_move => {
|
|
||||||
if ty::expr_is_lval(self.tcx, self.ir.method_map, *arg_expr) {
|
|
||||||
// Probably a bad error message (what's an rvalue?)
|
|
||||||
// but I can't think of anything better
|
|
||||||
self.tcx.sess.span_err(arg_expr.span,
|
|
||||||
fmt!("move mode argument must be an rvalue: try (move \
|
|
||||||
%s) instead",
|
|
||||||
expr_to_str(*arg_expr, self.tcx.sess.intr())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
match /*bad*/copy expr.node {
|
match /*bad*/copy expr.node {
|
||||||
expr_path(_) => {
|
expr_path(_) => {
|
||||||
|
@ -1568,19 +1560,12 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
let ln = self.live_node(expr.id, expr.span);
|
let ln = self.live_node(expr.id, expr.span);
|
||||||
self.consider_last_use(expr, ln, *var);
|
self.consider_last_use(expr, ln, *var);
|
||||||
|
|
||||||
match self.tcx.value_modes.find(expr.id) {
|
match self.ir.variable_moves_map.find(expr.id) {
|
||||||
Some(MoveValue) => {
|
None => {}
|
||||||
|
Some(entire_expr) => {
|
||||||
debug!("(checking expr) is a move: `%s`",
|
debug!("(checking expr) is a move: `%s`",
|
||||||
expr_to_str(expr, self.tcx.sess.intr()));
|
expr_to_str(expr, self.tcx.sess.intr()));
|
||||||
self.check_move_from_var(expr.span, ln, *var);
|
self.check_move_from_var(ln, *var, entire_expr);
|
||||||
}
|
|
||||||
Some(v) => {
|
|
||||||
debug!("(checking expr) not a move (%?): `%s`",
|
|
||||||
v,
|
|
||||||
expr_to_str(expr, self.tcx.sess.intr()));
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
fail ~"no mode for lval";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1589,12 +1574,12 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_fn(*) | expr_fn_block(*) => {
|
expr_fn(*) | expr_fn_block(*) => {
|
||||||
let caps = (*self.ir).captures(expr);
|
let caps = self.ir.captures(expr);
|
||||||
for (*caps).each |cap| {
|
for caps.each |cap| {
|
||||||
let var = self.variable(cap.var_nid, expr.span);
|
let var = self.variable(cap.var_nid, expr.span);
|
||||||
self.consider_last_use(expr, cap.ln, var);
|
self.consider_last_use(expr, cap.ln, var);
|
||||||
if cap.is_move {
|
if cap.is_move {
|
||||||
self.check_move_from_var(expr.span, cap.ln, var);
|
self.check_move_from_var(cap.ln, var, expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1608,32 +1593,14 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
visit::visit_expr(expr, self, vt);
|
visit::visit_expr(expr, self, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_unary_move(r) => {
|
|
||||||
self.check_move_from_expr(r, vt);
|
|
||||||
|
|
||||||
visit::visit_expr(expr, self, vt);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_assign_op(_, l, _) => {
|
expr_assign_op(_, l, _) => {
|
||||||
self.check_lvalue(l, vt);
|
self.check_lvalue(l, vt);
|
||||||
|
|
||||||
visit::visit_expr(expr, self, vt);
|
visit::visit_expr(expr, self, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_call(f, args, _) => {
|
|
||||||
let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f));
|
|
||||||
check_call(args, targs, self);
|
|
||||||
visit::visit_expr(expr, self, vt);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_method_call(_, _, _, args, _) => {
|
|
||||||
let targs = ty::ty_fn_args(ty::node_id_to_type(self.tcx,
|
|
||||||
expr.callee_id));
|
|
||||||
check_call(args, targs, self);
|
|
||||||
visit::visit_expr(expr, self, vt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no correctness conditions related to liveness
|
// no correctness conditions related to liveness
|
||||||
|
expr_call(*) | expr_method_call(*) |
|
||||||
expr_if(*) | expr_match(*) |
|
expr_if(*) | expr_match(*) |
|
||||||
expr_while(*) | expr_loop(*) |
|
expr_while(*) | expr_loop(*) |
|
||||||
expr_index(*) | expr_field(*) | expr_vstore(*) |
|
expr_index(*) | expr_field(*) | expr_vstore(*) |
|
||||||
|
@ -1659,7 +1626,8 @@ fn check_fn(_fk: visit::fn_kind, _decl: fn_decl,
|
||||||
enum ReadKind {
|
enum ReadKind {
|
||||||
PossiblyUninitializedVariable,
|
PossiblyUninitializedVariable,
|
||||||
PossiblyUninitializedField,
|
PossiblyUninitializedField,
|
||||||
MovedValue
|
MovedValue,
|
||||||
|
PartiallyMovedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
impl @Liveness {
|
impl @Liveness {
|
||||||
|
@ -1683,17 +1651,28 @@ impl @Liveness {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
fn check_move_from_var(ln: LiveNode,
|
||||||
Checks whether <var> is live on entry to any of the successors of <ln>.
|
var: Variable,
|
||||||
If it is, report an error.
|
move_expr: @expr)
|
||||||
*/
|
{
|
||||||
fn check_move_from_var(span: span, ln: LiveNode, var: Variable) {
|
/*!
|
||||||
|
*
|
||||||
|
* Checks whether `var` is live on entry to any of the
|
||||||
|
* successors of `ln`. If it is, report an error.
|
||||||
|
* `move_expr` is the expression which caused the variable
|
||||||
|
* to be moved.
|
||||||
|
*
|
||||||
|
* Note that `move_expr` is not necessarily a reference to the
|
||||||
|
* variable. It might be an expression like `x.f` which could
|
||||||
|
* cause a move of the variable `x`, or a closure creation.
|
||||||
|
*/
|
||||||
|
|
||||||
debug!("check_move_from_var(%s, %s)",
|
debug!("check_move_from_var(%s, %s)",
|
||||||
ln.to_str(), var.to_str());
|
ln.to_str(), var.to_str());
|
||||||
|
|
||||||
match self.live_on_exit(ln, var) {
|
match self.live_on_exit(ln, var) {
|
||||||
None => {}
|
None => {}
|
||||||
Some(lnk) => self.report_illegal_move(span, lnk, var)
|
Some(lnk) => self.report_illegal_move(lnk, var, move_expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1703,48 +1682,7 @@ impl @Liveness {
|
||||||
|
|
||||||
match self.live_on_exit(ln, var) {
|
match self.live_on_exit(ln, var) {
|
||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
None => (*self.ir).add_last_use(expr.id, var)
|
None => self.ir.add_last_use(expr.id, var)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_move_from_expr(expr: @expr, vt: vt<@Liveness>) {
|
|
||||||
debug!("check_move_from_expr(node %d: %s)",
|
|
||||||
expr.id, expr_to_str(expr, self.tcx.sess.intr()));
|
|
||||||
|
|
||||||
if self.ir.method_map.contains_key(expr.id) {
|
|
||||||
// actually an rvalue, since this calls a method
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match expr.node {
|
|
||||||
expr_path(_) => {
|
|
||||||
match self.variable_from_path(expr) {
|
|
||||||
Some(var) => {
|
|
||||||
let ln = self.live_node(expr.id, expr.span);
|
|
||||||
self.check_move_from_var(expr.span, ln, var);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_field(base, _, _) => {
|
|
||||||
// Moving from x.y is allowed if x is never used later.
|
|
||||||
// (Note that the borrowck guarantees that anything
|
|
||||||
// being moved from is uniquely tied to the stack frame)
|
|
||||||
self.check_move_from_expr(base, vt);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_index(base, _) => {
|
|
||||||
// Moving from x[y] is allowed if x is never used later.
|
|
||||||
// (Note that the borrowck guarantees that anything
|
|
||||||
// being moved from is uniquely tied to the stack frame)
|
|
||||||
self.check_move_from_expr(base, vt);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
// For other kinds of lvalues, no checks are required,
|
|
||||||
// and any embedded expressions are actually rvalues
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1808,36 +1746,84 @@ impl @Liveness {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_illegal_move(move_span: span,
|
fn report_illegal_move(lnk: LiveNodeKind,
|
||||||
lnk: LiveNodeKind,
|
var: Variable,
|
||||||
var: Variable) {
|
move_expr: @expr)
|
||||||
|
{
|
||||||
// the only time that it is possible to have a moved value
|
// the only time that it is possible to have a moved variable
|
||||||
// used by ExitNode would be arguments or fields in a ctor.
|
// used by ExitNode would be arguments or fields in a ctor.
|
||||||
// we give a slightly different error message in those cases.
|
// we give a slightly different error message in those cases.
|
||||||
if lnk == ExitNode {
|
if lnk == ExitNode {
|
||||||
|
// XXX this seems like it should be reported in the borrow checker
|
||||||
let vk = self.ir.var_kinds[*var];
|
let vk = self.ir.var_kinds[*var];
|
||||||
match vk {
|
match vk {
|
||||||
Arg(_, name, _) => {
|
Arg(_, name, _) => {
|
||||||
self.tcx.sess.span_err(
|
self.tcx.sess.span_err(
|
||||||
move_span,
|
move_expr.span,
|
||||||
fmt!("illegal move from argument `%s`, which is not \
|
fmt!("illegal move from argument `%s`, which is not \
|
||||||
copy or move mode", self.tcx.sess.str_of(name)));
|
copy or move mode", self.tcx.sess.str_of(name)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Local(*) | ImplicitRet => {
|
Local(*) | ImplicitRet => {
|
||||||
self.tcx.sess.span_bug(
|
self.tcx.sess.span_bug(
|
||||||
move_span,
|
move_expr.span,
|
||||||
fmt!("illegal reader (%?) for `%?`",
|
fmt!("illegal reader (%?) for `%?`",
|
||||||
lnk, vk));
|
lnk, vk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.report_illegal_read(move_span, lnk, var, MovedValue);
|
match move_expr.node {
|
||||||
self.tcx.sess.span_note(
|
expr_fn(*) | expr_fn_block(*) => {
|
||||||
move_span, ~"move of value occurred here");
|
self.report_illegal_read(
|
||||||
|
move_expr.span, lnk, var, MovedValue);
|
||||||
|
let name = self.ir.variable_name(var);
|
||||||
|
self.tcx.sess.span_note(
|
||||||
|
move_expr.span,
|
||||||
|
fmt!("`%s` moved into closure environment here \
|
||||||
|
because its type is moved by default",
|
||||||
|
name));
|
||||||
|
}
|
||||||
|
expr_path(*) => {
|
||||||
|
self.report_illegal_read(
|
||||||
|
move_expr.span, lnk, var, MovedValue);
|
||||||
|
self.report_move_location(
|
||||||
|
move_expr, var, "", "it");
|
||||||
|
}
|
||||||
|
expr_field(*) => {
|
||||||
|
self.report_illegal_read(
|
||||||
|
move_expr.span, lnk, var, PartiallyMovedValue);
|
||||||
|
self.report_move_location(
|
||||||
|
move_expr, var, "field of ", "the field");
|
||||||
|
}
|
||||||
|
expr_index(*) => {
|
||||||
|
self.report_illegal_read(
|
||||||
|
move_expr.span, lnk, var, PartiallyMovedValue);
|
||||||
|
self.report_move_location(
|
||||||
|
move_expr, var, "element of ", "the element");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.report_illegal_read(
|
||||||
|
move_expr.span, lnk, var, PartiallyMovedValue);
|
||||||
|
self.report_move_location(
|
||||||
|
move_expr, var, "subcomponent of ", "the subcomponent");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report_move_location(move_expr: @expr,
|
||||||
|
var: Variable,
|
||||||
|
expr_descr: &str,
|
||||||
|
pronoun: &str)
|
||||||
|
{
|
||||||
|
let move_expr_ty = ty::expr_ty(self.tcx, move_expr);
|
||||||
|
let name = self.ir.variable_name(var);
|
||||||
|
self.tcx.sess.span_note(
|
||||||
|
move_expr.span,
|
||||||
|
fmt!("%s`%s` moved here because %s has type %s, \
|
||||||
|
which is moved by default (use `copy` to override)",
|
||||||
|
expr_descr, name, pronoun,
|
||||||
|
ty_to_str(self.tcx, move_expr_ty)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_illegal_read(chk_span: span,
|
fn report_illegal_read(chk_span: span,
|
||||||
|
@ -1845,13 +1831,13 @@ impl @Liveness {
|
||||||
var: Variable,
|
var: Variable,
|
||||||
rk: ReadKind) {
|
rk: ReadKind) {
|
||||||
let msg = match rk {
|
let msg = match rk {
|
||||||
PossiblyUninitializedVariable => {
|
PossiblyUninitializedVariable => "possibly uninitialized \
|
||||||
~"possibly uninitialized variable"
|
variable",
|
||||||
}
|
PossiblyUninitializedField => "possibly uninitialized field",
|
||||||
PossiblyUninitializedField => ~"possibly uninitialized field",
|
MovedValue => "moved value",
|
||||||
MovedValue => ~"moved value"
|
PartiallyMovedValue => "partially moved value"
|
||||||
};
|
};
|
||||||
let name = (*self.ir).variable_name(var);
|
let name = self.ir.variable_name(var);
|
||||||
match lnk {
|
match lnk {
|
||||||
FreeVarNode(span) => {
|
FreeVarNode(span) => {
|
||||||
self.tcx.sess.span_err(
|
self.tcx.sess.span_err(
|
||||||
|
@ -1863,8 +1849,7 @@ impl @Liveness {
|
||||||
span,
|
span,
|
||||||
fmt!("use of %s: `%s`", msg, name));
|
fmt!("use of %s: `%s`", msg, name));
|
||||||
}
|
}
|
||||||
ExitNode |
|
ExitNode | VarDefNode(_) => {
|
||||||
VarDefNode(_) => {
|
|
||||||
self.tcx.sess.span_bug(
|
self.tcx.sess.span_bug(
|
||||||
chk_span,
|
chk_span,
|
||||||
fmt!("illegal reader: %?", lnk));
|
fmt!("illegal reader: %?", lnk));
|
||||||
|
@ -1873,7 +1858,7 @@ impl @Liveness {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_warn(var: Variable) -> Option<~str> {
|
fn should_warn(var: Variable) -> Option<~str> {
|
||||||
let name = (*self.ir).variable_name(var);
|
let name = self.ir.variable_name(var);
|
||||||
if name[0] == ('_' as u8) {None} else {Some(name)}
|
if name[0] == ('_' as u8) {None} else {Some(name)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -394,7 +394,7 @@ pub impl &mem_categorization_ctxt {
|
||||||
ast::expr_block(*) | ast::expr_loop(*) | ast::expr_match(*) |
|
ast::expr_block(*) | ast::expr_loop(*) | ast::expr_match(*) |
|
||||||
ast::expr_lit(*) | ast::expr_break(*) | ast::expr_mac(*) |
|
ast::expr_lit(*) | ast::expr_break(*) | ast::expr_mac(*) |
|
||||||
ast::expr_again(*) | ast::expr_rec(*) | ast::expr_struct(*) |
|
ast::expr_again(*) | ast::expr_rec(*) | ast::expr_struct(*) |
|
||||||
ast::expr_unary_move(*) | ast::expr_repeat(*) => {
|
ast::expr_repeat(*) => {
|
||||||
return self.cat_rvalue(expr, expr_ty);
|
return self.cat_rvalue(expr, expr_ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,20 +430,16 @@ pub impl &mem_categorization_ctxt {
|
||||||
// lp: loan path, must be none for aliasable things
|
// lp: loan path, must be none for aliasable things
|
||||||
let m = if mutbl {m_mutbl} else {m_imm};
|
let m = if mutbl {m_mutbl} else {m_imm};
|
||||||
let lp = match ty::resolved_mode(self.tcx, mode) {
|
let lp = match ty::resolved_mode(self.tcx, mode) {
|
||||||
ast::by_move | ast::by_copy => {
|
ast::by_copy => Some(@lp_arg(vid)),
|
||||||
Some(@lp_arg(vid))
|
ast::by_ref => None,
|
||||||
}
|
ast::by_val => {
|
||||||
ast::by_ref => {
|
// by-value is this hybrid mode where we have a
|
||||||
None
|
// pointer but we do not own it. This is not
|
||||||
}
|
// considered loanable because, for example, a by-ref
|
||||||
ast::by_val => {
|
// and and by-val argument might both actually contain
|
||||||
// by-value is this hybrid mode where we have a
|
// the same unique ptr.
|
||||||
// pointer but we do not own it. This is not
|
None
|
||||||
// considered loanable because, for example, a by-ref
|
}
|
||||||
// and and by-val argument might both actually contain
|
|
||||||
// the same unique ptr.
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@cmt_ {
|
@cmt_ {
|
||||||
id:id,
|
id:id,
|
||||||
|
|
|
@ -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 core::prelude::*;
|
||||||
|
|
||||||
use middle::resolve;
|
use middle::resolve;
|
||||||
use middle::ty::{CopyValue, MoveValue, ReadValue};
|
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
|
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
|
@ -94,30 +93,3 @@ pub fn pat_binding_ids(dm: resolve::DefMap, pat: @pat) -> ~[node_id] {
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arms_have_by_move_bindings(tcx: ty::ctxt, +arms: &[arm]) -> bool {
|
|
||||||
for arms.each |arm| {
|
|
||||||
for arm.pats.each |pat| {
|
|
||||||
let mut found = false;
|
|
||||||
do pat_bindings(tcx.def_map, *pat)
|
|
||||||
|binding_mode, node_id, span, _path| {
|
|
||||||
match binding_mode {
|
|
||||||
bind_by_move => found = true,
|
|
||||||
bind_infer => {
|
|
||||||
match tcx.value_modes.find(node_id) {
|
|
||||||
Some(MoveValue) => found = true,
|
|
||||||
Some(CopyValue) | Some(ReadValue) => {}
|
|
||||||
None => {
|
|
||||||
tcx.sess.span_bug(span, ~"pat binding not in \
|
|
||||||
value mode map");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bind_by_ref(*) | bind_by_value => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if found { return true; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -290,15 +290,6 @@ pub fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
|
||||||
cx.sess.intr()));
|
cx.sess.intr()));
|
||||||
new_cx.parent = Some(expr.id);
|
new_cx.parent = Some(expr.id);
|
||||||
}
|
}
|
||||||
ast::expr_fn(_, _, _, cap_clause) |
|
|
||||||
ast::expr_fn_block(_, _, cap_clause) => {
|
|
||||||
// although the capture items are not expressions per se, they
|
|
||||||
// do get "evaluated" in some sense as copies or moves of the
|
|
||||||
// relevant variables so we parent them like an expression
|
|
||||||
for (*cap_clause).each |cap_item| {
|
|
||||||
record_parent(new_cx, cap_item.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::expr_while(cond, _) => {
|
ast::expr_while(cond, _) => {
|
||||||
new_cx.root_exprs.insert(cond.id, ());
|
new_cx.root_exprs.insert(cond.id, ());
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ use core::cmp;
|
||||||
use core::str;
|
use core::str;
|
||||||
use core::vec;
|
use core::vec;
|
||||||
use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
|
use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
|
||||||
use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk, capture_clause};
|
use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk};
|
||||||
use syntax::ast::{bind_by_value, bind_infer, bind_by_ref, bind_by_move};
|
use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy};
|
||||||
use syntax::ast::{crate, crate_num, decl_item, def, def_arg, def_binding};
|
use syntax::ast::{crate, crate_num, decl_item, def, def_arg, def_binding};
|
||||||
use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label};
|
use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label};
|
||||||
use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self};
|
use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self};
|
||||||
|
@ -175,11 +175,6 @@ pub enum SelfBinding {
|
||||||
HasSelfBinding(node_id, bool /* is implicit */)
|
HasSelfBinding(node_id, bool /* is implicit */)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CaptureClause {
|
|
||||||
NoCaptureClause,
|
|
||||||
HasCaptureClause(capture_clause)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ResolveVisitor = vt<()>;
|
pub type ResolveVisitor = vt<()>;
|
||||||
|
|
||||||
#[deriving_eq]
|
#[deriving_eq]
|
||||||
|
@ -3727,7 +3722,6 @@ pub impl Resolver {
|
||||||
OpaqueFunctionRibKind),
|
OpaqueFunctionRibKind),
|
||||||
(*block),
|
(*block),
|
||||||
NoSelfBinding,
|
NoSelfBinding,
|
||||||
NoCaptureClause,
|
|
||||||
visitor);
|
visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3803,33 +3797,7 @@ pub impl Resolver {
|
||||||
type_parameters: TypeParameters,
|
type_parameters: TypeParameters,
|
||||||
block: blk,
|
block: blk,
|
||||||
self_binding: SelfBinding,
|
self_binding: SelfBinding,
|
||||||
capture_clause: CaptureClause,
|
|
||||||
visitor: ResolveVisitor) {
|
visitor: ResolveVisitor) {
|
||||||
// Check each element of the capture clause.
|
|
||||||
match capture_clause {
|
|
||||||
NoCaptureClause => {
|
|
||||||
// Nothing to do.
|
|
||||||
}
|
|
||||||
HasCaptureClause(capture_clause) => {
|
|
||||||
// Resolve each captured item.
|
|
||||||
for (*capture_clause).each |capture_item| {
|
|
||||||
match self.resolve_identifier(capture_item.name,
|
|
||||||
ValueNS,
|
|
||||||
true,
|
|
||||||
capture_item.span) {
|
|
||||||
None => {
|
|
||||||
self.session.span_err(capture_item.span,
|
|
||||||
~"unresolved name in \
|
|
||||||
capture clause");
|
|
||||||
}
|
|
||||||
Some(def) => {
|
|
||||||
self.record_def(capture_item.id, def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a value rib for the function.
|
// Create a value rib for the function.
|
||||||
let function_value_rib = @Rib(rib_kind);
|
let function_value_rib = @Rib(rib_kind);
|
||||||
(*self.value_ribs).push(function_value_rib);
|
(*self.value_ribs).push(function_value_rib);
|
||||||
|
@ -3945,7 +3913,6 @@ pub impl Resolver {
|
||||||
HasSelfBinding
|
HasSelfBinding
|
||||||
((*destructor).node.self_id,
|
((*destructor).node.self_id,
|
||||||
true),
|
true),
|
||||||
NoCaptureClause,
|
|
||||||
visitor);
|
visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3976,7 +3943,6 @@ pub impl Resolver {
|
||||||
type_parameters,
|
type_parameters,
|
||||||
method.body,
|
method.body,
|
||||||
self_binding,
|
self_binding,
|
||||||
NoCaptureClause,
|
|
||||||
visitor);
|
visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4047,7 +4013,6 @@ pub impl Resolver {
|
||||||
NormalRibKind),
|
NormalRibKind),
|
||||||
method.body,
|
method.body,
|
||||||
HasSelfBinding(method.self_id),
|
HasSelfBinding(method.self_id),
|
||||||
NoCaptureClause,
|
|
||||||
visitor);
|
visitor);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
@ -4855,14 +4820,13 @@ pub impl Resolver {
|
||||||
visit_expr(expr, (), visitor);
|
visit_expr(expr, (), visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_fn(_, ref fn_decl, ref block, capture_clause) |
|
expr_fn(_, ref fn_decl, ref block) |
|
||||||
expr_fn_block(ref fn_decl, ref block, capture_clause) => {
|
expr_fn_block(ref fn_decl, ref block) => {
|
||||||
self.resolve_function(FunctionRibKind(expr.id, block.node.id),
|
self.resolve_function(FunctionRibKind(expr.id, block.node.id),
|
||||||
Some(@/*bad*/copy *fn_decl),
|
Some(@/*bad*/copy *fn_decl),
|
||||||
NoTypeParameters,
|
NoTypeParameters,
|
||||||
(*block),
|
(*block),
|
||||||
NoSelfBinding,
|
NoSelfBinding,
|
||||||
HasCaptureClause(capture_clause),
|
|
||||||
visitor);
|
visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5139,18 +5103,12 @@ pub impl Resolver {
|
||||||
descr: &str) {
|
descr: &str) {
|
||||||
match pat_binding_mode {
|
match pat_binding_mode {
|
||||||
bind_infer => {}
|
bind_infer => {}
|
||||||
bind_by_value => {
|
bind_by_copy => {
|
||||||
self.session.span_err(
|
self.session.span_err(
|
||||||
pat.span,
|
pat.span,
|
||||||
fmt!("cannot use `copy` binding mode with %s",
|
fmt!("cannot use `copy` binding mode with %s",
|
||||||
descr));
|
descr));
|
||||||
}
|
}
|
||||||
bind_by_move => {
|
|
||||||
self.session.span_err(
|
|
||||||
pat.span,
|
|
||||||
fmt!("cannot use `move` binding mode with %s",
|
|
||||||
descr));
|
|
||||||
}
|
|
||||||
bind_by_ref(*) => {
|
bind_by_ref(*) => {
|
||||||
self.session.span_err(
|
self.session.span_err(
|
||||||
pat.span,
|
pat.span,
|
||||||
|
|
|
@ -160,7 +160,6 @@ use middle::trans::datum::*;
|
||||||
use middle::trans::expr::Dest;
|
use middle::trans::expr::Dest;
|
||||||
use middle::trans::expr;
|
use middle::trans::expr;
|
||||||
use middle::trans::glue;
|
use middle::trans::glue;
|
||||||
use middle::ty::{CopyValue, MoveValue, ReadValue};
|
|
||||||
use util::common::indenter;
|
use util::common::indenter;
|
||||||
|
|
||||||
use core::dvec::DVec;
|
use core::dvec::DVec;
|
||||||
|
@ -935,7 +934,7 @@ pub fn root_pats_as_necessary(bcx: block,
|
||||||
// for details (look for the case covering cat_discr).
|
// for details (look for the case covering cat_discr).
|
||||||
|
|
||||||
let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
|
let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
|
||||||
mode: ByRef, source: FromLvalue};
|
mode: ByRef, source: ZeroMem};
|
||||||
bcx = datum.root(bcx, root_info);
|
bcx = datum.root(bcx, root_info);
|
||||||
// If we kept going, we'd only re-root the same value, so
|
// If we kept going, we'd only re-root the same value, so
|
||||||
// return now.
|
// return now.
|
||||||
|
@ -1091,7 +1090,7 @@ pub fn store_non_ref_bindings(bcx: block,
|
||||||
TrByValue(is_move, lldest) => {
|
TrByValue(is_move, lldest) => {
|
||||||
let llval = Load(bcx, binding_info.llmatch); // get a T*
|
let llval = Load(bcx, binding_info.llmatch); // get a T*
|
||||||
let datum = Datum {val: llval, ty: binding_info.ty,
|
let datum = Datum {val: llval, ty: binding_info.ty,
|
||||||
mode: ByRef, source: FromLvalue};
|
mode: ByRef, source: ZeroMem};
|
||||||
bcx = {
|
bcx = {
|
||||||
if is_move {
|
if is_move {
|
||||||
datum.move_to(bcx, INIT, lldest)
|
datum.move_to(bcx, INIT, lldest)
|
||||||
|
@ -1575,30 +1574,19 @@ pub fn trans_match_inner(scope_cx: block,
|
||||||
// Note that we use the names because each binding will have many ids
|
// Note that we use the names because each binding will have many ids
|
||||||
// from the various alternatives.
|
// from the various alternatives.
|
||||||
let bindings_map = HashMap();
|
let bindings_map = HashMap();
|
||||||
do pat_bindings(tcx.def_map, arm.pats[0]) |bm, p_id, s, path| {
|
do pat_bindings(tcx.def_map, arm.pats[0]) |bm, p_id, _s, path| {
|
||||||
let ident = path_to_ident(path);
|
let ident = path_to_ident(path);
|
||||||
let variable_ty = node_id_type(bcx, p_id);
|
let variable_ty = node_id_type(bcx, p_id);
|
||||||
let llvariable_ty = type_of::type_of(bcx.ccx(), variable_ty);
|
let llvariable_ty = type_of::type_of(bcx.ccx(), variable_ty);
|
||||||
|
|
||||||
let llmatch, trmode;
|
let llmatch, trmode;
|
||||||
match bm {
|
match bm {
|
||||||
ast::bind_by_value | ast::bind_by_move => {
|
ast::bind_by_copy | ast::bind_infer => {
|
||||||
// in this case, the type of the variable will be T,
|
// in this case, the final type of the variable will be T,
|
||||||
// but we need to store a *T
|
// but during matching we need to store a *T as explained
|
||||||
let is_move = (bm == ast::bind_by_move);
|
// above
|
||||||
llmatch = alloca(bcx, T_ptr(llvariable_ty));
|
let is_move =
|
||||||
trmode = TrByValue(is_move, alloca(bcx, llvariable_ty));
|
scope_cx.ccx().maps.moves_map.contains_key(p_id);
|
||||||
}
|
|
||||||
ast::bind_infer => {
|
|
||||||
// in this case also, the type of the variable will be T,
|
|
||||||
// but we need to store a *T
|
|
||||||
let is_move = match tcx.value_modes.find(p_id) {
|
|
||||||
None => {
|
|
||||||
tcx.sess.span_bug(s, ~"no value mode");
|
|
||||||
}
|
|
||||||
Some(MoveValue) => true,
|
|
||||||
Some(CopyValue) | Some(ReadValue) => false
|
|
||||||
};
|
|
||||||
llmatch = alloca(bcx, T_ptr(llvariable_ty));
|
llmatch = alloca(bcx, T_ptr(llvariable_ty));
|
||||||
trmode = TrByValue(is_move, alloca(bcx, llvariable_ty));
|
trmode = TrByValue(is_move, alloca(bcx, llvariable_ty));
|
||||||
}
|
}
|
||||||
|
@ -1657,7 +1645,8 @@ pub fn trans_match_inner(scope_cx: block,
|
||||||
arm_cxs.push(bcx);
|
arm_cxs.push(bcx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return controlflow::join_blocks(scope_cx, dvec::unwrap(move arm_cxs));
|
bcx = controlflow::join_blocks(scope_cx, dvec::unwrap(move arm_cxs));
|
||||||
|
return bcx;
|
||||||
|
|
||||||
fn mk_fail(bcx: block, sp: span, +msg: ~str,
|
fn mk_fail(bcx: block, sp: span, +msg: ~str,
|
||||||
finished: @mut Option<BasicBlockRef>) -> BasicBlockRef {
|
finished: @mut Option<BasicBlockRef>) -> BasicBlockRef {
|
||||||
|
@ -1697,7 +1686,7 @@ pub fn bind_irrefutable_pat(bcx: block,
|
||||||
if make_copy {
|
if make_copy {
|
||||||
let binding_ty = node_id_type(bcx, pat.id);
|
let binding_ty = node_id_type(bcx, pat.id);
|
||||||
let datum = Datum {val: val, ty: binding_ty,
|
let datum = Datum {val: val, ty: binding_ty,
|
||||||
mode: ByRef, source: FromRvalue};
|
mode: ByRef, source: RevokeClean};
|
||||||
let scratch = scratch_datum(bcx, binding_ty, false);
|
let scratch = scratch_datum(bcx, binding_ty, false);
|
||||||
datum.copy_to_datum(bcx, INIT, scratch);
|
datum.copy_to_datum(bcx, INIT, scratch);
|
||||||
match binding_mode {
|
match binding_mode {
|
||||||
|
|
|
@ -1676,7 +1676,7 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
|
||||||
ast::by_ref => {
|
ast::by_ref => {
|
||||||
llarg = raw_llarg;
|
llarg = raw_llarg;
|
||||||
}
|
}
|
||||||
ast::by_move | ast::by_copy => {
|
ast::by_copy => {
|
||||||
// only by value if immediate:
|
// only by value if immediate:
|
||||||
if datum::appropriate_mode(arg_ty.ty).is_by_value() {
|
if datum::appropriate_mode(arg_ty.ty).is_by_value() {
|
||||||
let alloc = alloc_ty(bcx, arg_ty.ty);
|
let alloc = alloc_ty(bcx, arg_ty.ty);
|
||||||
|
@ -2198,16 +2198,8 @@ pub fn create_main_wrapper(ccx: @crate_ctxt, _sp: span, main_llfn: ValueRef) {
|
||||||
create_entry_fn(ccx, llfn);
|
create_entry_fn(ccx, llfn);
|
||||||
|
|
||||||
fn create_main(ccx: @crate_ctxt, main_llfn: ValueRef) -> ValueRef {
|
fn create_main(ccx: @crate_ctxt, main_llfn: ValueRef) -> ValueRef {
|
||||||
let unit_ty = ty::mk_estr(ccx.tcx, ty::vstore_uniq);
|
|
||||||
let vecarg_ty: ty::arg =
|
|
||||||
arg {
|
|
||||||
mode: ast::expl(ast::by_val),
|
|
||||||
ty: ty::mk_evec(ccx.tcx,
|
|
||||||
ty::mt {ty: unit_ty, mutbl: ast::m_imm},
|
|
||||||
ty::vstore_uniq)
|
|
||||||
};
|
|
||||||
let nt = ty::mk_nil(ccx.tcx);
|
let nt = ty::mk_nil(ccx.tcx);
|
||||||
let llfty = type_of_fn(ccx, ~[vecarg_ty], nt);
|
let llfty = type_of_fn(ccx, ~[], nt);
|
||||||
let llfdecl = decl_fn(ccx.llmod, ~"_rust_main",
|
let llfdecl = decl_fn(ccx.llmod, ~"_rust_main",
|
||||||
lib::llvm::CCallConv, llfty);
|
lib::llvm::CCallConv, llfty);
|
||||||
|
|
||||||
|
@ -2953,7 +2945,7 @@ pub fn trans_crate(sess: session::Session,
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
output: &Path,
|
output: &Path,
|
||||||
emap2: resolve::ExportMap2,
|
emap2: resolve::ExportMap2,
|
||||||
maps: astencode::maps) -> (ModuleRef, link_meta) {
|
maps: astencode::Maps) -> (ModuleRef, link_meta) {
|
||||||
|
|
||||||
let symbol_hasher = @hash::default_state();
|
let symbol_hasher = @hash::default_state();
|
||||||
let link_meta =
|
let link_meta =
|
||||||
|
|
|
@ -79,7 +79,7 @@ pub fn count_insn(cx: block, category: &str) {
|
||||||
s += ~"/";
|
s += ~"/";
|
||||||
s += category;
|
s += category;
|
||||||
|
|
||||||
let n = match h.find(s) {
|
let n = match h.find(/*bad*/ copy s) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
_ => 0u
|
_ => 0u
|
||||||
};
|
};
|
||||||
|
|
|
@ -334,7 +334,7 @@ pub fn trans_method_call(in_cx: block,
|
||||||
|
|
||||||
pub fn trans_rtcall_or_lang_call(bcx: block,
|
pub fn trans_rtcall_or_lang_call(bcx: block,
|
||||||
did: ast::def_id,
|
did: ast::def_id,
|
||||||
args: ~[ValueRef],
|
args: &[ValueRef],
|
||||||
dest: expr::Dest)
|
dest: expr::Dest)
|
||||||
-> block {
|
-> block {
|
||||||
let fty = if did.crate == ast::local_crate {
|
let fty = if did.crate == ast::local_crate {
|
||||||
|
@ -351,7 +351,7 @@ pub fn trans_rtcall_or_lang_call(bcx: block,
|
||||||
|
|
||||||
pub fn trans_rtcall_or_lang_call_with_type_params(bcx: block,
|
pub fn trans_rtcall_or_lang_call_with_type_params(bcx: block,
|
||||||
did: ast::def_id,
|
did: ast::def_id,
|
||||||
args: ~[ValueRef],
|
args: &[ValueRef],
|
||||||
type_params: ~[ty::t],
|
type_params: ~[ty::t],
|
||||||
dest: expr::Dest)
|
dest: expr::Dest)
|
||||||
-> block {
|
-> block {
|
||||||
|
@ -418,11 +418,11 @@ pub fn trans_call_inner(
|
||||||
dest: expr::Dest,
|
dest: expr::Dest,
|
||||||
autoref_arg: AutorefArg) -> block {
|
autoref_arg: AutorefArg) -> block {
|
||||||
do base::with_scope(in_cx, call_info, ~"call") |cx| {
|
do base::with_scope(in_cx, call_info, ~"call") |cx| {
|
||||||
let ret_in_loop = match /*bad*/copy args {
|
let ret_in_loop = match args {
|
||||||
ArgExprs(args) => {
|
ArgExprs(args) => {
|
||||||
args.len() > 0u && match vec::last(args).node {
|
args.len() > 0u && match vec::last(args).node {
|
||||||
ast::expr_loop_body(@ast::expr {
|
ast::expr_loop_body(@ast::expr {
|
||||||
node: ast::expr_fn_block(_, ref body, _),
|
node: ast::expr_fn_block(_, ref body),
|
||||||
_
|
_
|
||||||
}) => body_contains_ret((*body)),
|
}) => body_contains_ret((*body)),
|
||||||
_ => false
|
_ => false
|
||||||
|
@ -464,7 +464,7 @@ pub fn trans_call_inner(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let args_res = trans_args(bcx, llenv, /*bad*/copy args, fn_expr_ty,
|
let args_res = trans_args(bcx, llenv, args, fn_expr_ty,
|
||||||
dest, ret_flag, autoref_arg);
|
dest, ret_flag, autoref_arg);
|
||||||
bcx = args_res.bcx;
|
bcx = args_res.bcx;
|
||||||
let mut llargs = /*bad*/copy args_res.args;
|
let mut llargs = /*bad*/copy args_res.args;
|
||||||
|
@ -520,9 +520,10 @@ pub fn trans_call_inner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub enum CallArgs {
|
pub enum CallArgs {
|
||||||
ArgExprs(~[@ast::expr]),
|
ArgExprs(&[@ast::expr]),
|
||||||
ArgVals(~[ValueRef])
|
ArgVals(&[ValueRef])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trans_args(cx: block,
|
pub fn trans_args(cx: block,
|
||||||
|
@ -625,7 +626,7 @@ pub fn trans_arg_expr(bcx: block,
|
||||||
ast::expr_loop_body(
|
ast::expr_loop_body(
|
||||||
// XXX: Bad copy.
|
// XXX: Bad copy.
|
||||||
blk@@ast::expr {
|
blk@@ast::expr {
|
||||||
node: ast::expr_fn_block(copy decl, ref body, cap),
|
node: ast::expr_fn_block(copy decl, ref body),
|
||||||
_
|
_
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
|
@ -635,12 +636,12 @@ pub fn trans_arg_expr(bcx: block,
|
||||||
let proto = ty::ty_fn_proto(arg_ty);
|
let proto = ty::ty_fn_proto(arg_ty);
|
||||||
let bcx = closure::trans_expr_fn(
|
let bcx = closure::trans_expr_fn(
|
||||||
bcx, proto, decl, /*bad*/copy *body, arg_expr.id,
|
bcx, proto, decl, /*bad*/copy *body, arg_expr.id,
|
||||||
blk.id, cap, Some(ret_flag), expr::SaveIn(scratch));
|
blk.id, Some(ret_flag), expr::SaveIn(scratch));
|
||||||
DatumBlock {bcx: bcx,
|
DatumBlock {bcx: bcx,
|
||||||
datum: Datum {val: scratch,
|
datum: Datum {val: scratch,
|
||||||
ty: scratch_ty,
|
ty: scratch_ty,
|
||||||
mode: ByRef,
|
mode: ByRef,
|
||||||
source: FromRvalue}}
|
source: RevokeClean}}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bcx.sess().impossible_case(
|
bcx.sess().impossible_case(
|
||||||
|
@ -670,34 +671,35 @@ pub fn trans_arg_expr(bcx: block,
|
||||||
} else {
|
} else {
|
||||||
// FIXME(#3548) use the adjustments table
|
// FIXME(#3548) use the adjustments table
|
||||||
match autoref_arg {
|
match autoref_arg {
|
||||||
DoAutorefArg => { val = arg_datum.to_ref_llval(bcx); }
|
DoAutorefArg => {
|
||||||
|
assert !bcx.ccx().maps.moves_map.contains_key(arg_expr.id);
|
||||||
|
val = arg_datum.to_ref_llval(bcx);
|
||||||
|
}
|
||||||
DontAutorefArg => {
|
DontAutorefArg => {
|
||||||
match arg_mode {
|
match arg_mode {
|
||||||
ast::by_ref => {
|
ast::by_ref => {
|
||||||
|
// This assertion should really be valid, but because
|
||||||
|
// the explicit self code currently passes by-ref, it
|
||||||
|
// does not hold.
|
||||||
|
//
|
||||||
|
//assert !bcx.ccx().maps.moves_map.contains_key(
|
||||||
|
// arg_expr.id);
|
||||||
val = arg_datum.to_ref_llval(bcx);
|
val = arg_datum.to_ref_llval(bcx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::by_val => {
|
ast::by_val => {
|
||||||
// NB: avoid running the take glue.
|
// NB: avoid running the take glue.
|
||||||
|
|
||||||
|
assert !bcx.ccx().maps.moves_map.contains_key(
|
||||||
|
arg_expr.id);
|
||||||
val = arg_datum.to_value_llval(bcx);
|
val = arg_datum.to_value_llval(bcx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::by_copy | ast::by_move => {
|
ast::by_copy => {
|
||||||
let scratch = scratch_datum(bcx, arg_datum.ty, false);
|
let scratch = scratch_datum(bcx, arg_datum.ty, false);
|
||||||
|
|
||||||
if arg_mode == ast::by_move {
|
arg_datum.store_to_datum(bcx, arg_expr.id,
|
||||||
// NDM---Doesn't seem like this should be
|
INIT, scratch);
|
||||||
// necessary
|
|
||||||
if !arg_datum.store_will_move() {
|
|
||||||
bcx.sess().span_bug(
|
|
||||||
arg_expr.span,
|
|
||||||
fmt!("move mode but datum will not \
|
|
||||||
store: %s",
|
|
||||||
arg_datum.to_str(bcx.ccx())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_datum.store_to_datum(bcx, INIT, scratch);
|
|
||||||
|
|
||||||
// Technically, ownership of val passes to the callee.
|
// Technically, ownership of val passes to the callee.
|
||||||
// However, we must cleanup should we fail before the
|
// However, we must cleanup should we fail before the
|
||||||
|
|
|
@ -15,12 +15,12 @@ use back::link::{mangle_internal_name_by_path_and_seq};
|
||||||
use back::link::{mangle_internal_name_by_path};
|
use back::link::{mangle_internal_name_by_path};
|
||||||
use lib::llvm::llvm;
|
use lib::llvm::llvm;
|
||||||
use lib::llvm::{ValueRef, TypeRef};
|
use lib::llvm::{ValueRef, TypeRef};
|
||||||
use middle::capture;
|
use middle::moves;
|
||||||
use middle::trans::base::*;
|
use middle::trans::base::*;
|
||||||
use middle::trans::build::*;
|
use middle::trans::build::*;
|
||||||
use middle::trans::callee;
|
use middle::trans::callee;
|
||||||
use middle::trans::common::*;
|
use middle::trans::common::*;
|
||||||
use middle::trans::datum::{Datum, INIT, ByRef, ByValue, FromLvalue};
|
use middle::trans::datum::{Datum, INIT, ByRef, ByValue, ZeroMem};
|
||||||
use middle::trans::expr;
|
use middle::trans::expr;
|
||||||
use middle::trans::glue;
|
use middle::trans::glue;
|
||||||
use middle::trans::machine;
|
use middle::trans::machine;
|
||||||
|
@ -106,7 +106,7 @@ use syntax::print::pprust::expr_to_str;
|
||||||
|
|
||||||
pub enum EnvAction {
|
pub enum EnvAction {
|
||||||
/// Copy the value from this llvm ValueRef into the environment.
|
/// Copy the value from this llvm ValueRef into the environment.
|
||||||
EnvStore,
|
EnvCopy,
|
||||||
|
|
||||||
/// Move the value from this llvm ValueRef into the environment.
|
/// Move the value from this llvm ValueRef into the environment.
|
||||||
EnvMove,
|
EnvMove,
|
||||||
|
@ -123,7 +123,7 @@ pub struct EnvValue {
|
||||||
pub impl EnvAction {
|
pub impl EnvAction {
|
||||||
fn to_str() -> ~str {
|
fn to_str() -> ~str {
|
||||||
match self {
|
match self {
|
||||||
EnvStore => ~"EnvStore",
|
EnvCopy => ~"EnvCopy",
|
||||||
EnvMove => ~"EnvMove",
|
EnvMove => ~"EnvMove",
|
||||||
EnvRef => ~"EnvRef"
|
EnvRef => ~"EnvRef"
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ pub fn mk_closure_tys(tcx: ty::ctxt,
|
||||||
// converted to ptrs.
|
// converted to ptrs.
|
||||||
let bound_tys = bound_values.map(|bv| {
|
let bound_tys = bound_values.map(|bv| {
|
||||||
match bv.action {
|
match bv.action {
|
||||||
EnvStore | EnvMove => bv.datum.ty,
|
EnvCopy | EnvMove => bv.datum.ty,
|
||||||
EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
|
EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -242,8 +242,8 @@ pub fn store_environment(bcx: block,
|
||||||
let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
|
let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
|
||||||
|
|
||||||
match bv.action {
|
match bv.action {
|
||||||
EnvStore => {
|
EnvCopy => {
|
||||||
bcx = bv.datum.store_to(bcx, INIT, bound_data);
|
bcx = bv.datum.copy_to(bcx, INIT, bound_data);
|
||||||
}
|
}
|
||||||
EnvMove => {
|
EnvMove => {
|
||||||
bcx = bv.datum.move_to(bcx, INIT, bound_data);
|
bcx = bv.datum.move_to(bcx, INIT, bound_data);
|
||||||
|
@ -264,7 +264,7 @@ pub fn store_environment(bcx: block,
|
||||||
// Given a context and a list of upvars, build a closure. This just
|
// Given a context and a list of upvars, build a closure. This just
|
||||||
// collects the upvars and packages them up for store_environment.
|
// collects the upvars and packages them up for store_environment.
|
||||||
pub fn build_closure(bcx0: block,
|
pub fn build_closure(bcx0: block,
|
||||||
cap_vars: ~[capture::capture_var],
|
cap_vars: &[moves::CaptureVar],
|
||||||
proto: ast::Proto,
|
proto: ast::Proto,
|
||||||
include_ret_handle: Option<ValueRef>) -> closure_result {
|
include_ret_handle: Option<ValueRef>) -> closure_result {
|
||||||
let _icx = bcx0.insn_ctxt("closure::build_closure");
|
let _icx = bcx0.insn_ctxt("closure::build_closure");
|
||||||
|
@ -274,27 +274,23 @@ pub fn build_closure(bcx0: block,
|
||||||
|
|
||||||
// Package up the captured upvars
|
// Package up the captured upvars
|
||||||
let mut env_vals = ~[];
|
let mut env_vals = ~[];
|
||||||
for vec::each(cap_vars) |cap_var| {
|
for cap_vars.each |cap_var| {
|
||||||
debug!("Building closure: captured variable %?", *cap_var);
|
debug!("Building closure: captured variable %?", *cap_var);
|
||||||
let datum = expr::trans_local_var(bcx, cap_var.def, None);
|
let datum = expr::trans_local_var(bcx, cap_var.def);
|
||||||
match cap_var.mode {
|
match cap_var.mode {
|
||||||
capture::cap_ref => {
|
moves::CapRef => {
|
||||||
assert proto == ast::ProtoBorrowed;
|
assert proto == ast::ProtoBorrowed;
|
||||||
env_vals.push(EnvValue {action: EnvRef,
|
env_vals.push(EnvValue {action: EnvRef,
|
||||||
datum: datum});
|
datum: datum});
|
||||||
}
|
}
|
||||||
capture::cap_copy => {
|
moves::CapCopy => {
|
||||||
env_vals.push(EnvValue {action: EnvStore,
|
env_vals.push(EnvValue {action: EnvCopy,
|
||||||
datum: datum});
|
datum: datum});
|
||||||
}
|
}
|
||||||
capture::cap_move => {
|
moves::CapMove => {
|
||||||
env_vals.push(EnvValue {action: EnvMove,
|
env_vals.push(EnvValue {action: EnvMove,
|
||||||
datum: datum});
|
datum: datum});
|
||||||
}
|
}
|
||||||
capture::cap_drop => {
|
|
||||||
bcx = datum.drop_val(bcx);
|
|
||||||
datum.cancel_clean(bcx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +299,7 @@ pub fn build_closure(bcx0: block,
|
||||||
do option::iter(&include_ret_handle) |flagptr| {
|
do option::iter(&include_ret_handle) |flagptr| {
|
||||||
// Flag indicating we have returned (a by-ref bool):
|
// Flag indicating we have returned (a by-ref bool):
|
||||||
let flag_datum = Datum {val: *flagptr, ty: ty::mk_bool(tcx),
|
let flag_datum = Datum {val: *flagptr, ty: ty::mk_bool(tcx),
|
||||||
mode: ByRef, source: FromLvalue};
|
mode: ByRef, source: ZeroMem};
|
||||||
env_vals.push(EnvValue {action: EnvRef,
|
env_vals.push(EnvValue {action: EnvRef,
|
||||||
datum: flag_datum});
|
datum: flag_datum});
|
||||||
|
|
||||||
|
@ -315,7 +311,7 @@ pub fn build_closure(bcx0: block,
|
||||||
};
|
};
|
||||||
let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil()));
|
let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil()));
|
||||||
let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx),
|
let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx),
|
||||||
mode: ByRef, source: FromLvalue};
|
mode: ByRef, source: ZeroMem};
|
||||||
env_vals.push(EnvValue {action: EnvRef,
|
env_vals.push(EnvValue {action: EnvRef,
|
||||||
datum: ret_datum});
|
datum: ret_datum});
|
||||||
}
|
}
|
||||||
|
@ -328,7 +324,7 @@ pub fn build_closure(bcx0: block,
|
||||||
// with the upvars and type descriptors.
|
// with the upvars and type descriptors.
|
||||||
pub fn load_environment(fcx: fn_ctxt,
|
pub fn load_environment(fcx: fn_ctxt,
|
||||||
cdata_ty: ty::t,
|
cdata_ty: ty::t,
|
||||||
cap_vars: ~[capture::capture_var],
|
cap_vars: &[moves::CaptureVar],
|
||||||
load_ret_handle: bool,
|
load_ret_handle: bool,
|
||||||
proto: ast::Proto) {
|
proto: ast::Proto) {
|
||||||
let _icx = fcx.insn_ctxt("closure::load_environment");
|
let _icx = fcx.insn_ctxt("closure::load_environment");
|
||||||
|
@ -354,20 +350,15 @@ pub fn load_environment(fcx: fn_ctxt,
|
||||||
|
|
||||||
// Populate the upvars from the environment.
|
// Populate the upvars from the environment.
|
||||||
let mut i = 0u;
|
let mut i = 0u;
|
||||||
for vec::each(cap_vars) |cap_var| {
|
for cap_vars.each |cap_var| {
|
||||||
match cap_var.mode {
|
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
|
||||||
capture::cap_drop => { /* ignore */ }
|
match proto {
|
||||||
_ => {
|
ast::ProtoBorrowed => { upvarptr = Load(bcx, upvarptr); }
|
||||||
let mut upvarptr = GEPi(bcx, llcdata, [0u, i]);
|
ast::ProtoBox | ast::ProtoUniq | ast::ProtoBare => {}
|
||||||
match proto {
|
|
||||||
ast::ProtoBorrowed => { upvarptr = Load(bcx, upvarptr); }
|
|
||||||
ast::ProtoBox | ast::ProtoUniq | ast::ProtoBare => {}
|
|
||||||
}
|
|
||||||
let def_id = ast_util::def_id_of_def(cap_var.def);
|
|
||||||
fcx.llupvars.insert(def_id.node, upvarptr);
|
|
||||||
i += 1u;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let def_id = ast_util::def_id_of_def(cap_var.def);
|
||||||
|
fcx.llupvars.insert(def_id.node, upvarptr);
|
||||||
|
i += 1u;
|
||||||
}
|
}
|
||||||
if load_ret_handle {
|
if load_ret_handle {
|
||||||
let flagptr = Load(bcx, GEPi(bcx, llcdata, [0u, i]));
|
let flagptr = Load(bcx, GEPi(bcx, llcdata, [0u, i]));
|
||||||
|
@ -383,9 +374,9 @@ pub fn trans_expr_fn(bcx: block,
|
||||||
+body: ast::blk,
|
+body: ast::blk,
|
||||||
outer_id: ast::node_id,
|
outer_id: ast::node_id,
|
||||||
user_id: ast::node_id,
|
user_id: ast::node_id,
|
||||||
cap_clause: ast::capture_clause,
|
|
||||||
is_loop_body: Option<Option<ValueRef>>,
|
is_loop_body: Option<Option<ValueRef>>,
|
||||||
dest: expr::Dest) -> block {
|
dest: expr::Dest) -> block
|
||||||
|
{
|
||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
* Translates the body of a closure expression.
|
* Translates the body of a closure expression.
|
||||||
|
@ -426,29 +417,24 @@ pub fn trans_expr_fn(bcx: block,
|
||||||
~"expr_fn");
|
~"expr_fn");
|
||||||
let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
|
let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
|
||||||
|
|
||||||
let trans_closure_env: &fn(ast::Proto) -> Result = |proto| {
|
|
||||||
let cap_vars = capture::compute_capture_vars(ccx.tcx, user_id, proto,
|
|
||||||
cap_clause);
|
|
||||||
let ret_handle = match is_loop_body { Some(x) => x, None => None };
|
|
||||||
// XXX: Bad copy.
|
|
||||||
let {llbox, cdata_ty, bcx} = build_closure(bcx, copy cap_vars, proto,
|
|
||||||
ret_handle);
|
|
||||||
trans_closure(ccx, /*bad*/copy sub_path, decl, /*bad*/copy body,
|
|
||||||
llfn, no_self, /*bad*/copy bcx.fcx.param_substs,
|
|
||||||
user_id, None, |fcx| {
|
|
||||||
load_environment(fcx, cdata_ty, copy cap_vars,
|
|
||||||
ret_handle.is_some(), proto);
|
|
||||||
}, |bcx| {
|
|
||||||
if is_loop_body.is_some() {
|
|
||||||
Store(bcx, C_bool(true), bcx.fcx.llretptr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
rslt(bcx, llbox)
|
|
||||||
};
|
|
||||||
|
|
||||||
let Result {bcx: bcx, val: closure} = match proto {
|
let Result {bcx: bcx, val: closure} = match proto {
|
||||||
ast::ProtoBorrowed | ast::ProtoBox | ast::ProtoUniq => {
|
ast::ProtoBorrowed | ast::ProtoBox | ast::ProtoUniq => {
|
||||||
trans_closure_env(proto)
|
let cap_vars = ccx.maps.capture_map.get(user_id);
|
||||||
|
let ret_handle = match is_loop_body {Some(x) => x,
|
||||||
|
None => None};
|
||||||
|
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, proto,
|
||||||
|
ret_handle);
|
||||||
|
trans_closure(ccx, sub_path, decl,
|
||||||
|
body, llfn, no_self,
|
||||||
|
/*bad*/ copy bcx.fcx.param_substs, user_id, None,
|
||||||
|
|fcx| load_environment(fcx, cdata_ty, cap_vars,
|
||||||
|
ret_handle.is_some(), proto),
|
||||||
|
|bcx| {
|
||||||
|
if is_loop_body.is_some() {
|
||||||
|
Store(bcx, C_bool(true), bcx.fcx.llretptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rslt(bcx, llbox)
|
||||||
}
|
}
|
||||||
ast::ProtoBare => {
|
ast::ProtoBare => {
|
||||||
trans_closure(ccx, sub_path, decl, body, llfn, no_self, None,
|
trans_closure(ccx, sub_path, decl, body, llfn, no_self, None,
|
||||||
|
|
|
@ -205,7 +205,7 @@ pub struct crate_ctxt {
|
||||||
type_short_names: HashMap<ty::t, ~str>,
|
type_short_names: HashMap<ty::t, ~str>,
|
||||||
all_llvm_symbols: Set<~str>,
|
all_llvm_symbols: Set<~str>,
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
maps: astencode::maps,
|
maps: astencode::Maps,
|
||||||
stats: stats,
|
stats: stats,
|
||||||
upcalls: @upcall::upcalls,
|
upcalls: @upcall::upcalls,
|
||||||
tydesc_type: TypeRef,
|
tydesc_type: TypeRef,
|
||||||
|
@ -1134,7 +1134,7 @@ pub fn C_u8(i: uint) -> ValueRef {
|
||||||
// our boxed-and-length-annotated strings.
|
// our boxed-and-length-annotated strings.
|
||||||
pub fn C_cstr(cx: @crate_ctxt, +s: ~str) -> ValueRef {
|
pub fn C_cstr(cx: @crate_ctxt, +s: ~str) -> ValueRef {
|
||||||
unsafe {
|
unsafe {
|
||||||
match cx.const_cstr_cache.find(s) {
|
match cx.const_cstr_cache.find(/*bad*/copy s) {
|
||||||
Some(llval) => return llval,
|
Some(llval) => return llval,
|
||||||
None => ()
|
None => ()
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,45 +41,35 @@
|
||||||
* convenient for interfacing with the various code floating around
|
* convenient for interfacing with the various code floating around
|
||||||
* that predates datums.
|
* that predates datums.
|
||||||
*
|
*
|
||||||
* # Datum sources
|
* # Datum cleanup styles
|
||||||
*
|
*
|
||||||
* Each datum carries with it an idea of its "source". This indicates
|
* Each datum carries with it an idea of how its value will be cleaned
|
||||||
* the kind of expression from which the datum originated. The source
|
* up. This is important after a move, because we need to know how to
|
||||||
* affects what happens when the datum is stored or moved.
|
* cancel the cleanup (since the value has been moved and therefore does
|
||||||
|
* not need to be freed). There are two options:
|
||||||
*
|
*
|
||||||
* There are three options:
|
* 1. `RevokeClean`: To cancel the cleanup, we invoke `revoke_clean()`.
|
||||||
|
* This is used for temporary rvalues.
|
||||||
*
|
*
|
||||||
* 1. `FromRvalue`: This value originates from some temporary rvalue.
|
* 2. `ZeroMem`: To cancel the cleanup, we zero out the memory where
|
||||||
* This is therefore the owning reference to the datum. If the
|
* the value resides. This is used for lvalues.
|
||||||
* datum is stored, then, it will be *moved* into its new home.
|
|
||||||
* Furthermore, we will not zero out the datum but rather use
|
|
||||||
* `revoke_clean()` to cancel any cleanup.
|
|
||||||
*
|
*
|
||||||
* 2. `FromLvalue`: This value originates from an lvalue. If the datum
|
* # Copying, moving, and storing
|
||||||
* is stored, it will be *copied* into its new home. If the datum
|
|
||||||
* is moved, it will be zeroed out.
|
|
||||||
*
|
*
|
||||||
* 3. `FromLastUseLvalue`: The same as FromLvalue, except that it
|
* There are three methods for moving the value into a new
|
||||||
* originates from the *last use* of an lvalue. If the datum is
|
* location:
|
||||||
* stored, then, it will be moved (and zeroed out).
|
|
||||||
*
|
*
|
||||||
* # Storing, copying, and moving
|
* - `copy_to()` will copy the value into a new location, meaning that
|
||||||
|
* the value is first mem-copied and then the new location is "taken"
|
||||||
|
* via the take glue, in effect creating a deep clone.
|
||||||
*
|
*
|
||||||
* There are three kinds of methods for moving the value into a new
|
* - `move_to()` will copy the value, meaning that the value is mem-copied
|
||||||
* location. *Storing* a datum is probably the one you want to reach
|
* into its new home and then the cleanup on the this datum is revoked.
|
||||||
* for first: it is used when you will no longer use the datum and
|
* This is a "shallow" clone. After `move_to()`, the current datum
|
||||||
* would like to place it somewhere. It may translate to a copy or a
|
* is invalid and should no longer be used.
|
||||||
* move, depending on the source of the datum. After a store, the
|
|
||||||
* datum may or may not be usable anymore, so you must assume it is
|
|
||||||
* not.
|
|
||||||
*
|
*
|
||||||
* Sometimes, though, you want to use an explicit copy or move. A
|
* - `store_to()` either performs a copy or a move by consulting the
|
||||||
* copy copies the data from the datum into a new location and
|
* moves_map computed by `middle::moves`.
|
||||||
* executes the take glue on that location, thus leaving the datum
|
|
||||||
* valid for further use. Moving, in contrast, copies the data into
|
|
||||||
* the new location and then cancels any cleanups on the current datum
|
|
||||||
* (as appropriate for the source). No glue code is executed. After
|
|
||||||
* a move, the datum is no longer usable.
|
|
||||||
*
|
*
|
||||||
* # Scratch datum
|
* # Scratch datum
|
||||||
*
|
*
|
||||||
|
@ -133,8 +123,8 @@ pub struct Datum {
|
||||||
/// How did this value originate? This is particularly important
|
/// How did this value originate? This is particularly important
|
||||||
/// if the value is MOVED or prematurely DROPPED, because it
|
/// if the value is MOVED or prematurely DROPPED, because it
|
||||||
/// describes how to cancel the cleanup that was scheduled before.
|
/// describes how to cancel the cleanup that was scheduled before.
|
||||||
/// See the def'n of the `DatumSource` type.
|
/// See the def'n of the `DatumCleanup` type.
|
||||||
source: DatumSource
|
source: DatumCleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DatumBlock {
|
pub struct DatumBlock {
|
||||||
|
@ -173,32 +163,16 @@ pub impl DatumMode: to_bytes::IterBytes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `Datum Sources` section at the head of this module.
|
/// See `Datum cleanup styles` section at the head of this module.
|
||||||
pub enum DatumSource {
|
#[deriving_eq]
|
||||||
FromRvalue,
|
pub enum DatumCleanup {
|
||||||
FromLvalue,
|
RevokeClean,
|
||||||
FromLastUseLvalue,
|
ZeroMem
|
||||||
}
|
|
||||||
|
|
||||||
pub impl DatumSource {
|
|
||||||
fn is_rvalue() -> bool {
|
|
||||||
match self {
|
|
||||||
FromRvalue => true,
|
|
||||||
FromLvalue | FromLastUseLvalue => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_any_lvalue() -> bool {
|
|
||||||
match self {
|
|
||||||
FromRvalue => false,
|
|
||||||
FromLvalue | FromLastUseLvalue => true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum {
|
pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum {
|
||||||
return Datum {val: val, ty: ty,
|
return Datum {val: val, ty: ty,
|
||||||
mode: ByValue, source: FromRvalue};
|
mode: ByValue, source: RevokeClean};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn immediate_rvalue_bcx(bcx: block,
|
pub fn immediate_rvalue_bcx(bcx: block,
|
||||||
|
@ -221,7 +195,7 @@ pub fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum {
|
||||||
|
|
||||||
let llty = type_of::type_of(bcx.ccx(), ty);
|
let llty = type_of::type_of(bcx.ccx(), ty);
|
||||||
let scratch = alloca_maybe_zeroed(bcx, llty, zero);
|
let scratch = alloca_maybe_zeroed(bcx, llty, zero);
|
||||||
Datum { val: scratch, ty: ty, mode: ByRef, source: FromRvalue }
|
Datum { val: scratch, ty: ty, mode: ByRef, source: RevokeClean }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn appropriate_mode(ty: ty::t) -> DatumMode {
|
pub fn appropriate_mode(ty: ty::t) -> DatumMode {
|
||||||
|
@ -241,42 +215,39 @@ pub fn appropriate_mode(ty: ty::t) -> DatumMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl Datum {
|
pub impl Datum {
|
||||||
fn store_will_move() -> bool {
|
fn store_to(bcx: block, id: ast::node_id,
|
||||||
match self.source {
|
action: CopyAction, dst: ValueRef) -> block {
|
||||||
FromRvalue | FromLastUseLvalue => true,
|
|
||||||
FromLvalue => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_to(bcx: block, action: CopyAction, dst: ValueRef) -> block {
|
|
||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
* Stores this value into its final home. This moves if
|
* Stores this value into its final home. This moves if
|
||||||
* possible, but copies otherwise. */
|
* `id` is located in the move table, but copies otherwise.
|
||||||
|
*/
|
||||||
|
|
||||||
if self.store_will_move() {
|
if bcx.ccx().maps.moves_map.contains_key(id) {
|
||||||
self.move_to(bcx, action, dst)
|
self.move_to(bcx, action, dst)
|
||||||
} else {
|
} else {
|
||||||
self.copy_to(bcx, action, dst)
|
self.copy_to(bcx, action, dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_to_dest(bcx: block, dest: expr::Dest) -> block {
|
fn store_to_dest(bcx: block, id: ast::node_id,
|
||||||
|
dest: expr::Dest) -> block {
|
||||||
match dest {
|
match dest {
|
||||||
expr::Ignore => {
|
expr::Ignore => {
|
||||||
return bcx;
|
return bcx;
|
||||||
}
|
}
|
||||||
expr::SaveIn(addr) => {
|
expr::SaveIn(addr) => {
|
||||||
return self.store_to(bcx, INIT, addr);
|
return self.store_to(bcx, id, INIT, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
|
fn store_to_datum(bcx: block, id: ast::node_id,
|
||||||
|
action: CopyAction, datum: Datum) -> block {
|
||||||
debug!("store_to_datum(self=%s, action=%?, datum=%s)",
|
debug!("store_to_datum(self=%s, action=%?, datum=%s)",
|
||||||
self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
|
self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
|
||||||
assert datum.mode.is_by_ref();
|
assert datum.mode.is_by_ref();
|
||||||
self.store_to(bcx, action, datum.val)
|
self.store_to(bcx, id, action, datum.val)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
|
fn move_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
|
||||||
|
@ -396,7 +367,7 @@ pub impl Datum {
|
||||||
* Schedules this datum for cleanup in `bcx`. The datum
|
* Schedules this datum for cleanup in `bcx`. The datum
|
||||||
* must be an rvalue. */
|
* must be an rvalue. */
|
||||||
|
|
||||||
assert self.source.is_rvalue();
|
assert self.source == RevokeClean;
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ByValue => {
|
ByValue => {
|
||||||
add_clean_temp_immediate(bcx, self.val, self.ty);
|
add_clean_temp_immediate(bcx, self.val, self.ty);
|
||||||
|
@ -410,10 +381,10 @@ pub impl Datum {
|
||||||
fn cancel_clean(bcx: block) {
|
fn cancel_clean(bcx: block) {
|
||||||
if ty::type_needs_drop(bcx.tcx(), self.ty) {
|
if ty::type_needs_drop(bcx.tcx(), self.ty) {
|
||||||
match self.source {
|
match self.source {
|
||||||
FromRvalue => {
|
RevokeClean => {
|
||||||
revoke_clean(bcx, self.val);
|
revoke_clean(bcx, self.val);
|
||||||
}
|
}
|
||||||
FromLvalue | FromLastUseLvalue => {
|
ZeroMem => {
|
||||||
// Lvalues which potentially need to be dropped
|
// Lvalues which potentially need to be dropped
|
||||||
// must be passed by ref, so that we can zero them
|
// must be passed by ref, so that we can zero them
|
||||||
// out.
|
// out.
|
||||||
|
@ -444,7 +415,7 @@ pub impl Datum {
|
||||||
ByValue => self,
|
ByValue => self,
|
||||||
ByRef => {
|
ByRef => {
|
||||||
Datum {val: self.to_value_llval(bcx), mode: ByValue,
|
Datum {val: self.to_value_llval(bcx), mode: ByValue,
|
||||||
ty: self.ty, source: FromRvalue}
|
ty: self.ty, source: RevokeClean}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,7 +447,7 @@ pub impl Datum {
|
||||||
ByRef => self,
|
ByRef => self,
|
||||||
ByValue => {
|
ByValue => {
|
||||||
Datum {val: self.to_ref_llval(bcx), mode: ByRef,
|
Datum {val: self.to_ref_llval(bcx), mode: ByRef,
|
||||||
ty: self.ty, source: FromRvalue}
|
ty: self.ty, source: RevokeClean}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,7 +498,7 @@ pub impl Datum {
|
||||||
fn GEPi(bcx: block,
|
fn GEPi(bcx: block,
|
||||||
ixs: &[uint],
|
ixs: &[uint],
|
||||||
ty: ty::t,
|
ty: ty::t,
|
||||||
source: DatumSource)
|
source: DatumCleanup)
|
||||||
-> Datum {
|
-> Datum {
|
||||||
let base_val = self.to_ref_llval(bcx);
|
let base_val = self.to_ref_llval(bcx);
|
||||||
Datum {
|
Datum {
|
||||||
|
@ -618,7 +589,7 @@ pub impl Datum {
|
||||||
|
|
||||||
let ptr = self.to_value_llval(bcx);
|
let ptr = self.to_value_llval(bcx);
|
||||||
let body = opaque_box_body(bcx, content_ty, ptr);
|
let body = opaque_box_body(bcx, content_ty, ptr);
|
||||||
Datum {val: body, ty: content_ty, mode: ByRef, source: FromLvalue}
|
Datum {val: body, ty: content_ty, mode: ByRef, source: ZeroMem}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_rptr(bcx: block) -> Datum {
|
fn to_rptr(bcx: block) -> Datum {
|
||||||
|
@ -636,7 +607,7 @@ pub impl Datum {
|
||||||
let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static,
|
let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static,
|
||||||
self.ty);
|
self.ty);
|
||||||
Datum {val: llval, ty: rptr_ty,
|
Datum {val: llval, ty: rptr_ty,
|
||||||
mode: ByValue, source: FromRvalue}
|
mode: ByValue, source: RevokeClean}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_deref(
|
fn try_deref(
|
||||||
|
@ -701,7 +672,7 @@ pub impl Datum {
|
||||||
val: PointerCast(bcx, self.val, llty),
|
val: PointerCast(bcx, self.val, llty),
|
||||||
ty: ty,
|
ty: ty,
|
||||||
mode: ByRef,
|
mode: ByRef,
|
||||||
source: FromLvalue
|
source: ZeroMem
|
||||||
}),
|
}),
|
||||||
bcx
|
bcx
|
||||||
)
|
)
|
||||||
|
@ -740,7 +711,7 @@ pub impl Datum {
|
||||||
val: GEPi(bcx, self.val, [0, 0, 0]),
|
val: GEPi(bcx, self.val, [0, 0, 0]),
|
||||||
ty: ty,
|
ty: ty,
|
||||||
mode: ByRef,
|
mode: ByRef,
|
||||||
source: FromLvalue
|
source: ZeroMem
|
||||||
}),
|
}),
|
||||||
bcx
|
bcx
|
||||||
)
|
)
|
||||||
|
@ -768,7 +739,7 @@ pub impl Datum {
|
||||||
val: lv.to_value_llval(bcx),
|
val: lv.to_value_llval(bcx),
|
||||||
ty: ty,
|
ty: ty,
|
||||||
mode: ByRef,
|
mode: ByRef,
|
||||||
source: FromLvalue // *p is an lvalue
|
source: ZeroMem // *p is an lvalue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -841,8 +812,9 @@ pub impl DatumBlock {
|
||||||
self.datum.drop_val(self.bcx)
|
self.datum.drop_val(self.bcx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_to(action: CopyAction, dst: ValueRef) -> block {
|
fn store_to(id: ast::node_id, action: CopyAction,
|
||||||
self.datum.store_to(self.bcx, action, dst)
|
dst: ValueRef) -> block {
|
||||||
|
self.datum.store_to(self.bcx, id, action, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_to(action: CopyAction, dst: ValueRef) -> block {
|
fn copy_to(action: CopyAction, dst: ValueRef) -> block {
|
||||||
|
|
|
@ -789,10 +789,10 @@ pub fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> {
|
||||||
}
|
}
|
||||||
ast_map::node_expr(expr) => {
|
ast_map::node_expr(expr) => {
|
||||||
match /*bad*/copy expr.node {
|
match /*bad*/copy expr.node {
|
||||||
ast::expr_fn(_, decl, _, _) => {
|
ast::expr_fn(_, decl, _) => {
|
||||||
((dbg_cx.names)(~"fn"), decl.output, expr.id)
|
((dbg_cx.names)(~"fn"), decl.output, expr.id)
|
||||||
}
|
}
|
||||||
ast::expr_fn_block(decl, _, _) => {
|
ast::expr_fn_block(decl, _) => {
|
||||||
((dbg_cx.names)(~"fn"), decl.output, expr.id)
|
((dbg_cx.names)(~"fn"), decl.output, expr.id)
|
||||||
}
|
}
|
||||||
_ => fcx.ccx.sess.span_bug(expr.span,
|
_ => fcx.ccx.sess.span_bug(expr.span,
|
||||||
|
@ -818,7 +818,7 @@ pub fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
|
let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
|
||||||
let file_node = create_file(cx, loc.file.name).node;
|
let file_node = create_file(cx, copy loc.file.name).node;
|
||||||
let ty_node = if cx.sess.opts.extra_debuginfo {
|
let ty_node = if cx.sess.opts.extra_debuginfo {
|
||||||
match ret_ty.node {
|
match ret_ty.node {
|
||||||
ast::ty_nil => llnull(),
|
ast::ty_nil => llnull(),
|
||||||
|
|
|
@ -12,45 +12,53 @@
|
||||||
|
|
||||||
# Translation of expressions.
|
# Translation of expressions.
|
||||||
|
|
||||||
## User's guide
|
## Recommended entry point
|
||||||
|
|
||||||
If you wish to translate an expression, there are two basic modes:
|
If you wish to translate an expression, the preferred way to do
|
||||||
|
so is to use:
|
||||||
|
|
||||||
1. `trans_into(block, expr, Dest) -> block`
|
expr::trans_into(block, expr, Dest) -> block
|
||||||
2. `trans_to_datum(block, expr) -> DatumBlock`
|
|
||||||
|
|
||||||
`trans_into()` is the preferred form to use whenever possible. It
|
This will generate code that evaluates `expr`, storing the result into
|
||||||
evaluates the expression and stores its result into `Dest`, which
|
`Dest`, which must either be the special flag ignore (throw the result
|
||||||
must either be the special flag ignore (throw the result away) or
|
away) or be a pointer to memory of the same type/size as the
|
||||||
be a pointer to memory of the same type/size as the expression.
|
expression. It returns the resulting basic block. This form will
|
||||||
|
handle all automatic adjustments and moves for you.
|
||||||
|
|
||||||
Sometimes, though, you just want to evaluate the expression into
|
## Translation to a datum
|
||||||
some memory location so you can go and inspect it (e.g., a `match`
|
|
||||||
expression). In that case, `trans_to_datum()` is your friend. It
|
|
||||||
will evaluate the expression and return a `Datum` describing where
|
|
||||||
the result is to be found. This function tries to return its
|
|
||||||
result in the most efficient way possible, without introducing
|
|
||||||
extra copies or sacrificing information. Therefore, for lvalue
|
|
||||||
expressions, you always get a by-ref `Datum` in return that points
|
|
||||||
at the memory for this lvalue (almost, see [1]). For rvalue
|
|
||||||
expressions, we will return a by-value `Datum` whenever possible,
|
|
||||||
but it is often necessary to allocate a stack slot, store the
|
|
||||||
result of the rvalue in there, and then return a pointer to the
|
|
||||||
slot (see the discussion later on about the different kinds of
|
|
||||||
rvalues).
|
|
||||||
|
|
||||||
## More specific functions
|
In some cases, `trans_into()` is too narrow of an interface.
|
||||||
|
Generally this occurs either when you know that the result value is
|
||||||
|
going to be a scalar, or when you need to evaluate the expression into
|
||||||
|
some memory location so you can go and inspect it (e.g., assignments,
|
||||||
|
`match` expressions, the `&` operator).
|
||||||
|
|
||||||
The two functions above are the most general and can handle any
|
In such cases, you want the following function:
|
||||||
situation, but there are a few other functions that are useful
|
|
||||||
in specific scenarios:
|
|
||||||
|
|
||||||
- `trans_lvalue()` is exactly like `trans_to_datum()` but it only
|
trans_to_datum(block, expr) -> DatumBlock
|
||||||
works on lvalues. This is mostly used as an assertion for those
|
|
||||||
places where only an lvalue is expected. It also guarantees that
|
This function generates code to evaluate the expression and return a
|
||||||
you will get a by-ref Datum back (almost, see [1]).
|
`Datum` describing where the result is to be found. This function
|
||||||
- `trans_local_var()` can be used to trans a ref to a local variable
|
tries to return its result in the most efficient way possible, without
|
||||||
that is not an expression.
|
introducing extra copies or sacrificing information. Therefore, for
|
||||||
|
lvalue expressions, you always get a by-ref `Datum` in return that
|
||||||
|
points at the memory for this lvalue (almost, see [1]). For rvalue
|
||||||
|
expressions, we will return a by-value `Datum` whenever possible, but
|
||||||
|
it is often necessary to allocate a stack slot, store the result of
|
||||||
|
the rvalue in there, and then return a pointer to the slot (see the
|
||||||
|
discussion later on about the different kinds of rvalues).
|
||||||
|
|
||||||
|
NB: The `trans_to_datum()` function does perform adjustments, but
|
||||||
|
since it returns a pointer to the value "in place" it does not handle
|
||||||
|
any moves that may be relevant. If you are transing an expression
|
||||||
|
whose result should be moved, you should either use the Datum methods
|
||||||
|
`move_to()` (for unconditional moves) or `store_to()` (for moves
|
||||||
|
conditioned on the type of the expression) at some point.
|
||||||
|
|
||||||
|
## Translating local variables
|
||||||
|
|
||||||
|
`trans_local_var()` can be used to trans a ref to a local variable
|
||||||
|
that is not an expression. This is needed for captures.
|
||||||
|
|
||||||
## Ownership and cleanups
|
## Ownership and cleanups
|
||||||
|
|
||||||
|
@ -127,7 +135,6 @@ use middle::trans::datum::*;
|
||||||
use middle::trans::machine;
|
use middle::trans::machine;
|
||||||
use middle::trans::meth;
|
use middle::trans::meth;
|
||||||
use middle::trans::tvec;
|
use middle::trans::tvec;
|
||||||
use middle::ty::MoveValue;
|
|
||||||
use middle::ty::struct_mutable_fields;
|
use middle::ty::struct_mutable_fields;
|
||||||
use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn};
|
use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn};
|
||||||
use util::common::indenter;
|
use util::common::indenter;
|
||||||
|
@ -258,21 +265,70 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
||||||
return match bcx.tcx().adjustments.find(expr.id) {
|
if bcx.tcx().adjustments.contains_key(expr.id) {
|
||||||
None => trans_into_unadjusted(bcx, expr, dest),
|
// use trans_to_datum, which is mildly less efficient but
|
||||||
Some(_) => {
|
// which will perform the adjustments:
|
||||||
// use trans_to_datum, which is mildly less efficient but
|
let datumblock = trans_to_datum(bcx, expr);
|
||||||
// which will perform the adjustments:
|
return match dest {
|
||||||
let datumblock = trans_to_datum(bcx, expr);
|
Ignore => datumblock.bcx,
|
||||||
|
SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = expr_ty(bcx, expr);
|
||||||
|
|
||||||
|
debug!("trans_into_unadjusted(expr=%s, dest=%s)",
|
||||||
|
bcx.expr_to_str(expr),
|
||||||
|
dest.to_str(bcx.ccx()));
|
||||||
|
let _indenter = indenter();
|
||||||
|
|
||||||
|
debuginfo::update_source_pos(bcx, expr.span);
|
||||||
|
|
||||||
|
let dest = {
|
||||||
|
if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
|
||||||
|
Ignore
|
||||||
|
} else {
|
||||||
|
dest
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let kind = bcx.expr_kind(expr);
|
||||||
|
debug!("expr kind = %?", kind);
|
||||||
|
return match kind {
|
||||||
|
ty::LvalueExpr => {
|
||||||
|
let datumblock = trans_lvalue_unadjusted(bcx, expr);
|
||||||
match dest {
|
match dest {
|
||||||
Ignore => datumblock.bcx,
|
Ignore => datumblock.bcx,
|
||||||
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
|
SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ty::RvalueDatumExpr => {
|
||||||
|
let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
|
||||||
|
match dest {
|
||||||
|
Ignore => datumblock.drop_val(),
|
||||||
|
|
||||||
|
// NB: We always do `move_to()` regardless of the
|
||||||
|
// moves_map because we're processing an rvalue
|
||||||
|
SaveIn(lldest) => datumblock.move_to(INIT, lldest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::RvalueDpsExpr => {
|
||||||
|
trans_rvalue_dps_unadjusted(bcx, expr, dest)
|
||||||
|
}
|
||||||
|
ty::RvalueStmtExpr => {
|
||||||
|
trans_rvalue_stmt_unadjusted(bcx, expr)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
|
fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
* Translates an lvalue expression, always yielding a by-ref
|
||||||
|
* datum. Generally speaking you should call trans_to_datum()
|
||||||
|
* instead, but sometimes we call trans_lvalue() directly as a
|
||||||
|
* means of asserting that a particular expression is an lvalue. */
|
||||||
|
|
||||||
return match bcx.tcx().adjustments.find(expr.id) {
|
return match bcx.tcx().adjustments.find(expr.id) {
|
||||||
None => trans_lvalue_unadjusted(bcx, expr),
|
None => trans_lvalue_unadjusted(bcx, expr),
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
|
@ -350,50 +406,6 @@ fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trans_into_unadjusted(bcx: block, expr: @ast::expr, dest: Dest) -> block {
|
|
||||||
let ty = expr_ty(bcx, expr);
|
|
||||||
|
|
||||||
debug!("trans_into_unadjusted(expr=%s, dest=%s)",
|
|
||||||
bcx.expr_to_str(expr),
|
|
||||||
dest.to_str(bcx.ccx()));
|
|
||||||
let _indenter = indenter();
|
|
||||||
|
|
||||||
debuginfo::update_source_pos(bcx, expr.span);
|
|
||||||
|
|
||||||
let dest = {
|
|
||||||
if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
|
|
||||||
Ignore
|
|
||||||
} else {
|
|
||||||
dest
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let kind = bcx.expr_kind(expr);
|
|
||||||
debug!("expr kind = %?", kind);
|
|
||||||
match kind {
|
|
||||||
ty::LvalueExpr => {
|
|
||||||
let datumblock = trans_lvalue_unadjusted(bcx, expr);
|
|
||||||
match dest {
|
|
||||||
Ignore => datumblock.bcx,
|
|
||||||
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ty::RvalueDatumExpr => {
|
|
||||||
let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
|
|
||||||
match dest {
|
|
||||||
Ignore => datumblock.drop_val(),
|
|
||||||
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ty::RvalueDpsExpr => {
|
|
||||||
return trans_rvalue_dps_unadjusted(bcx, expr, dest);
|
|
||||||
}
|
|
||||||
ty::RvalueStmtExpr => {
|
|
||||||
return trans_rvalue_stmt_unadjusted(bcx, expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
|
let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
|
||||||
|
|
||||||
|
@ -472,9 +484,12 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
|
||||||
return controlflow::trans_loop(bcx, (*body), opt_label);
|
return controlflow::trans_loop(bcx, (*body), opt_label);
|
||||||
}
|
}
|
||||||
ast::expr_assign(dst, src) => {
|
ast::expr_assign(dst, src) => {
|
||||||
let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src));
|
let src_datum = unpack_datum!(
|
||||||
let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
|
bcx, trans_to_datum(bcx, src));
|
||||||
return src_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum);
|
let dst_datum = unpack_datum!(
|
||||||
|
bcx, trans_lvalue(bcx, dst));
|
||||||
|
return src_datum.store_to_datum(
|
||||||
|
bcx, src.id, DROP_EXISTING, dst_datum);
|
||||||
}
|
}
|
||||||
ast::expr_swap(dst, src) => {
|
ast::expr_swap(dst, src) => {
|
||||||
let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
|
let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
|
||||||
|
@ -509,8 +524,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
|
|
||||||
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
|
trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
|
||||||
|
|
||||||
// XXX: This copy is really bad.
|
match expr.node {
|
||||||
match /*bad*/copy expr.node {
|
|
||||||
ast::expr_paren(e) => {
|
ast::expr_paren(e) => {
|
||||||
return trans_rvalue_dps_unadjusted(bcx, e, dest);
|
return trans_rvalue_dps_unadjusted(bcx, e, dest);
|
||||||
}
|
}
|
||||||
|
@ -519,14 +533,14 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
bcx.def(expr.id), dest);
|
bcx.def(expr.id), dest);
|
||||||
}
|
}
|
||||||
ast::expr_if(cond, ref thn, els) => {
|
ast::expr_if(cond, ref thn, els) => {
|
||||||
return controlflow::trans_if(bcx, cond, (*thn), els, dest);
|
return controlflow::trans_if(bcx, cond, *thn, els, dest);
|
||||||
}
|
}
|
||||||
ast::expr_match(discr, ref arms) => {
|
ast::expr_match(discr, ref arms) => {
|
||||||
return _match::trans_match(bcx, expr, discr, /*bad*/copy *arms,
|
return _match::trans_match(bcx, expr, discr, /*bad*/copy *arms,
|
||||||
dest);
|
dest);
|
||||||
}
|
}
|
||||||
ast::expr_block(ref blk) => {
|
ast::expr_block(ref blk) => {
|
||||||
return do base::with_scope(bcx, (*blk).info(),
|
return do base::with_scope(bcx, blk.info(),
|
||||||
~"block-expr body") |bcx| {
|
~"block-expr body") |bcx| {
|
||||||
controlflow::trans_block(bcx, (*blk), dest)
|
controlflow::trans_block(bcx, (*blk), dest)
|
||||||
};
|
};
|
||||||
|
@ -535,8 +549,8 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
ast::expr_struct(_, ref fields, base) => {
|
ast::expr_struct(_, ref fields, base) => {
|
||||||
return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest);
|
return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest);
|
||||||
}
|
}
|
||||||
ast::expr_tup(args) => {
|
ast::expr_tup(ref args) => {
|
||||||
return trans_tup(bcx, args, dest);
|
return trans_tup(bcx, *args, dest);
|
||||||
}
|
}
|
||||||
ast::expr_lit(@ast::spanned {node: ast::lit_str(s), _}) => {
|
ast::expr_lit(@ast::spanned {node: ast::lit_str(s), _}) => {
|
||||||
return tvec::trans_lit_str(bcx, expr, s, dest);
|
return tvec::trans_lit_str(bcx, expr, s, dest);
|
||||||
|
@ -552,15 +566,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
return tvec::trans_fixed_vstore(bcx, expr, expr, dest);
|
return tvec::trans_fixed_vstore(bcx, expr, expr, dest);
|
||||||
}
|
}
|
||||||
// XXX: Bad copy.
|
// XXX: Bad copy.
|
||||||
ast::expr_fn(proto, copy decl, ref body, cap_clause) => {
|
ast::expr_fn(proto, copy decl, ref body) => {
|
||||||
// Don't use this function for anything real. Use the one in
|
// Don't use this function for anything real. Use the one in
|
||||||
// astconv instead.
|
// astconv instead.
|
||||||
return closure::trans_expr_fn(bcx, proto, decl,
|
return closure::trans_expr_fn(bcx, proto, decl,
|
||||||
/*bad*/copy *body,
|
/*bad*/copy *body,
|
||||||
expr.id, expr.id,
|
expr.id, expr.id,
|
||||||
cap_clause, None, dest);
|
None, dest);
|
||||||
}
|
}
|
||||||
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
|
ast::expr_fn_block(ref decl, ref body) => {
|
||||||
let expr_ty = expr_ty(bcx, expr);
|
let expr_ty = expr_ty(bcx, expr);
|
||||||
match ty::get(expr_ty).sty {
|
match ty::get(expr_ty).sty {
|
||||||
ty::ty_fn(ref fn_ty) => {
|
ty::ty_fn(ref fn_ty) => {
|
||||||
|
@ -570,7 +584,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
return closure::trans_expr_fn(
|
return closure::trans_expr_fn(
|
||||||
bcx, fn_ty.meta.proto, /*bad*/copy *decl,
|
bcx, fn_ty.meta.proto, /*bad*/copy *decl,
|
||||||
/*bad*/copy *body, expr.id, expr.id,
|
/*bad*/copy *body, expr.id, expr.id,
|
||||||
cap_clause, None, dest);
|
None, dest);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bcx.sess().impossible_case(
|
bcx.sess().impossible_case(
|
||||||
|
@ -582,7 +596,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
match ty::get(expr_ty(bcx, expr)).sty {
|
match ty::get(expr_ty(bcx, expr)).sty {
|
||||||
ty::ty_fn(ref fn_ty) => {
|
ty::ty_fn(ref fn_ty) => {
|
||||||
match blk.node {
|
match blk.node {
|
||||||
ast::expr_fn_block(copy decl, ref body, cap) => {
|
ast::expr_fn_block(copy decl, ref body) => {
|
||||||
return closure::trans_expr_fn(
|
return closure::trans_expr_fn(
|
||||||
bcx,
|
bcx,
|
||||||
fn_ty.meta.proto,
|
fn_ty.meta.proto,
|
||||||
|
@ -590,7 +604,6 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
/*bad*/copy *body,
|
/*bad*/copy *body,
|
||||||
expr.id,
|
expr.id,
|
||||||
blk.id,
|
blk.id,
|
||||||
cap,
|
|
||||||
Some(None),
|
Some(None),
|
||||||
dest);
|
dest);
|
||||||
}
|
}
|
||||||
|
@ -613,26 +626,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
ast::expr_copy(a) => {
|
ast::expr_copy(a) => {
|
||||||
return trans_into(bcx, a, dest);
|
return trans_into(bcx, a, dest);
|
||||||
}
|
}
|
||||||
ast::expr_unary_move(a) => {
|
ast::expr_call(f, ref args, _) => {
|
||||||
if bcx.expr_is_lval(a) {
|
|
||||||
let datum = unpack_datum!(bcx, trans_to_datum(bcx, a));
|
|
||||||
return match dest {
|
|
||||||
Ignore => drop_and_cancel_clean(bcx, datum),
|
|
||||||
SaveIn(addr) => datum.move_to(bcx, INIT, addr)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return trans_into(bcx, a, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::expr_call(f, args, _) => {
|
|
||||||
return callee::trans_call(
|
return callee::trans_call(
|
||||||
bcx, expr, f, callee::ArgExprs(args), expr.id, dest);
|
bcx, expr, f, callee::ArgExprs(*args), expr.id, dest);
|
||||||
}
|
}
|
||||||
ast::expr_method_call(rcvr, _, _, args, _) => {
|
ast::expr_method_call(rcvr, _, _, ref args, _) => {
|
||||||
return callee::trans_method_call(bcx,
|
return callee::trans_method_call(bcx,
|
||||||
expr,
|
expr,
|
||||||
rcvr,
|
rcvr,
|
||||||
callee::ArgExprs(args),
|
callee::ArgExprs(*args),
|
||||||
dest);
|
dest);
|
||||||
}
|
}
|
||||||
ast::expr_binary(_, lhs, rhs) => {
|
ast::expr_binary(_, lhs, rhs) => {
|
||||||
|
@ -727,9 +729,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
* Translates an lvalue expression, always yielding a by-ref
|
* Translates an lvalue expression, always yielding a by-ref
|
||||||
* datum. Generally speaking you should call trans_to_datum()
|
* datum. Does not apply any adjustments. */
|
||||||
* instead, but sometimes we call trans_lvalue() directly as a
|
|
||||||
* means of asserting that a particular expression is an lvalue. */
|
|
||||||
|
|
||||||
let _icx = bcx.insn_ctxt("trans_lval");
|
let _icx = bcx.insn_ctxt("trans_lval");
|
||||||
let mut bcx = bcx;
|
let mut bcx = bcx;
|
||||||
|
@ -752,6 +752,17 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
return DatumBlock {bcx: bcx, datum: unrooted_datum};
|
return DatumBlock {bcx: bcx, datum: unrooted_datum};
|
||||||
|
|
||||||
fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
* Translates `expr`. Note that this version generally
|
||||||
|
* yields an unrooted, unmoved version. Rooting and possible
|
||||||
|
* moves are dealt with above in trans_lvalue_unadjusted().
|
||||||
|
*
|
||||||
|
* One exception is if `expr` refers to a local variable,
|
||||||
|
* in which case the source may already be FromMovedLvalue
|
||||||
|
* if appropriate.
|
||||||
|
*/
|
||||||
|
|
||||||
let mut bcx = bcx;
|
let mut bcx = bcx;
|
||||||
|
|
||||||
match expr.node {
|
match expr.node {
|
||||||
|
@ -762,7 +773,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
return trans_def_lvalue(bcx, expr, bcx.def(expr.id));
|
return trans_def_lvalue(bcx, expr, bcx.def(expr.id));
|
||||||
}
|
}
|
||||||
ast::expr_field(base, ident, _) => {
|
ast::expr_field(base, ident, _) => {
|
||||||
return trans_rec_field(bcx, base, ident, expr.id);
|
return trans_rec_field(bcx, base, ident);
|
||||||
}
|
}
|
||||||
ast::expr_index(base, idx) => {
|
ast::expr_index(base, idx) => {
|
||||||
return trans_index(bcx, expr, base, idx);
|
return trans_index(bcx, expr, base, idx);
|
||||||
|
@ -779,52 +790,152 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn trans_def_lvalue(bcx: block,
|
fn trans_rec_field(bcx: block,
|
||||||
ref_expr: @ast::expr,
|
base: @ast::expr,
|
||||||
def: ast::def)
|
field: ast::ident) -> DatumBlock {
|
||||||
-> DatumBlock {
|
/*!
|
||||||
let _icx = bcx.insn_ctxt("trans_def_lvalue");
|
*
|
||||||
let ccx = bcx.ccx();
|
* Translates `base.field`. Note that this version always
|
||||||
match def {
|
* yields an unrooted, unmoved version. Rooting and possible
|
||||||
ast::def_const(did) => {
|
* moves are dealt with above in trans_lvalue_unadjusted().
|
||||||
let const_ty = expr_ty(bcx, ref_expr);
|
*/
|
||||||
let val = if did.crate == ast::local_crate {
|
|
||||||
// The LLVM global has the type of its initializer,
|
let mut bcx = bcx;
|
||||||
// which may not be equal to the enum's type for
|
let _icx = bcx.insn_ctxt("trans_rec_field");
|
||||||
// non-C-like enums.
|
|
||||||
PointerCast(bcx, base::get_item_val(ccx, did.node),
|
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
||||||
T_ptr(type_of(bcx.ccx(), const_ty)))
|
do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| {
|
||||||
} else {
|
let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
|
||||||
base::trans_external_path(ccx, did, const_ty)
|
|
||||||
};
|
|
||||||
DatumBlock {
|
DatumBlock {
|
||||||
bcx: bcx,
|
datum: base_datum.GEPi(bcx,
|
||||||
datum: Datum {val: val,
|
[0u, 0u, ix],
|
||||||
ty: const_ty,
|
field_tys[ix].mt.ty,
|
||||||
mode: ByRef,
|
ZeroMem),
|
||||||
source: FromLvalue}
|
bcx: bcx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
}
|
||||||
DatumBlock {
|
|
||||||
bcx: bcx,
|
fn trans_index(bcx: block,
|
||||||
datum: trans_local_var(bcx, def, Some(ref_expr.id))
|
index_expr: @ast::expr,
|
||||||
|
base: @ast::expr,
|
||||||
|
idx: @ast::expr) -> DatumBlock {
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
* Translates `base[idx]`. Note that this version always
|
||||||
|
* yields an unrooted, unmoved version. Rooting and possible
|
||||||
|
* moves are dealt with above in trans_lvalue_unadjusted().
|
||||||
|
*/
|
||||||
|
|
||||||
|
let _icx = bcx.insn_ctxt("trans_index");
|
||||||
|
let ccx = bcx.ccx();
|
||||||
|
let base_ty = expr_ty(bcx, base);
|
||||||
|
let mut bcx = bcx;
|
||||||
|
|
||||||
|
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
||||||
|
|
||||||
|
// Translate index expression and cast to a suitable LLVM integer.
|
||||||
|
// Rust is less strict than LLVM in this regard.
|
||||||
|
let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
|
||||||
|
let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val));
|
||||||
|
let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type);
|
||||||
|
let ix_val = {
|
||||||
|
if ix_size < int_size {
|
||||||
|
if ty::type_is_signed(expr_ty(bcx, idx)) {
|
||||||
|
SExt(bcx, ix_val, ccx.int_type)
|
||||||
|
} else { ZExt(bcx, ix_val, ccx.int_type) }
|
||||||
|
} else if ix_size > int_size {
|
||||||
|
Trunc(bcx, ix_val, ccx.int_type)
|
||||||
|
} else {
|
||||||
|
ix_val
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let vt = tvec::vec_types(bcx, base_datum.ty);
|
||||||
|
base::maybe_name_value(bcx.ccx(), vt.llunit_size, ~"unit_sz");
|
||||||
|
let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
|
||||||
|
base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix");
|
||||||
|
|
||||||
|
let mut (base, len) = base_datum.get_base_and_len(bcx);
|
||||||
|
|
||||||
|
if ty::type_is_str(base_ty) {
|
||||||
|
// acccount for null terminator in the case of string
|
||||||
|
len = Sub(bcx, len, C_uint(bcx.ccx(), 1u));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("trans_index: base %s", val_str(bcx.ccx().tn, base));
|
||||||
|
debug!("trans_index: len %s", val_str(bcx.ccx().tn, len));
|
||||||
|
|
||||||
|
let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len);
|
||||||
|
let bcx = do with_cond(bcx, bounds_check) |bcx| {
|
||||||
|
let unscaled_len = UDiv(bcx, len, vt.llunit_size);
|
||||||
|
controlflow::trans_fail_bounds_check(bcx, index_expr.span,
|
||||||
|
ix_val, unscaled_len)
|
||||||
|
};
|
||||||
|
let elt = InBoundsGEP(bcx, base, ~[ix_val]);
|
||||||
|
let elt = PointerCast(bcx, elt, T_ptr(vt.llunit_ty));
|
||||||
|
return DatumBlock {
|
||||||
|
bcx: bcx,
|
||||||
|
datum: Datum {val: elt,
|
||||||
|
ty: vt.unit_ty,
|
||||||
|
mode: ByRef,
|
||||||
|
source: ZeroMem}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trans_def_lvalue(bcx: block,
|
||||||
|
ref_expr: @ast::expr,
|
||||||
|
def: ast::def)
|
||||||
|
-> DatumBlock
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
* Translates a reference to a path. Note that this version
|
||||||
|
* generally yields an unrooted, unmoved version. Rooting and
|
||||||
|
* possible moves are dealt with above in
|
||||||
|
* trans_lvalue_unadjusted(), with the caveat that local variables
|
||||||
|
* may already be in move mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let _icx = bcx.insn_ctxt("trans_def_lvalue");
|
||||||
|
let ccx = bcx.ccx();
|
||||||
|
match def {
|
||||||
|
ast::def_const(did) => {
|
||||||
|
let const_ty = expr_ty(bcx, ref_expr);
|
||||||
|
let val = if did.crate == ast::local_crate {
|
||||||
|
// The LLVM global has the type of its initializer,
|
||||||
|
// which may not be equal to the enum's type for
|
||||||
|
// non-C-like enums.
|
||||||
|
PointerCast(bcx, base::get_item_val(ccx, did.node),
|
||||||
|
T_ptr(type_of(bcx.ccx(), const_ty)))
|
||||||
|
} else {
|
||||||
|
base::trans_external_path(ccx, did, const_ty)
|
||||||
|
};
|
||||||
|
DatumBlock {
|
||||||
|
bcx: bcx,
|
||||||
|
datum: Datum {val: val,
|
||||||
|
ty: const_ty,
|
||||||
|
mode: ByRef,
|
||||||
|
source: ZeroMem}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
DatumBlock {
|
||||||
|
bcx: bcx,
|
||||||
|
datum: trans_local_var(bcx, def)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trans_local_var(bcx: block,
|
pub fn trans_local_var(bcx: block, def: ast::def) -> Datum {
|
||||||
def: ast::def,
|
|
||||||
expr_id_opt: Option<ast::node_id>)
|
|
||||||
-> Datum {
|
|
||||||
let _icx = bcx.insn_ctxt("trans_local_var");
|
let _icx = bcx.insn_ctxt("trans_local_var");
|
||||||
|
|
||||||
return match def {
|
return match def {
|
||||||
ast::def_upvar(nid, _, _, _) => {
|
ast::def_upvar(nid, _, _, _) => {
|
||||||
// Can't move upvars, so this is never a FromLvalueLastUse.
|
// Can't move upvars, so this is never a ZeroMemLastUse.
|
||||||
let local_ty = node_id_type(bcx, nid);
|
let local_ty = node_id_type(bcx, nid);
|
||||||
match bcx.fcx.llupvars.find(nid) {
|
match bcx.fcx.llupvars.find(nid) {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
|
@ -832,7 +943,7 @@ pub fn trans_local_var(bcx: block,
|
||||||
val: val,
|
val: val,
|
||||||
ty: local_ty,
|
ty: local_ty,
|
||||||
mode: ByRef,
|
mode: ByRef,
|
||||||
source: FromLvalue
|
source: ZeroMem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -842,10 +953,10 @@ pub fn trans_local_var(bcx: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::def_arg(nid, _, _) => {
|
ast::def_arg(nid, _, _) => {
|
||||||
take_local(bcx, bcx.fcx.llargs, nid, expr_id_opt)
|
take_local(bcx, bcx.fcx.llargs, nid)
|
||||||
}
|
}
|
||||||
ast::def_local(nid, _) | ast::def_binding(nid, _) => {
|
ast::def_local(nid, _) | ast::def_binding(nid, _) => {
|
||||||
take_local(bcx, bcx.fcx.lllocals, nid, expr_id_opt)
|
take_local(bcx, bcx.fcx.lllocals, nid)
|
||||||
}
|
}
|
||||||
ast::def_self(nid, _) => {
|
ast::def_self(nid, _) => {
|
||||||
let self_info: ValSelfData = match bcx.fcx.llself {
|
let self_info: ValSelfData = match bcx.fcx.llself {
|
||||||
|
@ -867,7 +978,7 @@ pub fn trans_local_var(bcx: block,
|
||||||
val: casted_val,
|
val: casted_val,
|
||||||
ty: self_info.t,
|
ty: self_info.t,
|
||||||
mode: ByRef,
|
mode: ByRef,
|
||||||
source: source_from_opt_lvalue_type(bcx.tcx(), expr_id_opt)
|
source: ZeroMem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -878,8 +989,7 @@ pub fn trans_local_var(bcx: block,
|
||||||
|
|
||||||
fn take_local(bcx: block,
|
fn take_local(bcx: block,
|
||||||
table: HashMap<ast::node_id, local_val>,
|
table: HashMap<ast::node_id, local_val>,
|
||||||
nid: ast::node_id,
|
nid: ast::node_id) -> Datum {
|
||||||
expr_id_opt: Option<ast::node_id>) -> Datum {
|
|
||||||
let (v, mode) = match table.find(nid) {
|
let (v, mode) = match table.find(nid) {
|
||||||
Some(local_mem(v)) => (v, ByRef),
|
Some(local_mem(v)) => (v, ByRef),
|
||||||
Some(local_imm(v)) => (v, ByValue),
|
Some(local_imm(v)) => (v, ByValue),
|
||||||
|
@ -897,7 +1007,7 @@ pub fn trans_local_var(bcx: block,
|
||||||
val: v,
|
val: v,
|
||||||
ty: ty,
|
ty: ty,
|
||||||
mode: mode,
|
mode: mode,
|
||||||
source: source_from_opt_lvalue_type(bcx.tcx(), expr_id_opt)
|
source: ZeroMem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -981,102 +1091,6 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trans_rec_field(bcx: block,
|
|
||||||
base: @ast::expr,
|
|
||||||
field: ast::ident,
|
|
||||||
expr_id: ast::node_id) -> DatumBlock {
|
|
||||||
let mut bcx = bcx;
|
|
||||||
let _icx = bcx.insn_ctxt("trans_rec_field");
|
|
||||||
|
|
||||||
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
|
||||||
do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| {
|
|
||||||
let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
|
|
||||||
DatumBlock {
|
|
||||||
datum: base_datum.GEPi(bcx,
|
|
||||||
[0u, 0u, ix],
|
|
||||||
field_tys[ix].mt.ty,
|
|
||||||
source_from_opt_lvalue_type(
|
|
||||||
bcx.tcx(), Some(expr_id))),
|
|
||||||
bcx: bcx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source_from_opt_lvalue_type(tcx: ty::ctxt,
|
|
||||||
expr_id_opt: Option<ast::node_id>)
|
|
||||||
-> DatumSource {
|
|
||||||
match expr_id_opt {
|
|
||||||
None => FromLvalue,
|
|
||||||
Some(expr_id) => {
|
|
||||||
match tcx.value_modes.find(expr_id) {
|
|
||||||
Some(MoveValue) => FromLastUseLvalue,
|
|
||||||
Some(_) | None => FromLvalue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trans_index(bcx: block,
|
|
||||||
index_expr: @ast::expr,
|
|
||||||
base: @ast::expr,
|
|
||||||
idx: @ast::expr) -> DatumBlock {
|
|
||||||
let _icx = bcx.insn_ctxt("trans_index");
|
|
||||||
let ccx = bcx.ccx();
|
|
||||||
let base_ty = expr_ty(bcx, base);
|
|
||||||
let mut bcx = bcx;
|
|
||||||
|
|
||||||
let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
|
|
||||||
|
|
||||||
// Translate index expression and cast to a suitable LLVM integer.
|
|
||||||
// Rust is less strict than LLVM in this regard.
|
|
||||||
let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
|
|
||||||
let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val));
|
|
||||||
let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type);
|
|
||||||
let ix_val = {
|
|
||||||
if ix_size < int_size {
|
|
||||||
if ty::type_is_signed(expr_ty(bcx, idx)) {
|
|
||||||
SExt(bcx, ix_val, ccx.int_type)
|
|
||||||
} else { ZExt(bcx, ix_val, ccx.int_type) }
|
|
||||||
} else if ix_size > int_size {
|
|
||||||
Trunc(bcx, ix_val, ccx.int_type)
|
|
||||||
} else {
|
|
||||||
ix_val
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let vt = tvec::vec_types(bcx, base_datum.ty);
|
|
||||||
base::maybe_name_value(bcx.ccx(), vt.llunit_size, ~"unit_sz");
|
|
||||||
let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
|
|
||||||
base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix");
|
|
||||||
|
|
||||||
let mut (base, len) = base_datum.get_base_and_len(bcx);
|
|
||||||
|
|
||||||
if ty::type_is_str(base_ty) {
|
|
||||||
// acccount for null terminator in the case of string
|
|
||||||
len = Sub(bcx, len, C_uint(bcx.ccx(), 1u));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("trans_index: base %s", val_str(bcx.ccx().tn, base));
|
|
||||||
debug!("trans_index: len %s", val_str(bcx.ccx().tn, len));
|
|
||||||
|
|
||||||
let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len);
|
|
||||||
let bcx = do with_cond(bcx, bounds_check) |bcx| {
|
|
||||||
let unscaled_len = UDiv(bcx, len, vt.llunit_size);
|
|
||||||
controlflow::trans_fail_bounds_check(bcx, index_expr.span,
|
|
||||||
ix_val, unscaled_len)
|
|
||||||
};
|
|
||||||
let elt = InBoundsGEP(bcx, base, ~[ix_val]);
|
|
||||||
let elt = PointerCast(bcx, elt, T_ptr(vt.llunit_ty));
|
|
||||||
return DatumBlock {
|
|
||||||
bcx: bcx,
|
|
||||||
datum: Datum {val: elt,
|
|
||||||
ty: vt.unit_ty,
|
|
||||||
mode: ByRef,
|
|
||||||
source: source_from_opt_lvalue_type(
|
|
||||||
bcx.tcx(), Some(index_expr.id))}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trans_rec_or_struct(bcx: block,
|
fn trans_rec_or_struct(bcx: block,
|
||||||
fields: &[ast::field],
|
fields: &[ast::field],
|
||||||
base: Option<@ast::expr>,
|
base: Option<@ast::expr>,
|
||||||
|
@ -1158,7 +1172,7 @@ fn trans_rec_or_struct(bcx: block,
|
||||||
let base_datum = unpack_datum!(
|
let base_datum = unpack_datum!(
|
||||||
bcx, trans_to_datum(bcx, *base_expr));
|
bcx, trans_to_datum(bcx, *base_expr));
|
||||||
|
|
||||||
// Copy over inherited fields
|
// Copy/move over inherited fields
|
||||||
for field_tys.eachi |i, field_ty| {
|
for field_tys.eachi |i, field_ty| {
|
||||||
if !fields.any(|f| f.node.ident == field_ty.ident) {
|
if !fields.any(|f| f.node.ident == field_ty.ident) {
|
||||||
let dest = GEPi(bcx, addr, struct_field(i));
|
let dest = GEPi(bcx, addr, struct_field(i));
|
||||||
|
@ -1166,8 +1180,8 @@ fn trans_rec_or_struct(bcx: block,
|
||||||
base_datum.GEPi(bcx,
|
base_datum.GEPi(bcx,
|
||||||
struct_field(i),
|
struct_field(i),
|
||||||
field_ty.mt.ty,
|
field_ty.mt.ty,
|
||||||
FromLvalue);
|
ZeroMem);
|
||||||
bcx = base_field.store_to(bcx, INIT, dest);
|
bcx = base_field.store_to(bcx, base_expr.id, INIT, dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1187,7 +1201,7 @@ fn trans_rec_or_struct(bcx: block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trans_tup(bcx: block, elts: ~[@ast::expr], dest: Dest) -> block {
|
fn trans_tup(bcx: block, elts: &[@ast::expr], dest: Dest) -> block {
|
||||||
let _icx = bcx.insn_ctxt("trans_tup");
|
let _icx = bcx.insn_ctxt("trans_tup");
|
||||||
let mut bcx = bcx;
|
let mut bcx = bcx;
|
||||||
let addr = match dest {
|
let addr = match dest {
|
||||||
|
@ -1642,7 +1656,7 @@ fn trans_assign_op(bcx: block,
|
||||||
trans_eager_binop(
|
trans_eager_binop(
|
||||||
bcx, expr, dst_datum.ty, op,
|
bcx, expr, dst_datum.ty, op,
|
||||||
&dst_datum, &src_datum));
|
&dst_datum, &src_datum));
|
||||||
return result_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum);
|
return result_datum.copy_to_datum(bcx, DROP_EXISTING, dst_datum);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shorten(+x: ~str) -> ~str {
|
fn shorten(+x: ~str) -> ~str {
|
||||||
|
|
|
@ -69,7 +69,7 @@ fn c_arg_and_ret_lltys(ccx: @crate_ctxt,
|
||||||
ty::ty_fn(ref fn_ty) => {
|
ty::ty_fn(ref fn_ty) => {
|
||||||
let llargtys = type_of_explicit_args(
|
let llargtys = type_of_explicit_args(
|
||||||
ccx,
|
ccx,
|
||||||
/*bad*/copy fn_ty.sig.inputs);
|
fn_ty.sig.inputs);
|
||||||
let llretty = type_of::type_of(ccx, fn_ty.sig.output);
|
let llretty = type_of::type_of(ccx, fn_ty.sig.output);
|
||||||
(llargtys, llretty, fn_ty.sig.output)
|
(llargtys, llretty, fn_ty.sig.output)
|
||||||
}
|
}
|
||||||
|
@ -441,7 +441,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
|
||||||
//
|
//
|
||||||
// - the datum will be by ref if the value is non-immediate;
|
// - the datum will be by ref if the value is non-immediate;
|
||||||
//
|
//
|
||||||
// - the datum has a FromRvalue source because, that way,
|
// - the datum has a RevokeClean source because, that way,
|
||||||
// the `move_to()` method does not feel compelled to
|
// the `move_to()` method does not feel compelled to
|
||||||
// zero out the memory where the datum resides. Zeroing
|
// zero out the memory where the datum resides. Zeroing
|
||||||
// is not necessary since, for intrinsics, there is no
|
// is not necessary since, for intrinsics, there is no
|
||||||
|
@ -449,7 +449,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
|
||||||
let tp_ty = substs.tys[0];
|
let tp_ty = substs.tys[0];
|
||||||
let mode = appropriate_mode(tp_ty);
|
let mode = appropriate_mode(tp_ty);
|
||||||
let src = Datum {val: get_param(decl, first_real_arg + 1u),
|
let src = Datum {val: get_param(decl, first_real_arg + 1u),
|
||||||
ty: tp_ty, mode: mode, source: FromRvalue};
|
ty: tp_ty, mode: mode, source: RevokeClean};
|
||||||
bcx = src.move_to(bcx, DROP_EXISTING,
|
bcx = src.move_to(bcx, DROP_EXISTING,
|
||||||
get_param(decl, first_real_arg));
|
get_param(decl, first_real_arg));
|
||||||
}
|
}
|
||||||
|
@ -458,7 +458,7 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
|
||||||
let tp_ty = substs.tys[0];
|
let tp_ty = substs.tys[0];
|
||||||
let mode = appropriate_mode(tp_ty);
|
let mode = appropriate_mode(tp_ty);
|
||||||
let src = Datum {val: get_param(decl, first_real_arg + 1u),
|
let src = Datum {val: get_param(decl, first_real_arg + 1u),
|
||||||
ty: tp_ty, mode: mode, source: FromRvalue};
|
ty: tp_ty, mode: mode, source: RevokeClean};
|
||||||
bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
|
bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
|
||||||
}
|
}
|
||||||
~"min_align_of" => {
|
~"min_align_of" => {
|
||||||
|
@ -546,16 +546,17 @@ pub fn trans_intrinsic(ccx: @crate_ctxt,
|
||||||
onceness: ast::Many,
|
onceness: ast::Many,
|
||||||
region: ty::re_bound(ty::br_anon(0)),
|
region: ty::re_bound(ty::br_anon(0)),
|
||||||
bounds: @~[]},
|
bounds: @~[]},
|
||||||
sig: FnSig {inputs: ~[arg {mode: ast::expl(ast::by_val),
|
sig: FnSig {inputs: ~[arg {mode: ast::expl(ast::by_copy),
|
||||||
ty: star_u8}],
|
ty: star_u8}],
|
||||||
output: ty::mk_nil(bcx.tcx())}
|
output: ty::mk_nil(bcx.tcx())}
|
||||||
});
|
});
|
||||||
let datum = Datum {val: get_param(decl, first_real_arg),
|
let datum = Datum {val: get_param(decl, first_real_arg),
|
||||||
mode: ByRef, ty: fty, source: FromLvalue};
|
mode: ByRef, ty: fty, source: ZeroMem};
|
||||||
|
let arg_vals = ~[frameaddress_val];
|
||||||
bcx = trans_call_inner(
|
bcx = trans_call_inner(
|
||||||
bcx, None, fty, ty::mk_nil(bcx.tcx()),
|
bcx, None, fty, ty::mk_nil(bcx.tcx()),
|
||||||
|bcx| Callee {bcx: bcx, data: Closure(datum)},
|
|bcx| Callee {bcx: bcx, data: Closure(datum)},
|
||||||
ArgVals(~[frameaddress_val]), Ignore, DontAutorefArg);
|
ArgVals(arg_vals), Ignore, DontAutorefArg);
|
||||||
}
|
}
|
||||||
~"morestack_addr" => {
|
~"morestack_addr" => {
|
||||||
// XXX This is a hack to grab the address of this particular
|
// XXX This is a hack to grab the address of this particular
|
||||||
|
|
|
@ -232,7 +232,6 @@ pub impl reflector {
|
||||||
ast::expl(e) => match e {
|
ast::expl(e) => match e {
|
||||||
ast::by_ref => 1u,
|
ast::by_ref => 1u,
|
||||||
ast::by_val => 2u,
|
ast::by_val => 2u,
|
||||||
ast::by_move => 4u,
|
|
||||||
ast::by_copy => 5u
|
ast::by_copy => 5u
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef {
|
||||||
let llty = type_of(ccx, arg.ty);
|
let llty = type_of(ccx, arg.ty);
|
||||||
match ty::resolved_mode(ccx.tcx, arg.mode) {
|
match ty::resolved_mode(ccx.tcx, arg.mode) {
|
||||||
ast::by_val => llty,
|
ast::by_val => llty,
|
||||||
ast::by_copy | ast::by_move => {
|
ast::by_copy => {
|
||||||
if ty::type_is_immediate(arg.ty) {
|
if ty::type_is_immediate(arg.ty) {
|
||||||
llty
|
llty
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,12 +35,12 @@ pub fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_of_explicit_args(ccx: @crate_ctxt, inputs: ~[ty::arg])
|
pub fn type_of_explicit_args(ccx: @crate_ctxt,
|
||||||
-> ~[TypeRef] {
|
inputs: &[ty::arg]) -> ~[TypeRef] {
|
||||||
inputs.map(|arg| type_of_explicit_arg(ccx, *arg))
|
inputs.map(|arg| type_of_explicit_arg(ccx, *arg))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_of_fn(cx: @crate_ctxt, inputs: ~[ty::arg],
|
pub fn type_of_fn(cx: @crate_ctxt, inputs: &[ty::arg],
|
||||||
output: ty::t) -> TypeRef {
|
output: ty::t) -> TypeRef {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut atys: ~[TypeRef] = ~[];
|
let mut atys: ~[TypeRef] = ~[];
|
||||||
|
|
|
@ -72,7 +72,7 @@ pub fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
|
||||||
ty::ty_fn(ref fn_ty) => {
|
ty::ty_fn(ref fn_ty) => {
|
||||||
for vec::each(fn_ty.sig.inputs) |arg| {
|
for vec::each(fn_ty.sig.inputs) |arg| {
|
||||||
match ty::resolved_mode(ccx.tcx, arg.mode) {
|
match ty::resolved_mode(ccx.tcx, arg.mode) {
|
||||||
by_val | by_move | by_copy => {
|
by_val | by_copy => {
|
||||||
type_needs(cx, use_repr, arg.ty);
|
type_needs(cx, use_repr, arg.ty);
|
||||||
}
|
}
|
||||||
by_ref => {}
|
by_ref => {}
|
||||||
|
@ -255,7 +255,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) {
|
||||||
expr_rec(_, _) | expr_struct(*) | expr_tup(_) |
|
expr_rec(_, _) | expr_struct(*) | expr_tup(_) |
|
||||||
expr_unary(box(_), _) | expr_unary(uniq(_), _) |
|
expr_unary(box(_), _) | expr_unary(uniq(_), _) |
|
||||||
expr_binary(add, _, _) |
|
expr_binary(add, _, _) |
|
||||||
expr_copy(_) | expr_unary_move(_) | expr_repeat(*) => {
|
expr_copy(_) | expr_repeat(*) => {
|
||||||
node_type_needs(cx, use_repr, e.id);
|
node_type_needs(cx, use_repr, e.id);
|
||||||
}
|
}
|
||||||
expr_cast(base, _) => {
|
expr_cast(base, _) => {
|
||||||
|
@ -316,7 +316,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) {
|
||||||
ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id))
|
ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx, f.id))
|
||||||
) |a| {
|
) |a| {
|
||||||
match a.mode {
|
match a.mode {
|
||||||
expl(by_move) | expl(by_copy) | expl(by_val) => {
|
expl(by_copy) | expl(by_val) => {
|
||||||
type_needs(cx, use_repr, a.ty);
|
type_needs(cx, use_repr, a.ty);
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -330,7 +330,7 @@ pub fn mark_for_expr(cx: ctx, e: @expr) {
|
||||||
for ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx,
|
for ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx,
|
||||||
e.callee_id)).each |a| {
|
e.callee_id)).each |a| {
|
||||||
match a.mode {
|
match a.mode {
|
||||||
expl(by_move) | expl(by_copy) | expl(by_val) => {
|
expl(by_copy) | expl(by_val) => {
|
||||||
type_needs(cx, use_repr, a.ty);
|
type_needs(cx, use_repr, a.ty);
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
|
|
|
@ -99,15 +99,6 @@ pub struct field_ty {
|
||||||
mutability: ast::struct_mutability,
|
mutability: ast::struct_mutability,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How an lvalue is to be used.
|
|
||||||
#[auto_encode]
|
|
||||||
#[auto_decode]
|
|
||||||
pub enum ValueMode {
|
|
||||||
ReadValue, // Non-destructively read the value; do not copy or move.
|
|
||||||
CopyValue, // Copy the value.
|
|
||||||
MoveValue, // Move the value.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains information needed to resolve types and (in the future) look up
|
// Contains information needed to resolve types and (in the future) look up
|
||||||
// the types of AST nodes.
|
// the types of AST nodes.
|
||||||
#[deriving_eq]
|
#[deriving_eq]
|
||||||
|
@ -295,9 +286,6 @@ struct ctxt_ {
|
||||||
// A method will be in this list if and only if it is a destructor.
|
// A method will be in this list if and only if it is a destructor.
|
||||||
destructors: HashMap<ast::def_id, ()>,
|
destructors: HashMap<ast::def_id, ()>,
|
||||||
|
|
||||||
// Records the value mode (read, copy, or move) for every value.
|
|
||||||
value_modes: HashMap<ast::node_id, ValueMode>,
|
|
||||||
|
|
||||||
// Maps a trait onto a mapping from self-ty to impl
|
// Maps a trait onto a mapping from self-ty to impl
|
||||||
trait_impls: HashMap<ast::def_id, HashMap<t, @Impl>>
|
trait_impls: HashMap<ast::def_id, HashMap<t, @Impl>>
|
||||||
}
|
}
|
||||||
|
@ -875,7 +863,6 @@ pub fn mk_ctxt(s: session::Session,
|
||||||
supertraits: HashMap(),
|
supertraits: HashMap(),
|
||||||
destructor_for_type: HashMap(),
|
destructor_for_type: HashMap(),
|
||||||
destructors: HashMap(),
|
destructors: HashMap(),
|
||||||
value_modes: HashMap(),
|
|
||||||
trait_impls: HashMap()
|
trait_impls: HashMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2170,7 +2157,14 @@ pub fn type_kind_ext(cx: ctxt, ty: t, allow_ty_var: bool) -> Kind {
|
||||||
}
|
}
|
||||||
|
|
||||||
ty_param(p) => {
|
ty_param(p) => {
|
||||||
param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node))
|
// We only ever ask for the kind of types that are defined in the
|
||||||
|
// current crate; therefore, the only type parameters that could be
|
||||||
|
// in scope are those defined in the current crate. If this
|
||||||
|
// assertion failures, it is likely because of a failure in the
|
||||||
|
// cross-crate inlining code to translate a def-id.
|
||||||
|
assert p.def_id.crate == ast::local_crate;
|
||||||
|
|
||||||
|
param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node))
|
||||||
}
|
}
|
||||||
|
|
||||||
// self is a special type parameter that can only appear in traits; it
|
// self is a special type parameter that can only appear in traits; it
|
||||||
|
@ -2925,7 +2919,12 @@ pub fn pat_ty(cx: ctxt, pat: @ast::pat) -> t {
|
||||||
|
|
||||||
// Returns the type of an expression as a monotype.
|
// Returns the type of an expression as a monotype.
|
||||||
//
|
//
|
||||||
// NB: This type doesn't provide type parameter substitutions; e.g. if you
|
// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
|
||||||
|
// some cases, we insert `AutoAdjustment` annotations such as auto-deref or
|
||||||
|
// auto-ref. The type returned by this function does not consider such
|
||||||
|
// adjustments. See `expr_ty_adjusted()` instead.
|
||||||
|
//
|
||||||
|
// NB (2): This type doesn't provide type parameter substitutions; e.g. if you
|
||||||
// ask for the type of "id" in "id(3)", it will return "fn(&int) -> int"
|
// ask for the type of "id" in "id(3)", it will return "fn(&int) -> int"
|
||||||
// instead of "fn(t) -> T with T = int". If this isn't what you want, see
|
// instead of "fn(t) -> T with T = int". If this isn't what you want, see
|
||||||
// expr_ty_params_and_ty() below.
|
// expr_ty_params_and_ty() below.
|
||||||
|
@ -2933,6 +2932,112 @@ pub fn expr_ty(cx: ctxt, expr: @ast::expr) -> t {
|
||||||
return node_id_to_type(cx, expr.id);
|
return node_id_to_type(cx, expr.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
|
||||||
|
/*!
|
||||||
|
*
|
||||||
|
* Returns the type of `expr`, considering any `AutoAdjustment`
|
||||||
|
* entry recorded for that expression.
|
||||||
|
*
|
||||||
|
* It would almost certainly be better to store the adjusted ty in with
|
||||||
|
* the `AutoAdjustment`, but I opted not to do this because it would
|
||||||
|
* require serializing and deserializing the type and, although that's not
|
||||||
|
* hard to do, I just hate that code so much I didn't want to touch it
|
||||||
|
* unless it was to fix it properly, which seemed a distraction from the
|
||||||
|
* task at hand! -nmatsakis
|
||||||
|
*/
|
||||||
|
|
||||||
|
let unadjusted_ty = expr_ty(cx, expr);
|
||||||
|
|
||||||
|
return match cx.adjustments.find(expr.id) {
|
||||||
|
None => unadjusted_ty,
|
||||||
|
|
||||||
|
Some(adj) => {
|
||||||
|
let mut adjusted_ty = unadjusted_ty;
|
||||||
|
|
||||||
|
for uint::range(0, adj.autoderefs) |i| {
|
||||||
|
match ty::deref(cx, adjusted_ty, true) {
|
||||||
|
Some(mt) => { adjusted_ty = mt.ty; }
|
||||||
|
None => {
|
||||||
|
cx.sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
fmt!("The %uth autoderef failed: %s",
|
||||||
|
i, ty_to_str(cx,
|
||||||
|
adjusted_ty)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match adj.autoref {
|
||||||
|
None => adjusted_ty,
|
||||||
|
Some(ref autoref) => {
|
||||||
|
match autoref.kind {
|
||||||
|
AutoPtr => {
|
||||||
|
mk_rptr(cx, autoref.region,
|
||||||
|
mt {ty: adjusted_ty,
|
||||||
|
mutbl: autoref.mutbl})
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoBorrowVec => {
|
||||||
|
borrow_vec(cx, expr, autoref, adjusted_ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoBorrowVecRef => {
|
||||||
|
adjusted_ty = borrow_vec(cx, expr, autoref,
|
||||||
|
adjusted_ty);
|
||||||
|
mk_rptr(cx, autoref.region,
|
||||||
|
mt {ty: adjusted_ty, mutbl: ast::m_imm})
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoBorrowFn => {
|
||||||
|
borrow_fn(cx, expr, autoref, adjusted_ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn borrow_vec(cx: ctxt, expr: @ast::expr,
|
||||||
|
autoref: &AutoRef, ty: ty::t) -> ty::t {
|
||||||
|
match get(ty).sty {
|
||||||
|
ty_evec(mt, _) => {
|
||||||
|
ty::mk_evec(cx, mt {ty: mt.ty, mutbl: autoref.mutbl},
|
||||||
|
vstore_slice(autoref.region))
|
||||||
|
}
|
||||||
|
|
||||||
|
ty_estr(_) => {
|
||||||
|
ty::mk_estr(cx, vstore_slice(autoref.region))
|
||||||
|
}
|
||||||
|
|
||||||
|
ref s => {
|
||||||
|
cx.sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
fmt!("borrow-vec associated with bad sty: %?",
|
||||||
|
s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn borrow_fn(cx: ctxt, expr: @ast::expr,
|
||||||
|
autoref: &AutoRef, ty: ty::t) -> ty::t {
|
||||||
|
match get(ty).sty {
|
||||||
|
ty_fn(ref fty) => {
|
||||||
|
ty::mk_fn(cx, FnTyBase {meta: FnMeta {proto: ProtoBorrowed,
|
||||||
|
region: autoref.region,
|
||||||
|
..copy fty.meta},
|
||||||
|
sig: copy fty.sig})
|
||||||
|
}
|
||||||
|
|
||||||
|
ref s => {
|
||||||
|
cx.sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
fmt!("borrow-fn associated with bad sty: %?",
|
||||||
|
s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expr_ty_params_and_ty(cx: ctxt,
|
pub fn expr_ty_params_and_ty(cx: ctxt,
|
||||||
expr: @ast::expr)
|
expr: @ast::expr)
|
||||||
-> {params: ~[t], ty: t} {
|
-> {params: ~[t], ty: t} {
|
||||||
|
@ -3060,7 +3165,6 @@ pub fn expr_kind(tcx: ctxt,
|
||||||
ast::expr_do_body(*) |
|
ast::expr_do_body(*) |
|
||||||
ast::expr_block(*) |
|
ast::expr_block(*) |
|
||||||
ast::expr_copy(*) |
|
ast::expr_copy(*) |
|
||||||
ast::expr_unary_move(*) |
|
|
||||||
ast::expr_repeat(*) |
|
ast::expr_repeat(*) |
|
||||||
ast::expr_lit(@ast::spanned {node: lit_str(_), _}) |
|
ast::expr_lit(@ast::spanned {node: lit_str(_), _}) |
|
||||||
ast::expr_vstore(_, ast::expr_vstore_slice) |
|
ast::expr_vstore(_, ast::expr_vstore_slice) |
|
||||||
|
|
|
@ -381,7 +381,7 @@ pub fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
|
||||||
demand::eqtype(fcx, pat.span, region_ty, typ);
|
demand::eqtype(fcx, pat.span, region_ty, typ);
|
||||||
}
|
}
|
||||||
// otherwise the type of x is the expected type T
|
// otherwise the type of x is the expected type T
|
||||||
ast::bind_by_value | ast::bind_by_move | ast::bind_infer => {
|
ast::bind_by_copy | ast::bind_infer => {
|
||||||
demand::eqtype(fcx, pat.span, expected, typ);
|
demand::eqtype(fcx, pat.span, expected, typ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,6 @@ type parameter).
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use middle::capture;
|
|
||||||
use middle::const_eval;
|
use middle::const_eval;
|
||||||
use middle::pat_util::pat_id_map;
|
use middle::pat_util::pat_id_map;
|
||||||
use middle::pat_util;
|
use middle::pat_util;
|
||||||
|
@ -1454,7 +1453,7 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
match ast_util::binop_to_method_name(op) {
|
match ast_util::binop_to_method_name(op) {
|
||||||
Some(ref name) => {
|
Some(ref name) => {
|
||||||
match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t,
|
match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t,
|
||||||
fcx.tcx().sess.ident_of((*name)),
|
fcx.tcx().sess.ident_of(copy *name),
|
||||||
~[rhs], DoDerefArgs) {
|
~[rhs], DoDerefArgs) {
|
||||||
Some(pair) => return pair,
|
Some(pair) => return pair,
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -1488,9 +1487,11 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
|
fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
|
||||||
ex: @ast::expr,
|
ex: @ast::expr,
|
||||||
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
|
rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
|
||||||
match lookup_op_method(fcx, ex, rhs_expr, rhs_t,
|
match lookup_op_method(
|
||||||
fcx.tcx().sess.ident_of(mname), ~[],
|
fcx, ex, rhs_expr, rhs_t,
|
||||||
DontDerefArgs) {
|
fcx.tcx().sess.ident_of(/*bad*/ copy mname), ~[],
|
||||||
|
DontDerefArgs)
|
||||||
|
{
|
||||||
Some((ret_ty, _)) => ret_ty,
|
Some((ret_ty, _)) => ret_ty,
|
||||||
_ => {
|
_ => {
|
||||||
fcx.type_error_message(ex.span, |actual| {
|
fcx.type_error_message(ex.span, |actual| {
|
||||||
|
@ -2132,7 +2133,7 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
bot = check_expr_has_type(fcx, e, ty::mk_bool(tcx));
|
bot = check_expr_has_type(fcx, e, ty::mk_bool(tcx));
|
||||||
fcx.write_nil(id);
|
fcx.write_nil(id);
|
||||||
}
|
}
|
||||||
ast::expr_copy(a) | ast::expr_unary_move(a) => {
|
ast::expr_copy(a) => {
|
||||||
bot = check_expr_with_opt_hint(fcx, a, expected);
|
bot = check_expr_with_opt_hint(fcx, a, expected);
|
||||||
fcx.write_ty(id, fcx.expr_ty(a));
|
fcx.write_ty(id, fcx.expr_ty(a));
|
||||||
}
|
}
|
||||||
|
@ -2163,15 +2164,13 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
ast::expr_match(discrim, ref arms) => {
|
ast::expr_match(discrim, ref arms) => {
|
||||||
bot = _match::check_match(fcx, expr, discrim, (/*bad*/copy *arms));
|
bot = _match::check_match(fcx, expr, discrim, (/*bad*/copy *arms));
|
||||||
}
|
}
|
||||||
ast::expr_fn(proto, ref decl, ref body, cap_clause) => {
|
ast::expr_fn(proto, ref decl, ref body) => {
|
||||||
check_expr_fn(fcx, expr, Some(proto),
|
check_expr_fn(fcx, expr, Some(proto),
|
||||||
decl, (*body), Vanilla, expected);
|
decl, (*body), Vanilla, expected);
|
||||||
capture::check_capture_clause(tcx, expr.id, cap_clause);
|
|
||||||
}
|
}
|
||||||
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
|
ast::expr_fn_block(ref decl, ref body) => {
|
||||||
check_expr_fn(fcx, expr, None,
|
check_expr_fn(fcx, expr, None,
|
||||||
decl, (*body), Vanilla, expected);
|
decl, (*body), Vanilla, expected);
|
||||||
capture::check_capture_clause(tcx, expr.id, cap_clause);
|
|
||||||
}
|
}
|
||||||
ast::expr_loop_body(b) => {
|
ast::expr_loop_body(b) => {
|
||||||
// a loop body is the special argument to a `for` loop. We know that
|
// a loop body is the special argument to a `for` loop. We know that
|
||||||
|
@ -2234,7 +2233,7 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match b.node {
|
match b.node {
|
||||||
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
|
ast::expr_fn_block(ref decl, ref body) => {
|
||||||
// If an error occurred, we pretend this isn't a for
|
// If an error occurred, we pretend this isn't a for
|
||||||
// loop, so as to assign types to all nodes while also
|
// loop, so as to assign types to all nodes while also
|
||||||
// propagating ty_err throughout so as to suppress
|
// propagating ty_err throughout so as to suppress
|
||||||
|
@ -2246,7 +2245,6 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
check_expr_fn(fcx, b, None,
|
check_expr_fn(fcx, b, None,
|
||||||
decl, *body, fn_kind, Some(inner_ty));
|
decl, *body, fn_kind, Some(inner_ty));
|
||||||
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
|
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
|
||||||
capture::check_capture_clause(tcx, b.id, cap_clause);
|
|
||||||
}
|
}
|
||||||
// argh
|
// argh
|
||||||
_ => fail ~"expr_fn_block"
|
_ => fail ~"expr_fn_block"
|
||||||
|
@ -2283,11 +2281,10 @@ pub fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match b.node {
|
match b.node {
|
||||||
ast::expr_fn_block(ref decl, ref body, cap_clause) => {
|
ast::expr_fn_block(ref decl, ref body) => {
|
||||||
check_expr_fn(fcx, b, None,
|
check_expr_fn(fcx, b, None,
|
||||||
decl, *body, DoBlock, Some(inner_ty));
|
decl, *body, DoBlock, Some(inner_ty));
|
||||||
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
|
demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b));
|
||||||
capture::check_capture_clause(tcx, b.id, cap_clause);
|
|
||||||
}
|
}
|
||||||
// argh
|
// argh
|
||||||
_ => fail ~"expected fn ty"
|
_ => fail ~"expected fn ty"
|
||||||
|
@ -3052,7 +3049,7 @@ pub fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
|
||||||
~"size_of" |
|
~"size_of" |
|
||||||
~"pref_align_of" | ~"min_align_of" => (1u, ~[], ty::mk_uint(ccx.tcx)),
|
~"pref_align_of" | ~"min_align_of" => (1u, ~[], ty::mk_uint(ccx.tcx)),
|
||||||
~"init" => (1u, ~[], param(ccx, 0u)),
|
~"init" => (1u, ~[], param(ccx, 0u)),
|
||||||
~"forget" => (1u, ~[arg(ast::by_move, param(ccx, 0u))],
|
~"forget" => (1u, ~[arg(ast::by_copy, param(ccx, 0u))],
|
||||||
ty::mk_nil(tcx)),
|
ty::mk_nil(tcx)),
|
||||||
~"reinterpret_cast" => (2u, ~[arg(ast::by_ref, param(ccx, 0u))],
|
~"reinterpret_cast" => (2u, ~[arg(ast::by_ref, param(ccx, 0u))],
|
||||||
param(ccx, 1u)),
|
param(ccx, 1u)),
|
||||||
|
@ -3062,7 +3059,7 @@ pub fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::foreign_item) {
|
||||||
(1u, ~[arg(ast::by_copy,
|
(1u, ~[arg(ast::by_copy,
|
||||||
ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)),
|
ty::mk_mut_rptr(tcx, ty::re_bound(ty::br_anon(0)),
|
||||||
param(ccx, 0u))),
|
param(ccx, 0u))),
|
||||||
arg(ast::by_move, param(ccx, 0u))],
|
arg(ast::by_copy, param(ccx, 0u))],
|
||||||
ty::mk_nil(tcx))
|
ty::mk_nil(tcx))
|
||||||
}
|
}
|
||||||
~"needs_drop" => (1u, ~[], ty::mk_bool(tcx)),
|
~"needs_drop" => (1u, ~[], ty::mk_bool(tcx)),
|
||||||
|
|
|
@ -710,7 +710,6 @@ pub mod guarantor {
|
||||||
ast::expr_do_body(*) |
|
ast::expr_do_body(*) |
|
||||||
ast::expr_block(*) |
|
ast::expr_block(*) |
|
||||||
ast::expr_copy(*) |
|
ast::expr_copy(*) |
|
||||||
ast::expr_unary_move(*) |
|
|
||||||
ast::expr_repeat(*) |
|
ast::expr_repeat(*) |
|
||||||
ast::expr_vec(*) => {
|
ast::expr_vec(*) => {
|
||||||
assert !ty::expr_is_lval(
|
assert !ty::expr_is_lval(
|
||||||
|
|
|
@ -606,10 +606,10 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
ty::ty_uniq(mt) => {
|
ty::ty_uniq(mt) => {
|
||||||
// Ensure that the trait vstore and the pointer
|
// Ensure that the trait vstore and the pointer
|
||||||
// type match.
|
// type match.
|
||||||
match (ty::get(ty).sty, vstore) {
|
match (&ty::get(ty).sty, vstore) {
|
||||||
(ty::ty_box(_), ty::vstore_box) |
|
(&ty::ty_box(_), ty::vstore_box) |
|
||||||
(ty::ty_uniq(_), ty::vstore_uniq) |
|
(&ty::ty_uniq(_), ty::vstore_uniq) |
|
||||||
(ty::ty_rptr(*), ty::vstore_slice(*)) => {
|
(&ty::ty_rptr(*), ty::vstore_slice(*)) => {
|
||||||
let location_info =
|
let location_info =
|
||||||
&location_info_for_expr(ex);
|
&location_info_for_expr(ex);
|
||||||
let vtable_opt =
|
let vtable_opt =
|
||||||
|
@ -634,8 +634,8 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
|
|
||||||
// Now, if this is &trait, we need to link
|
// Now, if this is &trait, we need to link
|
||||||
// the regions.
|
// the regions.
|
||||||
match (ty::get(ty).sty, vstore) {
|
match (&ty::get(ty).sty, vstore) {
|
||||||
(ty::ty_rptr(ra, _),
|
(&ty::ty_rptr(ra, _),
|
||||||
ty::vstore_slice(rb)) => {
|
ty::vstore_slice(rb)) => {
|
||||||
infer::mk_subr(fcx.infcx(),
|
infer::mk_subr(fcx.infcx(),
|
||||||
false,
|
false,
|
||||||
|
@ -646,7 +646,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ty::ty_box(_), _) => {
|
(&ty::ty_box(_), _) => {
|
||||||
fcx.ccx.tcx.sess.span_err(ex.span,
|
fcx.ccx.tcx.sess.span_err(ex.span,
|
||||||
~"must cast \
|
~"must cast \
|
||||||
a boxed \
|
a boxed \
|
||||||
|
@ -655,7 +655,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
trait");
|
trait");
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
(ty::ty_rptr(*), _) => {
|
(&ty::ty_rptr(*), _) => {
|
||||||
fcx.ccx.tcx.sess.span_err(ex.span,
|
fcx.ccx.tcx.sess.span_err(ex.span,
|
||||||
~"must cast \
|
~"must cast \
|
||||||
a borrowed \
|
a borrowed \
|
||||||
|
@ -663,7 +663,7 @@ pub fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
a borrowed \
|
a borrowed \
|
||||||
trait");
|
trait");
|
||||||
}
|
}
|
||||||
(ty::ty_uniq(*), _) => {
|
(&ty::ty_uniq(*), _) => {
|
||||||
fcx.ccx.tcx.sess.span_err(ex.span,
|
fcx.ccx.tcx.sess.span_err(ex.span,
|
||||||
~"must cast \
|
~"must cast \
|
||||||
a unique \
|
a unique \
|
||||||
|
|
|
@ -165,7 +165,7 @@ fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
|
||||||
resolve_method_map_entry(wbcx.fcx, e.span, e.id);
|
resolve_method_map_entry(wbcx.fcx, e.span, e.id);
|
||||||
resolve_method_map_entry(wbcx.fcx, e.span, e.callee_id);
|
resolve_method_map_entry(wbcx.fcx, e.span, e.callee_id);
|
||||||
match e.node {
|
match e.node {
|
||||||
ast::expr_fn_block(ref decl, _, _) => {
|
ast::expr_fn_block(ref decl, _) => {
|
||||||
for vec::each(decl.inputs) |input| {
|
for vec::each(decl.inputs) |input| {
|
||||||
let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);
|
let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id);
|
||||||
|
|
||||||
|
|
|
@ -362,11 +362,11 @@ pub fn super_lattice_tys<L:LatticeDir TyLatticeDir Combine>(
|
||||||
|
|
||||||
let tcx = self.infcx().tcx;
|
let tcx = self.infcx().tcx;
|
||||||
|
|
||||||
match (ty::get(a).sty, ty::get(b).sty) {
|
match (&ty::get(a).sty, &ty::get(b).sty) {
|
||||||
(ty::ty_bot, _) => { return self.ty_bot(b); }
|
(&ty::ty_bot, _) => { return self.ty_bot(b); }
|
||||||
(_, ty::ty_bot) => { return self.ty_bot(a); }
|
(_, &ty::ty_bot) => { return self.ty_bot(a); }
|
||||||
|
|
||||||
(ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => {
|
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
|
||||||
let r = if_ok!(lattice_vars(self, a_id, b_id,
|
let r = if_ok!(lattice_vars(self, a_id, b_id,
|
||||||
|x, y| self.tys(*x, *y)));
|
|x, y| self.tys(*x, *y)));
|
||||||
return match r {
|
return match r {
|
||||||
|
@ -375,12 +375,12 @@ pub fn super_lattice_tys<L:LatticeDir TyLatticeDir Combine>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::ty_infer(TyVar(a_id)), _) => {
|
(&ty::ty_infer(TyVar(a_id)), _) => {
|
||||||
return lattice_var_and_t(self, a_id, &b,
|
return lattice_var_and_t(self, a_id, &b,
|
||||||
|x, y| self.tys(*x, *y));
|
|x, y| self.tys(*x, *y));
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, ty::ty_infer(TyVar(b_id))) => {
|
(_, &ty::ty_infer(TyVar(b_id))) => {
|
||||||
return lattice_var_and_t(self, b_id, &a,
|
return lattice_var_and_t(self, b_id, &a,
|
||||||
|x, y| self.tys(*x, *y));
|
|x, y| self.tys(*x, *y));
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,25 +106,25 @@ pub impl Sub: Combine {
|
||||||
a.inf_str(self.infcx), b.inf_str(self.infcx));
|
a.inf_str(self.infcx), b.inf_str(self.infcx));
|
||||||
if a == b { return Ok(a); }
|
if a == b { return Ok(a); }
|
||||||
let _indenter = indenter();
|
let _indenter = indenter();
|
||||||
match (ty::get(a).sty, ty::get(b).sty) {
|
match (&ty::get(a).sty, &ty::get(b).sty) {
|
||||||
(ty::ty_bot, _) => {
|
(&ty::ty_bot, _) => {
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
(ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => {
|
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
|
||||||
if_ok!(self.var_sub_var(a_id, b_id));
|
if_ok!(self.var_sub_var(a_id, b_id));
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
(ty::ty_infer(TyVar(a_id)), _) => {
|
(&ty::ty_infer(TyVar(a_id)), _) => {
|
||||||
if_ok!(self.var_sub_t(a_id, b));
|
if_ok!(self.var_sub_t(a_id, b));
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
(_, ty::ty_infer(TyVar(b_id))) => {
|
(_, &ty::ty_infer(TyVar(b_id))) => {
|
||||||
if_ok!(self.t_sub_var(a, b_id));
|
if_ok!(self.t_sub_var(a, b_id));
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, ty::ty_bot) => {
|
(_, &ty::ty_bot) => {
|
||||||
Err(ty::terr_sorts(expected_found(&self, a, b)))
|
Err(ty::terr_sorts(expected_found(&self, a, b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,14 +91,13 @@ pub mod middle {
|
||||||
pub mod liveness;
|
pub mod liveness;
|
||||||
pub mod kind;
|
pub mod kind;
|
||||||
pub mod freevars;
|
pub mod freevars;
|
||||||
pub mod capture;
|
|
||||||
pub mod pat_util;
|
pub mod pat_util;
|
||||||
pub mod region;
|
pub mod region;
|
||||||
pub mod const_eval;
|
pub mod const_eval;
|
||||||
pub mod astencode;
|
pub mod astencode;
|
||||||
pub mod lang_items;
|
pub mod lang_items;
|
||||||
pub mod privacy;
|
pub mod privacy;
|
||||||
pub mod mode;
|
pub mod moves;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod front {
|
pub mod front {
|
||||||
|
@ -221,9 +220,9 @@ pub fn run_compiler(args: &~[~str], demitter: diagnostic::emitter) {
|
||||||
|
|
||||||
let matches =
|
let matches =
|
||||||
&match getopts::groups::getopts(args, optgroups()) {
|
&match getopts::groups::getopts(args, optgroups()) {
|
||||||
Ok(ref m) => (*m),
|
Ok(m) => m,
|
||||||
Err(ref f) => {
|
Err(f) => {
|
||||||
early_error(demitter, getopts::fail_str((*f)))
|
early_error(demitter, getopts::fail_str(f));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -375,7 +374,7 @@ pub fn monitor(+f: fn~(diagnostic::emitter)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let mut args = os::args();
|
let args = os::args();
|
||||||
do monitor |move args, demitter| {
|
do monitor |move args, demitter| {
|
||||||
run_compiler(&args, demitter);
|
run_compiler(&args, demitter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,8 +421,15 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
|
||||||
}
|
}
|
||||||
ty_infer(infer_ty) => infer_ty.to_str(),
|
ty_infer(infer_ty) => infer_ty.to_str(),
|
||||||
ty_err => ~"[type error]",
|
ty_err => ~"[type error]",
|
||||||
ty_param(param_ty {idx: id, _}) => {
|
ty_param(param_ty {idx: id, def_id: did}) => {
|
||||||
~"'" + str::from_bytes(~[('a' as u8) + (id as u8)])
|
if cx.sess.verbose() {
|
||||||
|
fmt!("'%s:%?",
|
||||||
|
str::from_bytes(~[('a' as u8) + (id as u8)]),
|
||||||
|
did)
|
||||||
|
} else {
|
||||||
|
fmt!("'%s",
|
||||||
|
str::from_bytes(~[('a' as u8) + (id as u8)]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ty_self => ~"self",
|
ty_self => ~"self",
|
||||||
ty_enum(did, ref substs) | ty_struct(did, ref substs) => {
|
ty_enum(did, ref substs) | ty_struct(did, ref substs) => {
|
||||||
|
|
|
@ -166,20 +166,27 @@ fn fold_enum(
|
||||||
doc::EnumDoc {
|
doc::EnumDoc {
|
||||||
variants: do par::map(doc.variants) |variant| {
|
variants: do par::map(doc.variants) |variant| {
|
||||||
let variant = copy *variant;
|
let variant = copy *variant;
|
||||||
let desc = do astsrv::exec(srv) |ctxt, copy variant| {
|
let desc = {
|
||||||
match ctxt.ast_map.get(doc_id) {
|
let variant = copy variant;
|
||||||
ast_map::node_item(@ast::item {
|
do astsrv::exec(srv) |ctxt| {
|
||||||
node: ast::item_enum(ref enum_definition, _), _
|
match ctxt.ast_map.get(doc_id) {
|
||||||
}, _) => {
|
ast_map::node_item(@ast::item {
|
||||||
let ast_variant = option::get(
|
node: ast::item_enum(ref enum_definition, _), _
|
||||||
vec::find(enum_definition.variants, |v| {
|
}, _) => {
|
||||||
to_str(v.node.name) == variant.name
|
let ast_variant = option::get(
|
||||||
}));
|
vec::find(enum_definition.variants, |v| {
|
||||||
|
to_str(v.node.name) == variant.name
|
||||||
|
}));
|
||||||
|
|
||||||
attr_parser::parse_desc(copy ast_variant.node.attrs)
|
attr_parser::parse_desc(
|
||||||
}
|
copy ast_variant.node.attrs)
|
||||||
_ => fail fmt!("Enum variant %s has id that's not bound \
|
}
|
||||||
to an enum item", variant.name)
|
_ => {
|
||||||
|
fail fmt!("Enum variant %s has id that's \
|
||||||
|
not bound to an enum item",
|
||||||
|
variant.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ use pass::Pass;
|
||||||
|
|
||||||
use core::str;
|
use core::str;
|
||||||
use core::vec;
|
use core::vec;
|
||||||
|
use core::util;
|
||||||
use std::par;
|
use std::par;
|
||||||
|
|
||||||
pub fn mk_pass() -> Pass {
|
pub fn mk_pass() -> Pass {
|
||||||
|
@ -194,8 +195,8 @@ fn paragraphs(s: ~str) -> ~[~str] {
|
||||||
} else {
|
} else {
|
||||||
if whitespace_lines > 0 {
|
if whitespace_lines > 0 {
|
||||||
if !accum.is_empty() {
|
if !accum.is_empty() {
|
||||||
res += ~[accum];
|
let v = util::replace(&mut accum, ~"");
|
||||||
accum = ~"";
|
res.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,10 +102,11 @@ fn item_to_entry(
|
||||||
let link = match doc {
|
let link = match doc {
|
||||||
doc::ModTag(_) | doc::NmodTag(_)
|
doc::ModTag(_) | doc::NmodTag(_)
|
||||||
if config.output_style == config::DocPerMod => {
|
if config.output_style == config::DocPerMod => {
|
||||||
markdown_writer::make_filename(config, doc::ItemPage(doc)).to_str()
|
markdown_writer::make_filename(config,
|
||||||
|
doc::ItemPage(copy doc)).to_str()
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
~"#" + pandoc_header_id(markdown_pass::header_text(doc))
|
~"#" + pandoc_header_id(markdown_pass::header_text(copy doc))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -291,12 +291,13 @@ pub fn header_text(doc: doc::ItemTag) -> ~str {
|
||||||
fmt!("of `%s` for `%s`", ImplDoc.trait_types[0],
|
fmt!("of `%s` for `%s`", ImplDoc.trait_types[0],
|
||||||
(&ImplDoc.self_ty).get())
|
(&ImplDoc.self_ty).get())
|
||||||
};
|
};
|
||||||
fmt!("%s %s", header_kind, desc)
|
return fmt!("%s %s", header_kind, desc);
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
header_text_(header_kind(doc), header_name(doc))
|
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header_text_(header_kind(copy doc),
|
||||||
|
header_name(doc))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_text_(kind: &str, name: &str) -> ~str {
|
fn header_text_(kind: &str, name: &str) -> ~str {
|
||||||
|
|
|
@ -73,7 +73,7 @@ fn fold_item(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_to_sections(
|
fn apply_to_sections(
|
||||||
op: NominalOp<Op>,
|
+op: NominalOp<Op>,
|
||||||
sections: ~[doc::Section]
|
sections: ~[doc::Section]
|
||||||
) -> ~[doc::Section] {
|
) -> ~[doc::Section] {
|
||||||
par::map(sections, |section, copy op| doc::Section {
|
par::map(sections, |section, copy op| doc::Section {
|
||||||
|
@ -115,7 +115,8 @@ fn apply_to_methods(
|
||||||
op: NominalOp<Op>,
|
op: NominalOp<Op>,
|
||||||
docs: ~[doc::MethodDoc]
|
docs: ~[doc::MethodDoc]
|
||||||
) -> ~[doc::MethodDoc] {
|
) -> ~[doc::MethodDoc] {
|
||||||
do par::map(docs) |doc, copy op| {
|
let op = copy op;
|
||||||
|
do par::map(docs) |doc| {
|
||||||
doc::MethodDoc {
|
doc::MethodDoc {
|
||||||
brief: maybe_apply_op(copy op, &doc.brief),
|
brief: maybe_apply_op(copy op, &doc.brief),
|
||||||
desc: maybe_apply_op(copy op, &doc.desc),
|
desc: maybe_apply_op(copy op, &doc.desc),
|
||||||
|
|
|
@ -103,16 +103,18 @@ fn fold_const(
|
||||||
let srv = fold.ctxt;
|
let srv = fold.ctxt;
|
||||||
|
|
||||||
doc::SimpleItemDoc {
|
doc::SimpleItemDoc {
|
||||||
sig: Some(do astsrv::exec(srv) |copy doc, ctxt| {
|
sig: Some({
|
||||||
match ctxt.ast_map.get(doc.id()) {
|
let doc = copy doc;
|
||||||
ast_map::node_item(@ast::item {
|
do astsrv::exec(srv) |ctxt| {
|
||||||
node: ast::item_const(ty, _), _
|
match ctxt.ast_map.get(doc.id()) {
|
||||||
}, _) => {
|
ast_map::node_item(@ast::item {
|
||||||
pprust::ty_to_str(ty, extract::interner())
|
node: ast::item_const(ty, _), _
|
||||||
}
|
}, _) => {
|
||||||
_ => fail ~"fold_const: id not bound to a const item"
|
pprust::ty_to_str(ty, extract::interner())
|
||||||
}
|
}
|
||||||
}),
|
_ => fail ~"fold_const: id not bound to a const item"
|
||||||
|
}
|
||||||
|
}}),
|
||||||
.. doc
|
.. doc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,26 +134,29 @@ fn fold_enum(
|
||||||
|
|
||||||
doc::EnumDoc {
|
doc::EnumDoc {
|
||||||
variants: do par::map(doc.variants) |variant| {
|
variants: do par::map(doc.variants) |variant| {
|
||||||
let variant = copy *variant;
|
let sig = {
|
||||||
let sig = do astsrv::exec(srv) |copy variant, ctxt| {
|
let variant = copy *variant;
|
||||||
match ctxt.ast_map.get(doc_id) {
|
do astsrv::exec(srv) |copy variant, ctxt| {
|
||||||
ast_map::node_item(@ast::item {
|
match ctxt.ast_map.get(doc_id) {
|
||||||
node: ast::item_enum(ref enum_definition, _), _
|
ast_map::node_item(@ast::item {
|
||||||
}, _) => {
|
node: ast::item_enum(ref enum_definition, _), _
|
||||||
let ast_variant =
|
}, _) => {
|
||||||
do vec::find(enum_definition.variants) |v| {
|
let ast_variant =
|
||||||
to_str(v.node.name) == variant.name
|
do vec::find(enum_definition.variants) |v| {
|
||||||
}.get();
|
to_str(v.node.name) == variant.name
|
||||||
|
}.get();
|
||||||
|
|
||||||
pprust::variant_to_str(ast_variant, extract::interner())
|
pprust::variant_to_str(
|
||||||
}
|
ast_variant, extract::interner())
|
||||||
_ => fail ~"enum variant not bound to an enum item"
|
}
|
||||||
|
_ => fail ~"enum variant not bound to an enum item"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
doc::VariantDoc {
|
doc::VariantDoc {
|
||||||
sig: Some(sig),
|
sig: Some(sig),
|
||||||
.. variant
|
.. copy *variant
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.. doc
|
.. doc
|
||||||
|
@ -262,18 +267,22 @@ fn fold_impl(
|
||||||
|
|
||||||
let srv = fold.ctxt;
|
let srv = fold.ctxt;
|
||||||
|
|
||||||
let (trait_types, self_ty) = do astsrv::exec(srv) |copy doc, ctxt| {
|
let (trait_types, self_ty) = {
|
||||||
match ctxt.ast_map.get(doc.id()) {
|
let doc = copy doc;
|
||||||
ast_map::node_item(@ast::item {
|
do astsrv::exec(srv) |ctxt| {
|
||||||
node: ast::item_impl(_, opt_trait_type, self_ty, _), _
|
match ctxt.ast_map.get(doc.id()) {
|
||||||
}, _) => {
|
ast_map::node_item(@ast::item {
|
||||||
let trait_types = opt_trait_type.map_default(~[], |p| {
|
node: ast::item_impl(_, opt_trait_type, self_ty, _), _
|
||||||
~[pprust::path_to_str(p.path, extract::interner())]
|
}, _) => {
|
||||||
});
|
let trait_types = opt_trait_type.map_default(~[], |p| {
|
||||||
(trait_types, Some(pprust::ty_to_str(self_ty,
|
~[pprust::path_to_str(p.path, extract::interner())]
|
||||||
extract::interner())))
|
});
|
||||||
}
|
(trait_types,
|
||||||
_ => fail ~"expected impl"
|
Some(pprust::ty_to_str(
|
||||||
|
self_ty, extract::interner())))
|
||||||
|
}
|
||||||
|
_ => fail ~"expected impl"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,20 +327,25 @@ fn fold_type(
|
||||||
let srv = fold.ctxt;
|
let srv = fold.ctxt;
|
||||||
|
|
||||||
doc::SimpleItemDoc {
|
doc::SimpleItemDoc {
|
||||||
sig: do astsrv::exec(srv) |copy doc, ctxt| {
|
sig: {
|
||||||
match ctxt.ast_map.get(doc.id()) {
|
let doc = copy doc;
|
||||||
ast_map::node_item(@ast::item {
|
do astsrv::exec(srv) |ctxt| {
|
||||||
ident: ident,
|
match ctxt.ast_map.get(doc.id()) {
|
||||||
node: ast::item_ty(ty, ref params), _
|
ast_map::node_item(@ast::item {
|
||||||
}, _) => {
|
ident: ident,
|
||||||
Some(fmt!(
|
node: ast::item_ty(ty, ref params), _
|
||||||
"type %s%s = %s",
|
}, _) => {
|
||||||
to_str(ident),
|
Some(fmt!(
|
||||||
pprust::typarams_to_str(*params, extract::interner()),
|
"type %s%s = %s",
|
||||||
pprust::ty_to_str(ty, extract::interner())
|
to_str(ident),
|
||||||
))
|
pprust::typarams_to_str(*params,
|
||||||
}
|
extract::interner()),
|
||||||
_ => fail ~"expected type"
|
pprust::ty_to_str(ty,
|
||||||
|
extract::interner())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => fail ~"expected type"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.. doc
|
.. doc
|
||||||
|
@ -351,14 +365,17 @@ fn fold_struct(
|
||||||
let srv = fold.ctxt;
|
let srv = fold.ctxt;
|
||||||
|
|
||||||
doc::StructDoc {
|
doc::StructDoc {
|
||||||
sig: do astsrv::exec(srv) |copy doc, ctxt| {
|
sig: {
|
||||||
match ctxt.ast_map.get(doc.id()) {
|
let doc = copy doc;
|
||||||
ast_map::node_item(item, _) => {
|
do astsrv::exec(srv) |ctxt| {
|
||||||
let item = strip_struct_extra_stuff(item);
|
match ctxt.ast_map.get(doc.id()) {
|
||||||
Some(pprust::item_to_str(item,
|
ast_map::node_item(item, _) => {
|
||||||
extract::interner()))
|
let item = strip_struct_extra_stuff(item);
|
||||||
|
Some(pprust::item_to_str(item,
|
||||||
|
extract::interner()))
|
||||||
|
}
|
||||||
|
_ => fail ~"not an item"
|
||||||
}
|
}
|
||||||
_ => fail ~"not an item"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.. doc
|
.. doc
|
||||||
|
|
|
@ -788,13 +788,14 @@ mod test {
|
||||||
// The main task will wait until the test is over to proceed
|
// The main task will wait until the test is over to proceed
|
||||||
let (finish_port, finish_chan) = pipes::stream();
|
let (finish_port, finish_chan) = pipes::stream();
|
||||||
|
|
||||||
let addr = ip::v4::parse_addr("127.0.0.1");
|
let addr0 = ip::v4::parse_addr("127.0.0.1");
|
||||||
|
|
||||||
let begin_connect_chan = Cell(move begin_connect_chan);
|
let begin_connect_chan = Cell(move begin_connect_chan);
|
||||||
let accept_chan = Cell(move accept_chan);
|
let accept_chan = Cell(move accept_chan);
|
||||||
|
|
||||||
// The server task
|
// The server task
|
||||||
do task::spawn |copy addr, move begin_connect_chan,
|
let addr = copy addr0;
|
||||||
|
do task::spawn |move begin_connect_chan,
|
||||||
move accept_chan| {
|
move accept_chan| {
|
||||||
let iotask = &uv::global_loop::get();
|
let iotask = &uv::global_loop::get();
|
||||||
let begin_connect_chan = begin_connect_chan.take();
|
let begin_connect_chan = begin_connect_chan.take();
|
||||||
|
@ -821,7 +822,8 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client task
|
// Client task
|
||||||
do task::spawn |copy addr, move begin_connect_port,
|
let addr = copy addr0;
|
||||||
|
do task::spawn |move begin_connect_port,
|
||||||
move writer_chan| {
|
move writer_chan| {
|
||||||
|
|
||||||
// Wait for the server to start listening
|
// Wait for the server to start listening
|
||||||
|
|
|
@ -117,8 +117,9 @@ enum IpGetAddrErr {
|
||||||
pub fn get_addr(node: &str, iotask: &iotask)
|
pub fn get_addr(node: &str, iotask: &iotask)
|
||||||
-> result::Result<~[IpAddr], IpGetAddrErr> {
|
-> result::Result<~[IpAddr], IpGetAddrErr> {
|
||||||
let (output_po, output_ch) = stream();
|
let (output_po, output_ch) = stream();
|
||||||
let output_ch = SharedChan(output_ch);
|
let mut output_ch = Some(SharedChan(output_ch));
|
||||||
do str::as_buf(node) |node_ptr, len| {
|
do str::as_buf(node) |node_ptr, len| {
|
||||||
|
let output_ch = output_ch.swap_unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
log(debug, fmt!("slice len %?", len));
|
log(debug, fmt!("slice len %?", len));
|
||||||
let handle = create_uv_getaddrinfo_t();
|
let handle = create_uv_getaddrinfo_t();
|
||||||
|
|
|
@ -59,7 +59,8 @@ fn map_slices<A: Copy Owned, B: Copy Owned>(
|
||||||
let end = uint::min(len, base + items_per_task);
|
let end = uint::min(len, base + items_per_task);
|
||||||
do vec::as_imm_buf(xs) |p, _len| {
|
do vec::as_imm_buf(xs) |p, _len| {
|
||||||
let f = f();
|
let f = f();
|
||||||
let f = do future_spawn() |move f, copy base| {
|
let base = base;
|
||||||
|
let f = do future_spawn() |move f| {
|
||||||
unsafe {
|
unsafe {
|
||||||
let len = end - base;
|
let len = end - base;
|
||||||
let slice = (ptr::offset(p, base),
|
let slice = (ptr::offset(p, base),
|
||||||
|
@ -94,7 +95,8 @@ fn map_slices<A: Copy Owned, B: Copy Owned>(
|
||||||
pub fn map<A: Copy Owned, B: Copy Owned>(
|
pub fn map<A: Copy Owned, B: Copy Owned>(
|
||||||
xs: &[A], f: fn~(&A) -> B) -> ~[B] {
|
xs: &[A], f: fn~(&A) -> B) -> ~[B] {
|
||||||
vec::concat(map_slices(xs, || {
|
vec::concat(map_slices(xs, || {
|
||||||
fn~(_base: uint, slice : &[A], copy f) -> ~[B] {
|
let f = copy f;
|
||||||
|
fn~(_base: uint, slice : &[A]) -> ~[B] {
|
||||||
vec::map(slice, |x| f(x))
|
vec::map(slice, |x| f(x))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -104,6 +106,7 @@ pub fn map<A: Copy Owned, B: Copy Owned>(
|
||||||
pub fn mapi<A: Copy Owned, B: Copy Owned>(xs: &[A],
|
pub fn mapi<A: Copy Owned, B: Copy Owned>(xs: &[A],
|
||||||
f: fn~(uint, &A) -> B) -> ~[B] {
|
f: fn~(uint, &A) -> B) -> ~[B] {
|
||||||
let slices = map_slices(xs, || {
|
let slices = map_slices(xs, || {
|
||||||
|
let f = copy f;
|
||||||
fn~(base: uint, slice : &[A], copy f) -> ~[B] {
|
fn~(base: uint, slice : &[A], copy f) -> ~[B] {
|
||||||
vec::mapi(slice, |i, x| {
|
vec::mapi(slice, |i, x| {
|
||||||
f(i + base, x)
|
f(i + base, x)
|
||||||
|
@ -141,6 +144,7 @@ pub fn mapi_factory<A: Copy Owned, B: Copy Owned>(
|
||||||
/// Returns true if the function holds for all elements in the vector.
|
/// Returns true if the function holds for all elements in the vector.
|
||||||
pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool {
|
pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool {
|
||||||
do vec::all(map_slices(xs, || {
|
do vec::all(map_slices(xs, || {
|
||||||
|
let f = copy f;
|
||||||
fn~(base: uint, slice : &[A], copy f) -> bool {
|
fn~(base: uint, slice : &[A], copy f) -> bool {
|
||||||
vec::alli(slice, |i, x| {
|
vec::alli(slice, |i, x| {
|
||||||
f(i + base, x)
|
f(i + base, x)
|
||||||
|
@ -152,6 +156,7 @@ pub fn alli<A: Copy Owned>(xs: &[A], f: fn~(uint, &A) -> bool) -> bool {
|
||||||
/// Returns true if the function holds for any elements in the vector.
|
/// Returns true if the function holds for any elements in the vector.
|
||||||
pub fn any<A: Copy Owned>(xs: &[A], f: fn~(&A) -> bool) -> bool {
|
pub fn any<A: Copy Owned>(xs: &[A], f: fn~(&A) -> bool) -> bool {
|
||||||
do vec::any(map_slices(xs, || {
|
do vec::any(map_slices(xs, || {
|
||||||
|
let f = copy f;
|
||||||
fn~(_base : uint, slice: &[A], copy f) -> bool {
|
fn~(_base : uint, slice: &[A], copy f) -> bool {
|
||||||
vec::any(slice, |x| f(x))
|
vec::any(slice, |x| f(x))
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,9 +337,9 @@ pub struct field_pat {
|
||||||
|
|
||||||
#[auto_encode]
|
#[auto_encode]
|
||||||
#[auto_decode]
|
#[auto_decode]
|
||||||
|
#[deriving_eq]
|
||||||
pub enum binding_mode {
|
pub enum binding_mode {
|
||||||
bind_by_value,
|
bind_by_copy,
|
||||||
bind_by_move,
|
|
||||||
bind_by_ref(mutability),
|
bind_by_ref(mutability),
|
||||||
bind_infer
|
bind_infer
|
||||||
}
|
}
|
||||||
|
@ -347,51 +347,17 @@ pub enum binding_mode {
|
||||||
pub impl binding_mode : to_bytes::IterBytes {
|
pub impl binding_mode : to_bytes::IterBytes {
|
||||||
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
|
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
|
||||||
match *self {
|
match *self {
|
||||||
bind_by_value => 0u8.iter_bytes(lsb0, f),
|
bind_by_copy => 0u8.iter_bytes(lsb0, f),
|
||||||
|
|
||||||
bind_by_move => 1u8.iter_bytes(lsb0, f),
|
|
||||||
|
|
||||||
bind_by_ref(ref m) =>
|
bind_by_ref(ref m) =>
|
||||||
to_bytes::iter_bytes_2(&2u8, m, lsb0, f),
|
to_bytes::iter_bytes_2(&1u8, m, lsb0, f),
|
||||||
|
|
||||||
bind_infer =>
|
bind_infer =>
|
||||||
3u8.iter_bytes(lsb0, f),
|
2u8.iter_bytes(lsb0, f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl binding_mode : cmp::Eq {
|
|
||||||
pure fn eq(&self, other: &binding_mode) -> bool {
|
|
||||||
match (*self) {
|
|
||||||
bind_by_value => {
|
|
||||||
match (*other) {
|
|
||||||
bind_by_value => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bind_by_move => {
|
|
||||||
match (*other) {
|
|
||||||
bind_by_move => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bind_by_ref(e0a) => {
|
|
||||||
match (*other) {
|
|
||||||
bind_by_ref(e0b) => e0a == e0b,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bind_infer => {
|
|
||||||
match (*other) {
|
|
||||||
bind_infer => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pure fn ne(&self, other: &binding_mode) -> bool { !(*self).eq(other) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[auto_encode]
|
#[auto_encode]
|
||||||
#[auto_decode]
|
#[auto_decode]
|
||||||
pub enum pat_ {
|
pub enum pat_ {
|
||||||
|
@ -603,7 +569,7 @@ pub impl<T:cmp::Eq> inferable<T> : cmp::Eq {
|
||||||
// "resolved" mode: the real modes.
|
// "resolved" mode: the real modes.
|
||||||
#[auto_encode]
|
#[auto_encode]
|
||||||
#[auto_decode]
|
#[auto_decode]
|
||||||
pub enum rmode { by_ref, by_val, by_move, by_copy }
|
pub enum rmode { by_ref, by_val, by_copy }
|
||||||
|
|
||||||
pub impl rmode : to_bytes::IterBytes {
|
pub impl rmode : to_bytes::IterBytes {
|
||||||
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
|
pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) {
|
||||||
|
@ -729,8 +695,8 @@ pub enum expr_ {
|
||||||
(implicit) condition is always true. */
|
(implicit) condition is always true. */
|
||||||
expr_loop(blk, Option<ident>),
|
expr_loop(blk, Option<ident>),
|
||||||
expr_match(@expr, ~[arm]),
|
expr_match(@expr, ~[arm]),
|
||||||
expr_fn(Proto, fn_decl, blk, capture_clause),
|
expr_fn(Proto, fn_decl, blk),
|
||||||
expr_fn_block(fn_decl, blk, capture_clause),
|
expr_fn_block(fn_decl, blk),
|
||||||
// Inner expr is always an expr_fn_block. We need the wrapping node to
|
// Inner expr is always an expr_fn_block. We need the wrapping node to
|
||||||
// easily type this (a function returning nil on the inside but bool on
|
// easily type this (a function returning nil on the inside but bool on
|
||||||
// the outside).
|
// the outside).
|
||||||
|
@ -740,7 +706,6 @@ pub enum expr_ {
|
||||||
expr_block(blk),
|
expr_block(blk),
|
||||||
|
|
||||||
expr_copy(@expr),
|
expr_copy(@expr),
|
||||||
expr_unary_move(@expr),
|
|
||||||
expr_assign(@expr, @expr),
|
expr_assign(@expr, @expr),
|
||||||
expr_swap(@expr, @expr),
|
expr_swap(@expr, @expr),
|
||||||
expr_assign_op(binop, @expr, @expr),
|
expr_assign_op(binop, @expr, @expr),
|
||||||
|
@ -769,20 +734,6 @@ pub enum expr_ {
|
||||||
expr_paren(@expr)
|
expr_paren(@expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[auto_encode]
|
|
||||||
#[auto_decode]
|
|
||||||
pub struct capture_item_ {
|
|
||||||
id: int,
|
|
||||||
is_move: bool,
|
|
||||||
name: ident, // Currently, can only capture a local var.
|
|
||||||
span: span,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type capture_item = @capture_item_;
|
|
||||||
|
|
||||||
pub type capture_clause = @~[capture_item];
|
|
||||||
|
|
||||||
//
|
|
||||||
// When the main rust parser encounters a syntax-extension invocation, it
|
// When the main rust parser encounters a syntax-extension invocation, it
|
||||||
// parses the arguments to the invocation as a token-tree. This is a very
|
// parses the arguments to the invocation as a token-tree. This is a very
|
||||||
// loose structure, such that all sorts of different AST-fragments can
|
// loose structure, such that all sorts of different AST-fragments can
|
||||||
|
|
|
@ -256,7 +256,7 @@ pub fn ident_to_path(s: span, +i: ident) -> @path {
|
||||||
|
|
||||||
pub fn ident_to_pat(id: node_id, s: span, +i: ident) -> @pat {
|
pub fn ident_to_pat(id: node_id, s: span, +i: ident) -> @pat {
|
||||||
@ast::pat { id: id,
|
@ast::pat { id: id,
|
||||||
node: pat_ident(bind_by_value, ident_to_path(s, i), None),
|
node: pat_ident(bind_by_copy, ident_to_path(s, i), None),
|
||||||
span: s }
|
span: s }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,11 +503,8 @@ pub fn id_visitor(vfn: fn@(node_id)) -> visit::vt<()> {
|
||||||
vfn(m.self_id);
|
vfn(m.self_id);
|
||||||
for vec::each(tps) |tp| { vfn(tp.id); }
|
for vec::each(tps) |tp| { vfn(tp.id); }
|
||||||
}
|
}
|
||||||
visit::fk_anon(_, capture_clause) |
|
visit::fk_anon(_) |
|
||||||
visit::fk_fn_block(capture_clause) => {
|
visit::fk_fn_block => {
|
||||||
for vec::each(*capture_clause) |clause| {
|
|
||||||
vfn(clause.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,8 +173,8 @@ pub fn find_meta_items_by_name(metas: &[@ast::meta_item], name: &str) ->
|
||||||
* Returns true if a list of meta items contains another meta item. The
|
* Returns true if a list of meta items contains another meta item. The
|
||||||
* comparison is performed structurally.
|
* comparison is performed structurally.
|
||||||
*/
|
*/
|
||||||
pub fn contains(haystack: ~[@ast::meta_item], needle: @ast::meta_item)
|
pub fn contains(haystack: &[@ast::meta_item],
|
||||||
-> bool {
|
needle: @ast::meta_item) -> bool {
|
||||||
for haystack.each |item| {
|
for haystack.each |item| {
|
||||||
if eq(*item, needle) { return true; }
|
if eq(*item, needle) { return true; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -569,7 +569,7 @@ fn mk_ser_method(
|
||||||
pat: @ast::pat {
|
pat: @ast::pat {
|
||||||
id: cx.next_id(),
|
id: cx.next_id(),
|
||||||
node: ast::pat_ident(
|
node: ast::pat_ident(
|
||||||
ast::bind_by_value,
|
ast::bind_by_copy,
|
||||||
ast_util::ident_to_path(span, cx.ident_of(~"__s")),
|
ast_util::ident_to_path(span, cx.ident_of(~"__s")),
|
||||||
None),
|
None),
|
||||||
span: span,
|
span: span,
|
||||||
|
@ -633,7 +633,7 @@ fn mk_deser_method(
|
||||||
pat: @ast::pat {
|
pat: @ast::pat {
|
||||||
id: cx.next_id(),
|
id: cx.next_id(),
|
||||||
node: ast::pat_ident(
|
node: ast::pat_ident(
|
||||||
ast::bind_by_value,
|
ast::bind_by_copy,
|
||||||
ast_util::ident_to_path(span, cx.ident_of(~"__d")),
|
ast_util::ident_to_path(span, cx.ident_of(~"__d")),
|
||||||
None),
|
None),
|
||||||
span: span,
|
span: span,
|
||||||
|
@ -1095,7 +1095,7 @@ fn mk_enum_deser_body(
|
||||||
pat: @ast::pat {
|
pat: @ast::pat {
|
||||||
id: cx.next_id(),
|
id: cx.next_id(),
|
||||||
node: ast::pat_ident(
|
node: ast::pat_ident(
|
||||||
ast::bind_by_value,
|
ast::bind_by_copy,
|
||||||
ast_util::ident_to_path(span, cx.ident_of(~"i")),
|
ast_util::ident_to_path(span, cx.ident_of(~"i")),
|
||||||
None),
|
None),
|
||||||
span: span,
|
span: span,
|
||||||
|
@ -1114,8 +1114,7 @@ fn mk_enum_deser_body(
|
||||||
span,
|
span,
|
||||||
ast::expr_match(cx.expr_var(span, ~"i"), arms)
|
ast::expr_match(cx.expr_var(span, ~"i"), arms)
|
||||||
)
|
)
|
||||||
),
|
)
|
||||||
@~[]
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ pub fn mk_local(cx: ext_ctxt, sp: span, mutbl: bool,
|
||||||
let pat = @ast::pat {
|
let pat = @ast::pat {
|
||||||
id: cx.next_id(),
|
id: cx.next_id(),
|
||||||
node: ast::pat_ident(
|
node: ast::pat_ident(
|
||||||
ast::bind_by_value,
|
ast::bind_by_copy,
|
||||||
mk_raw_path(sp, ~[ident]),
|
mk_raw_path(sp, ~[ident]),
|
||||||
None),
|
None),
|
||||||
span: sp,
|
span: sp,
|
||||||
|
@ -279,9 +279,8 @@ pub fn mk_pat(cx: ext_ctxt, span: span, +pat: ast::pat_) -> @ast::pat {
|
||||||
}
|
}
|
||||||
pub fn mk_pat_ident(cx: ext_ctxt,
|
pub fn mk_pat_ident(cx: ext_ctxt,
|
||||||
span: span,
|
span: span,
|
||||||
ident: ast::ident)
|
ident: ast::ident) -> @ast::pat {
|
||||||
-> @ast::pat {
|
mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_copy)
|
||||||
mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_value)
|
|
||||||
}
|
}
|
||||||
pub fn mk_pat_ident_with_binding_mode(cx: ext_ctxt,
|
pub fn mk_pat_ident_with_binding_mode(cx: ext_ctxt,
|
||||||
span: span,
|
span: span,
|
||||||
|
|
|
@ -105,7 +105,6 @@ pub trait ext_ctxt_ast_builder {
|
||||||
fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt;
|
fn stmt_let(ident: ident, e: @ast::expr) -> @ast::stmt;
|
||||||
fn stmt_expr(e: @ast::expr) -> @ast::stmt;
|
fn stmt_expr(e: @ast::expr) -> @ast::stmt;
|
||||||
fn block_expr(b: ast::blk) -> @ast::expr;
|
fn block_expr(b: ast::blk) -> @ast::expr;
|
||||||
fn move_expr(e: @ast::expr) -> @ast::expr;
|
|
||||||
fn ty_option(ty: @ast::Ty) -> @ast::Ty;
|
fn ty_option(ty: @ast::Ty) -> @ast::Ty;
|
||||||
fn ty_infer() -> @ast::Ty;
|
fn ty_infer() -> @ast::Ty;
|
||||||
fn ty_nil_ast_builder() -> @ast::Ty;
|
fn ty_nil_ast_builder() -> @ast::Ty;
|
||||||
|
@ -130,15 +129,6 @@ pub impl ext_ctxt: ext_ctxt_ast_builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_expr(e: @ast::expr) -> @ast::expr {
|
|
||||||
@expr {
|
|
||||||
id: self.next_id(),
|
|
||||||
callee_id: self.next_id(),
|
|
||||||
node: ast::expr_unary_move(e),
|
|
||||||
span: e.span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stmt_expr(e: @ast::expr) -> @ast::stmt {
|
fn stmt_expr(e: @ast::expr) -> @ast::stmt {
|
||||||
@spanned { node: ast::stmt_expr(e, self.next_id()),
|
@spanned { node: ast::stmt_expr(e, self.next_id()),
|
||||||
span: dummy_sp()}
|
span: dummy_sp()}
|
||||||
|
@ -205,7 +195,7 @@ pub impl ext_ctxt: ext_ctxt_ast_builder {
|
||||||
pat: @ast::pat {
|
pat: @ast::pat {
|
||||||
id: self.next_id(),
|
id: self.next_id(),
|
||||||
node: ast::pat_ident(
|
node: ast::pat_ident(
|
||||||
ast::bind_by_value,
|
ast::bind_by_copy,
|
||||||
ast_util::ident_to_path(dummy_sp(), name),
|
ast_util::ident_to_path(dummy_sp(), name),
|
||||||
None),
|
None),
|
||||||
span: dummy_sp(),
|
span: dummy_sp(),
|
||||||
|
|
|
@ -466,30 +466,17 @@ pub fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
|
||||||
expr_match(fld.fold_expr(expr),
|
expr_match(fld.fold_expr(expr),
|
||||||
vec::map((*arms), |x| fld.fold_arm(*x)))
|
vec::map((*arms), |x| fld.fold_arm(*x)))
|
||||||
}
|
}
|
||||||
expr_fn(proto, decl, ref body, captures) => {
|
expr_fn(proto, decl, ref body) => {
|
||||||
let captures = do captures.map |cap_item| {
|
expr_fn(proto,
|
||||||
@ast::capture_item_ {
|
fold_fn_decl(decl, fld),
|
||||||
id: fld.new_id(cap_item.id),
|
fld.fold_block(*body))
|
||||||
..**cap_item
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expr_fn(proto, fold_fn_decl(decl, fld),
|
|
||||||
fld.fold_block((*body)),
|
|
||||||
@captures)
|
|
||||||
}
|
}
|
||||||
expr_fn_block(decl, ref body, captures) => {
|
expr_fn_block(decl, ref body) => {
|
||||||
let captures = do captures.map |cap_item| {
|
expr_fn_block(fold_fn_decl(decl, fld),
|
||||||
@ast::capture_item_ {
|
fld.fold_block(*body))
|
||||||
id: fld.new_id(cap_item.id),
|
|
||||||
..**cap_item
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expr_fn_block(fold_fn_decl(decl, fld), fld.fold_block((*body)),
|
|
||||||
@captures)
|
|
||||||
}
|
}
|
||||||
expr_block(ref blk) => expr_block(fld.fold_block((*blk))),
|
expr_block(ref blk) => expr_block(fld.fold_block((*blk))),
|
||||||
expr_copy(e) => expr_copy(fld.fold_expr(e)),
|
expr_copy(e) => expr_copy(fld.fold_expr(e)),
|
||||||
expr_unary_move(e) => expr_unary_move(fld.fold_expr(e)),
|
|
||||||
expr_assign(el, er) => {
|
expr_assign(el, er) => {
|
||||||
expr_assign(fld.fold_expr(el), fld.fold_expr(er))
|
expr_assign(fld.fold_expr(el), fld.fold_expr(er))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ use core::prelude::*;
|
||||||
use ast::{ProtoBox, ProtoUniq, RegionTyParamBound, TraitTyParamBound};
|
use ast::{ProtoBox, ProtoUniq, RegionTyParamBound, TraitTyParamBound};
|
||||||
use ast::{provided, public, pure_fn, purity, re_static};
|
use ast::{provided, public, pure_fn, purity, re_static};
|
||||||
use ast::{_mod, add, arg, arm, attribute, bind_by_ref, bind_infer};
|
use ast::{_mod, add, arg, arm, attribute, bind_by_ref, bind_infer};
|
||||||
use ast::{bind_by_value, bind_by_move, bitand, bitor, bitxor, blk};
|
use ast::{bind_by_copy, bitand, bitor, bitxor, blk};
|
||||||
use ast::{blk_check_mode, box, by_copy, by_move, by_ref, by_val};
|
use ast::{blk_check_mode, box, by_copy, by_ref, by_val};
|
||||||
use ast::{capture_clause, capture_item, crate, crate_cfg, decl, decl_item};
|
use ast::{crate, crate_cfg, decl, decl_item};
|
||||||
use ast::{decl_local, default_blk, deref, div, enum_def, enum_variant_kind};
|
use ast::{decl_local, default_blk, deref, div, enum_def, enum_variant_kind};
|
||||||
use ast::{expl, expr, expr_, expr_addr_of, expr_match, expr_again};
|
use ast::{expl, expr, expr_, expr_addr_of, expr_match, expr_again};
|
||||||
use ast::{expr_assert, expr_assign, expr_assign_op, expr_binary, expr_block};
|
use ast::{expr_assert, expr_assign, expr_assign_op, expr_binary, expr_block};
|
||||||
|
@ -24,7 +24,7 @@ use ast::{expr_fail, expr_field, expr_fn, expr_fn_block, expr_if, expr_index};
|
||||||
use ast::{expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac};
|
use ast::{expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac};
|
||||||
use ast::{expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat};
|
use ast::{expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat};
|
||||||
use ast::{expr_ret, expr_swap, expr_struct, expr_tup, expr_unary};
|
use ast::{expr_ret, expr_swap, expr_struct, expr_tup, expr_unary};
|
||||||
use ast::{expr_unary_move, expr_vec, expr_vstore, expr_vstore_mut_box};
|
use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
|
||||||
use ast::{expr_vstore_fixed, expr_vstore_slice, expr_vstore_box};
|
use ast::{expr_vstore_fixed, expr_vstore_slice, expr_vstore_box};
|
||||||
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, field, fn_decl};
|
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, field, fn_decl};
|
||||||
use ast::{expr_vstore_uniq, TyFn, Onceness, Once, Many};
|
use ast::{expr_vstore_uniq, TyFn, Onceness, Once, Many};
|
||||||
|
@ -102,7 +102,7 @@ enum restriction {
|
||||||
enum class_contents { dtor_decl(blk, ~[attribute], codemap::span),
|
enum class_contents { dtor_decl(blk, ~[attribute], codemap::span),
|
||||||
members(~[@struct_field]) }
|
members(~[@struct_field]) }
|
||||||
|
|
||||||
type arg_or_capture_item = Either<arg, capture_item>;
|
type arg_or_capture_item = Either<arg, ()>;
|
||||||
type item_info = (ident, item_, Option<~[attribute]>);
|
type item_info = (ident, item_, Option<~[attribute]>);
|
||||||
|
|
||||||
pub enum item_or_view_item {
|
pub enum item_or_view_item {
|
||||||
|
@ -401,7 +401,7 @@ pub impl Parser {
|
||||||
|
|
||||||
let tps = p.parse_ty_params();
|
let tps = p.parse_ty_params();
|
||||||
|
|
||||||
let (self_ty, d, _) = do self.parse_fn_decl_with_self() |p| {
|
let (self_ty, d) = do self.parse_fn_decl_with_self() |p| {
|
||||||
// This is somewhat dubious; We don't want to allow argument
|
// This is somewhat dubious; We don't want to allow argument
|
||||||
// names to be left off if there is a definition...
|
// names to be left off if there is a definition...
|
||||||
either::Left(p.parse_arg_general(false))
|
either::Left(p.parse_arg_general(false))
|
||||||
|
@ -651,7 +651,7 @@ pub impl Parser {
|
||||||
|
|
||||||
fn parse_arg_mode() -> mode {
|
fn parse_arg_mode() -> mode {
|
||||||
if self.eat(token::BINOP(token::MINUS)) {
|
if self.eat(token::BINOP(token::MINUS)) {
|
||||||
expl(by_move)
|
expl(by_copy) // NDM outdated syntax
|
||||||
} else if self.eat(token::ANDAND) {
|
} else if self.eat(token::ANDAND) {
|
||||||
expl(by_ref)
|
expl(by_ref)
|
||||||
} else if self.eat(token::BINOP(token::PLUS)) {
|
} else if self.eat(token::BINOP(token::PLUS)) {
|
||||||
|
@ -689,23 +689,12 @@ pub impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_capture_item_or(parse_arg_fn: fn(Parser) -> arg_or_capture_item)
|
fn parse_capture_item_or(parse_arg_fn: fn(Parser) -> arg_or_capture_item)
|
||||||
-> arg_or_capture_item {
|
-> arg_or_capture_item
|
||||||
|
{
|
||||||
fn parse_capture_item(p:Parser, is_move: bool) -> capture_item {
|
if self.eat_keyword(~"move") || self.eat_keyword(~"copy") {
|
||||||
let sp = mk_sp(p.span.lo, p.span.hi);
|
// XXX outdated syntax now that moves-based-on-type has gone in
|
||||||
let ident = p.parse_ident();
|
self.parse_ident();
|
||||||
@ast::capture_item_ {
|
either::Right(())
|
||||||
id: p.get_id(),
|
|
||||||
is_move: is_move,
|
|
||||||
name: ident,
|
|
||||||
span: sp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.eat_keyword(~"move") {
|
|
||||||
either::Right(parse_capture_item(self, true))
|
|
||||||
} else if self.eat_keyword(~"copy") {
|
|
||||||
either::Right(parse_capture_item(self, false))
|
|
||||||
} else {
|
} else {
|
||||||
parse_arg_fn(self)
|
parse_arg_fn(self)
|
||||||
}
|
}
|
||||||
|
@ -1078,9 +1067,8 @@ pub impl Parser {
|
||||||
ex = expr_copy(e);
|
ex = expr_copy(e);
|
||||||
hi = e.span.hi;
|
hi = e.span.hi;
|
||||||
} else if self.eat_keyword(~"move") {
|
} else if self.eat_keyword(~"move") {
|
||||||
let e = self.parse_expr();
|
// XXX move keyword is no longer important, remove after snapshot
|
||||||
ex = expr_unary_move(e);
|
return self.parse_expr();
|
||||||
hi = e.span.hi;
|
|
||||||
} else if self.token == token::MOD_SEP ||
|
} else if self.token == token::MOD_SEP ||
|
||||||
is_ident(self.token) && !self.is_keyword(~"true") &&
|
is_ident(self.token) && !self.is_keyword(~"true") &&
|
||||||
!self.is_keyword(~"false") {
|
!self.is_keyword(~"false") {
|
||||||
|
@ -1576,12 +1564,10 @@ pub impl Parser {
|
||||||
|
|
||||||
// if we want to allow fn expression argument types to be inferred in
|
// if we want to allow fn expression argument types to be inferred in
|
||||||
// the future, just have to change parse_arg to parse_fn_block_arg.
|
// the future, just have to change parse_arg to parse_fn_block_arg.
|
||||||
let (decl, capture_clause) =
|
let decl = self.parse_fn_decl(|p| p.parse_arg_or_capture_item());
|
||||||
self.parse_fn_decl(|p| p.parse_arg_or_capture_item());
|
|
||||||
|
|
||||||
let body = self.parse_block();
|
let body = self.parse_block();
|
||||||
return self.mk_expr(lo, body.span.hi,
|
return self.mk_expr(lo, body.span.hi,expr_fn(proto, decl, body));
|
||||||
expr_fn(proto, decl, body, capture_clause));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `|args| { ... }` like in `do` expressions
|
// `|args| { ... }` like in `do` expressions
|
||||||
|
@ -1594,18 +1580,15 @@ pub impl Parser {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// No argument list - `do foo {`
|
// No argument list - `do foo {`
|
||||||
(
|
ast::fn_decl {
|
||||||
ast::fn_decl {
|
inputs: ~[],
|
||||||
inputs: ~[],
|
output: @Ty {
|
||||||
output: @Ty {
|
id: self.get_id(),
|
||||||
id: self.get_id(),
|
node: ty_infer,
|
||||||
node: ty_infer,
|
span: self.span
|
||||||
span: self.span
|
},
|
||||||
},
|
cf: return_val
|
||||||
cf: return_val
|
}
|
||||||
},
|
|
||||||
@~[]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1621,10 +1604,10 @@ pub impl Parser {
|
||||||
|| self.parse_expr())
|
|| self.parse_expr())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lambda_expr_(parse_decl: fn&() -> (fn_decl, capture_clause),
|
fn parse_lambda_expr_(parse_decl: fn&() -> fn_decl,
|
||||||
parse_body: fn&() -> @expr) -> @expr {
|
parse_body: fn&() -> @expr) -> @expr {
|
||||||
let lo = self.last_span.lo;
|
let lo = self.last_span.lo;
|
||||||
let (decl, captures) = parse_decl();
|
let decl = parse_decl();
|
||||||
let body = parse_body();
|
let body = parse_body();
|
||||||
let fakeblock = ast::blk_ {
|
let fakeblock = ast::blk_ {
|
||||||
view_items: ~[],
|
view_items: ~[],
|
||||||
|
@ -1636,7 +1619,7 @@ pub impl Parser {
|
||||||
let fakeblock = spanned(body.span.lo, body.span.hi,
|
let fakeblock = spanned(body.span.lo, body.span.hi,
|
||||||
fakeblock);
|
fakeblock);
|
||||||
return self.mk_expr(lo, body.span.hi,
|
return self.mk_expr(lo, body.span.hi,
|
||||||
expr_fn_block(decl, fakeblock, captures));
|
expr_fn_block(decl, fakeblock));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_else_expr() -> @expr {
|
fn parse_else_expr() -> @expr {
|
||||||
|
@ -2065,22 +2048,16 @@ pub impl Parser {
|
||||||
let mutbl = self.parse_mutability();
|
let mutbl = self.parse_mutability();
|
||||||
pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl));
|
pat = self.parse_pat_ident(refutable, bind_by_ref(mutbl));
|
||||||
} else if self.eat_keyword(~"copy") {
|
} else if self.eat_keyword(~"copy") {
|
||||||
pat = self.parse_pat_ident(refutable, bind_by_value);
|
pat = self.parse_pat_ident(refutable, bind_by_copy);
|
||||||
} else if self.eat_keyword(~"move") {
|
|
||||||
pat = self.parse_pat_ident(refutable, bind_by_move);
|
|
||||||
} else {
|
} else {
|
||||||
let binding_mode;
|
if self.eat_keyword(~"move") {
|
||||||
// XXX: Aren't these two cases deadcode? -- bblum
|
/* XXX---remove move keyword */
|
||||||
if self.eat_keyword(~"copy") {
|
|
||||||
binding_mode = bind_by_value;
|
|
||||||
} else if self.eat_keyword(~"move") {
|
|
||||||
binding_mode = bind_by_move;
|
|
||||||
} else if refutable {
|
|
||||||
binding_mode = bind_infer;
|
|
||||||
} else {
|
|
||||||
binding_mode = bind_by_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX---refutable match bindings should work same as let
|
||||||
|
let binding_mode =
|
||||||
|
if refutable {bind_infer} else {bind_by_copy};
|
||||||
|
|
||||||
let cannot_be_enum_or_struct;
|
let cannot_be_enum_or_struct;
|
||||||
match self.look_ahead(1) {
|
match self.look_ahead(1) {
|
||||||
token::LPAREN | token::LBRACKET | token::LT |
|
token::LPAREN | token::LBRACKET | token::LT |
|
||||||
|
@ -2560,25 +2537,21 @@ pub impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fn_decl(parse_arg_fn: fn(Parser) -> arg_or_capture_item)
|
fn parse_fn_decl(parse_arg_fn: fn(Parser) -> arg_or_capture_item)
|
||||||
-> (fn_decl, capture_clause) {
|
-> fn_decl
|
||||||
|
{
|
||||||
let args_or_capture_items: ~[arg_or_capture_item] =
|
let args_or_capture_items: ~[arg_or_capture_item] =
|
||||||
self.parse_unspanned_seq(
|
self.parse_unspanned_seq(
|
||||||
token::LPAREN, token::RPAREN,
|
token::LPAREN, token::RPAREN,
|
||||||
seq_sep_trailing_disallowed(token::COMMA), parse_arg_fn);
|
seq_sep_trailing_disallowed(token::COMMA), parse_arg_fn);
|
||||||
|
|
||||||
let inputs = either::lefts(args_or_capture_items);
|
let inputs = either::lefts(args_or_capture_items);
|
||||||
let capture_clause = @either::rights(args_or_capture_items);
|
|
||||||
|
|
||||||
let (ret_style, ret_ty) = self.parse_ret_ty();
|
let (ret_style, ret_ty) = self.parse_ret_ty();
|
||||||
(
|
ast::fn_decl {
|
||||||
ast::fn_decl {
|
inputs: inputs,
|
||||||
inputs: inputs,
|
output: ret_ty,
|
||||||
output: ret_ty,
|
cf: ret_style,
|
||||||
cf: ret_style,
|
}
|
||||||
},
|
|
||||||
capture_clause
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_self_ident() -> bool {
|
fn is_self_ident() -> bool {
|
||||||
|
@ -2598,8 +2571,8 @@ pub impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fn_decl_with_self(parse_arg_fn:
|
fn parse_fn_decl_with_self(parse_arg_fn:
|
||||||
fn(Parser) -> arg_or_capture_item)
|
fn(Parser) -> arg_or_capture_item)
|
||||||
-> (self_ty, fn_decl, capture_clause) {
|
-> (self_ty, fn_decl) {
|
||||||
|
|
||||||
fn maybe_parse_self_ty(cnstr: fn(+v: mutability) -> ast::self_ty_,
|
fn maybe_parse_self_ty(cnstr: fn(+v: mutability) -> ast::self_ty_,
|
||||||
p: Parser) -> ast::self_ty_ {
|
p: Parser) -> ast::self_ty_ {
|
||||||
|
@ -2675,7 +2648,6 @@ pub impl Parser {
|
||||||
let hi = self.span.hi;
|
let hi = self.span.hi;
|
||||||
|
|
||||||
let inputs = either::lefts(args_or_capture_items);
|
let inputs = either::lefts(args_or_capture_items);
|
||||||
let capture_clause = @either::rights(args_or_capture_items);
|
|
||||||
let (ret_style, ret_ty) = self.parse_ret_ty();
|
let (ret_style, ret_ty) = self.parse_ret_ty();
|
||||||
|
|
||||||
let fn_decl = ast::fn_decl {
|
let fn_decl = ast::fn_decl {
|
||||||
|
@ -2684,10 +2656,10 @@ pub impl Parser {
|
||||||
cf: ret_style
|
cf: ret_style
|
||||||
};
|
};
|
||||||
|
|
||||||
(spanned(lo, hi, self_ty), fn_decl, capture_clause)
|
(spanned(lo, hi, self_ty), fn_decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fn_block_decl() -> (fn_decl, capture_clause) {
|
fn parse_fn_block_decl() -> fn_decl {
|
||||||
let inputs_captures = {
|
let inputs_captures = {
|
||||||
if self.eat(token::OROR) {
|
if self.eat(token::OROR) {
|
||||||
~[]
|
~[]
|
||||||
|
@ -2704,14 +2676,11 @@ pub impl Parser {
|
||||||
@Ty { id: self.get_id(), node: ty_infer, span: self.span }
|
@Ty { id: self.get_id(), node: ty_infer, span: self.span }
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
ast::fn_decl {
|
||||||
ast::fn_decl {
|
inputs: either::lefts(inputs_captures),
|
||||||
inputs: either::lefts(inputs_captures),
|
output: output,
|
||||||
output: output,
|
cf: return_val,
|
||||||
cf: return_val,
|
}
|
||||||
},
|
|
||||||
@either::rights(inputs_captures)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fn_header() -> {ident: ident, tps: ~[ty_param]} {
|
fn parse_fn_header() -> {ident: ident, tps: ~[ty_param]} {
|
||||||
|
@ -2733,7 +2702,7 @@ pub impl Parser {
|
||||||
|
|
||||||
fn parse_item_fn(purity: purity) -> item_info {
|
fn parse_item_fn(purity: purity) -> item_info {
|
||||||
let t = self.parse_fn_header();
|
let t = self.parse_fn_header();
|
||||||
let (decl, _) = self.parse_fn_decl(|p| p.parse_arg());
|
let decl = self.parse_fn_decl(|p| p.parse_arg());
|
||||||
let (inner_attrs, body) = self.parse_inner_attrs_and_block(true);
|
let (inner_attrs, body) = self.parse_inner_attrs_and_block(true);
|
||||||
(t.ident, item_fn(decl, purity, t.tps, body), Some(inner_attrs))
|
(t.ident, item_fn(decl, purity, t.tps, body), Some(inner_attrs))
|
||||||
}
|
}
|
||||||
|
@ -2753,7 +2722,7 @@ pub impl Parser {
|
||||||
let pur = self.parse_fn_purity();
|
let pur = self.parse_fn_purity();
|
||||||
let ident = self.parse_method_name();
|
let ident = self.parse_method_name();
|
||||||
let tps = self.parse_ty_params();
|
let tps = self.parse_ty_params();
|
||||||
let (self_ty, decl, _) = do self.parse_fn_decl_with_self() |p| {
|
let (self_ty, decl) = do self.parse_fn_decl_with_self() |p| {
|
||||||
p.parse_arg()
|
p.parse_arg()
|
||||||
};
|
};
|
||||||
// XXX: interaction between staticness, self_ty is broken now
|
// XXX: interaction between staticness, self_ty is broken now
|
||||||
|
@ -3262,7 +3231,7 @@ pub impl Parser {
|
||||||
let vis = self.parse_visibility();
|
let vis = self.parse_visibility();
|
||||||
let purity = self.parse_fn_purity();
|
let purity = self.parse_fn_purity();
|
||||||
let t = self.parse_fn_header();
|
let t = self.parse_fn_header();
|
||||||
let (decl, _) = self.parse_fn_decl(|p| p.parse_arg());
|
let decl = self.parse_fn_decl(|p| p.parse_arg());
|
||||||
let mut hi = self.span.hi;
|
let mut hi = self.span.hi;
|
||||||
self.expect(token::SEMI);
|
self.expect(token::SEMI);
|
||||||
@ast::foreign_item { ident: t.ident,
|
@ast::foreign_item { ident: t.ident,
|
||||||
|
|
|
@ -1305,24 +1305,24 @@ pub fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||||
}
|
}
|
||||||
bclose_(s, expr.span, match_indent_unit);
|
bclose_(s, expr.span, match_indent_unit);
|
||||||
}
|
}
|
||||||
ast::expr_fn(proto, decl, ref body, cap_clause) => {
|
ast::expr_fn(proto, decl, ref body) => {
|
||||||
// containing cbox, will be closed by print-block at }
|
// containing cbox, will be closed by print-block at }
|
||||||
cbox(s, indent_unit);
|
cbox(s, indent_unit);
|
||||||
// head-box, will be closed by print-block at start
|
// head-box, will be closed by print-block at start
|
||||||
ibox(s, 0u);
|
ibox(s, 0u);
|
||||||
print_fn_header_info(s, None, None, ast::Many,
|
print_fn_header_info(s, None, None, ast::Many,
|
||||||
Some(proto), ast::inherited);
|
Some(proto), ast::inherited);
|
||||||
print_fn_args_and_ret(s, decl, *cap_clause, None);
|
print_fn_args_and_ret(s, decl, None);
|
||||||
space(s.s);
|
space(s.s);
|
||||||
print_block(s, (*body));
|
print_block(s, (*body));
|
||||||
}
|
}
|
||||||
ast::expr_fn_block(decl, ref body, cap_clause) => {
|
ast::expr_fn_block(decl, ref body) => {
|
||||||
// in do/for blocks we don't want to show an empty
|
// in do/for blocks we don't want to show an empty
|
||||||
// argument list, but at this point we don't know which
|
// argument list, but at this point we don't know which
|
||||||
// we are inside.
|
// we are inside.
|
||||||
//
|
//
|
||||||
// if !decl.inputs.is_empty() {
|
// if !decl.inputs.is_empty() {
|
||||||
print_fn_block_args(s, decl, *cap_clause);
|
print_fn_block_args(s, decl);
|
||||||
space(s.s);
|
space(s.s);
|
||||||
// }
|
// }
|
||||||
assert (*body).node.stmts.is_empty();
|
assert (*body).node.stmts.is_empty();
|
||||||
|
@ -1357,10 +1357,6 @@ pub fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||||
print_block(s, (*blk));
|
print_block(s, (*blk));
|
||||||
}
|
}
|
||||||
ast::expr_copy(e) => { word_space(s, ~"copy"); print_expr(s, e); }
|
ast::expr_copy(e) => { word_space(s, ~"copy"); print_expr(s, e); }
|
||||||
ast::expr_unary_move(e) => {
|
|
||||||
word_space(s, ~"move");
|
|
||||||
print_expr(s, e);
|
|
||||||
}
|
|
||||||
ast::expr_assign(lhs, rhs) => {
|
ast::expr_assign(lhs, rhs) => {
|
||||||
print_expr(s, lhs);
|
print_expr(s, lhs);
|
||||||
space(s.s);
|
space(s.s);
|
||||||
|
@ -1554,10 +1550,7 @@ pub fn print_pat(s: ps, &&pat: @ast::pat, refutable: bool) {
|
||||||
word_nbsp(s, ~"ref");
|
word_nbsp(s, ~"ref");
|
||||||
print_mutability(s, mutbl);
|
print_mutability(s, mutbl);
|
||||||
}
|
}
|
||||||
ast::bind_by_move => {
|
ast::bind_by_copy => {
|
||||||
word_nbsp(s, ~"move");
|
|
||||||
}
|
|
||||||
ast::bind_by_value => {
|
|
||||||
word_nbsp(s, ~"copy");
|
word_nbsp(s, ~"copy");
|
||||||
}
|
}
|
||||||
ast::bind_infer => {}
|
ast::bind_infer => {}
|
||||||
|
@ -1693,16 +1686,14 @@ pub fn print_fn(s: ps,
|
||||||
nbsp(s);
|
nbsp(s);
|
||||||
print_ident(s, name);
|
print_ident(s, name);
|
||||||
print_type_params(s, typarams);
|
print_type_params(s, typarams);
|
||||||
print_fn_args_and_ret(s, decl, ~[], opt_self_ty);
|
print_fn_args_and_ret(s, decl, opt_self_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_fn_args(s: ps, decl: ast::fn_decl,
|
pub fn print_fn_args(s: ps, decl: ast::fn_decl,
|
||||||
cap_items: ~[ast::capture_item],
|
opt_self_ty: Option<ast::self_ty_>) {
|
||||||
opt_self_ty: Option<ast::self_ty_>) {
|
// It is unfortunate to duplicate the commasep logic, but we we want the
|
||||||
// It is unfortunate to duplicate the commasep logic, but we
|
// self type and the args all in the same box.
|
||||||
// we want the self type, the args, and the capture clauses all
|
box(s, 0u, inconsistent);
|
||||||
// in the same box.
|
|
||||||
box(s, 0, inconsistent);
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for opt_self_ty.each |self_ty| {
|
for opt_self_ty.each |self_ty| {
|
||||||
first = !print_self_ty(s, *self_ty);
|
first = !print_self_ty(s, *self_ty);
|
||||||
|
@ -1713,21 +1704,13 @@ pub fn print_fn_args(s: ps, decl: ast::fn_decl,
|
||||||
print_arg(s, *arg);
|
print_arg(s, *arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
for cap_items.each |cap_item| {
|
|
||||||
if first { first = false; } else { word_space(s, ~","); }
|
|
||||||
if cap_item.is_move { word_nbsp(s, ~"move") }
|
|
||||||
else { word_nbsp(s, ~"copy") }
|
|
||||||
print_ident(s, cap_item.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
end(s);
|
end(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl,
|
pub fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl,
|
||||||
cap_items: ~[ast::capture_item],
|
|
||||||
opt_self_ty: Option<ast::self_ty_>) {
|
opt_self_ty: Option<ast::self_ty_>) {
|
||||||
popen(s);
|
popen(s);
|
||||||
print_fn_args(s, decl, cap_items, opt_self_ty);
|
print_fn_args(s, decl, opt_self_ty);
|
||||||
pclose(s);
|
pclose(s);
|
||||||
|
|
||||||
maybe_print_comment(s, decl.output.span.lo);
|
maybe_print_comment(s, decl.output.span.lo);
|
||||||
|
@ -1741,10 +1724,9 @@ pub fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_fn_block_args(s: ps, decl: ast::fn_decl,
|
pub fn print_fn_block_args(s: ps, decl: ast::fn_decl) {
|
||||||
cap_items: ~[ast::capture_item]) {
|
|
||||||
word(s.s, ~"|");
|
word(s.s, ~"|");
|
||||||
print_fn_args(s, decl, cap_items, None);
|
print_fn_args(s, decl, None);
|
||||||
word(s.s, ~"|");
|
word(s.s, ~"|");
|
||||||
|
|
||||||
match decl.output.node {
|
match decl.output.node {
|
||||||
|
@ -1761,10 +1743,9 @@ pub fn print_fn_block_args(s: ps, decl: ast::fn_decl,
|
||||||
|
|
||||||
pub fn mode_to_str(m: ast::mode) -> ~str {
|
pub fn mode_to_str(m: ast::mode) -> ~str {
|
||||||
match m {
|
match m {
|
||||||
ast::expl(ast::by_move) => ~"-",
|
|
||||||
ast::expl(ast::by_ref) => ~"&&",
|
ast::expl(ast::by_ref) => ~"&&",
|
||||||
ast::expl(ast::by_val) => ~"++",
|
|
||||||
ast::expl(ast::by_copy) => ~"+",
|
ast::expl(ast::by_copy) => ~"+",
|
||||||
|
ast::expl(ast::by_val) => ~"++",
|
||||||
ast::infer(_) => ~""
|
ast::infer(_) => ~""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ pub enum vt<E> { mk_vt(visitor<E>), }
|
||||||
pub enum fn_kind {
|
pub enum fn_kind {
|
||||||
fk_item_fn(ident, ~[ty_param], purity), //< an item declared with fn()
|
fk_item_fn(ident, ~[ty_param], purity), //< an item declared with fn()
|
||||||
fk_method(ident, ~[ty_param], @method),
|
fk_method(ident, ~[ty_param], @method),
|
||||||
fk_anon(Proto, capture_clause), //< an anonymous function like fn@(...)
|
fk_anon(Proto), //< an anonymous function like fn@(...)
|
||||||
fk_fn_block(capture_clause), //< a block {||...}
|
fk_fn_block, //< a block {||...}
|
||||||
fk_dtor(~[ty_param], ~[attribute], node_id /* self id */,
|
fk_dtor(~[ty_param], ~[attribute], node_id /* self id */,
|
||||||
def_id /* parent class id */) // class destructor
|
def_id /* parent class id */) // class destructor
|
||||||
|
|
||||||
|
@ -457,12 +457,12 @@ pub fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||||
(v.visit_expr)(x, e, v);
|
(v.visit_expr)(x, e, v);
|
||||||
for (*arms).each |a| { (v.visit_arm)(*a, e, v); }
|
for (*arms).each |a| { (v.visit_arm)(*a, e, v); }
|
||||||
}
|
}
|
||||||
expr_fn(proto, decl, ref body, cap_clause) => {
|
expr_fn(proto, decl, ref body) => {
|
||||||
(v.visit_fn)(fk_anon(proto, cap_clause), decl, (*body),
|
(v.visit_fn)(fk_anon(proto), decl, (*body),
|
||||||
ex.span, ex.id, e, v);
|
ex.span, ex.id, e, v);
|
||||||
}
|
}
|
||||||
expr_fn_block(decl, ref body, cap_clause) => {
|
expr_fn_block(decl, ref body) => {
|
||||||
(v.visit_fn)(fk_fn_block(cap_clause), decl, (*body),
|
(v.visit_fn)(fk_fn_block, decl, (*body),
|
||||||
ex.span, ex.id, e, v);
|
ex.span, ex.id, e, v);
|
||||||
}
|
}
|
||||||
expr_block(ref b) => (v.visit_block)((*b), e, v),
|
expr_block(ref b) => (v.visit_block)((*b), e, v),
|
||||||
|
@ -471,7 +471,6 @@ pub fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||||
(v.visit_expr)(a, e, v);
|
(v.visit_expr)(a, e, v);
|
||||||
}
|
}
|
||||||
expr_copy(a) => (v.visit_expr)(a, e, v),
|
expr_copy(a) => (v.visit_expr)(a, e, v),
|
||||||
expr_unary_move(a) => (v.visit_expr)(a, e, v),
|
|
||||||
expr_swap(a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); }
|
expr_swap(a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); }
|
||||||
expr_assign_op(_, a, b) => {
|
expr_assign_op(_, a, b) => {
|
||||||
(v.visit_expr)(b, e, v);
|
(v.visit_expr)(b, e, v);
|
||||||
|
|
|
@ -377,7 +377,8 @@ fn validate(edges: ~[(node_id, node_id)],
|
||||||
|
|
||||||
log(info, ~"Verifying tree and graph edges...");
|
log(info, ~"Verifying tree and graph edges...");
|
||||||
|
|
||||||
let status = do par::alli(tree) |u, v, copy edges| {
|
let edges = copy edges;
|
||||||
|
let status = do par::alli(tree) |u, v| {
|
||||||
let u = u as node_id;
|
let u = u as node_id;
|
||||||
if *v == -1i64 || u == root {
|
if *v == -1i64 || u == root {
|
||||||
true
|
true
|
||||||
|
|
|
@ -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() {
|
fn box_imm() {
|
||||||
let mut v = ~3;
|
let v = ~3;
|
||||||
let _w = &mut v; //~ NOTE loan of mutable local variable granted here
|
let _w = &v; //~ NOTE loan of immutable local variable granted here
|
||||||
do task::spawn |move v| {
|
do task::spawn {
|
||||||
//~^ ERROR moving out of mutable local variable prohibited due to outstanding loan
|
|
||||||
debug!("v=%d", *v);
|
debug!("v=%d", *v);
|
||||||
|
//~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut v = ~3;
|
let v = ~3;
|
||||||
let _w = &mut v; //~ NOTE loan of mutable local variable granted here
|
let _w = &v; //~ NOTE loan of immutable local variable granted here
|
||||||
task::spawn(fn~(move v) {
|
task::spawn(fn~() {
|
||||||
//~^ ERROR moving out of mutable local variable prohibited due to outstanding loan
|
|
||||||
debug!("v=%d", *v);
|
debug!("v=%d", *v);
|
||||||
|
//~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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() {
|
fn main() {
|
||||||
let a = Foo { x: 1, y: Bar { x: 5 } };
|
let a = Foo { x: 1, y: Bar { x: 5 } };
|
||||||
let c = Foo { x: 4, .. a}; //~ ERROR copying a noncopyable value
|
let c = Foo { x: 4, .. a}; //~ ERROR cannot copy field `y` of base expression, which has a noncopyable type
|
||||||
io::println(fmt!("%?", c));
|
io::println(fmt!("%?", c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
fn car() {
|
||||||
// Here, i is mutable, but *explicitly* copied:
|
// Here, i is mutable, but *explicitly* shadowed copied:
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < 10 {
|
while i < 10 {
|
||||||
do task::spawn |copy i| {
|
{
|
||||||
user(i);
|
let i = i;
|
||||||
|
do task::spawn {
|
||||||
|
user(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// error-pattern: use of moved value
|
fn take(_x: ~int) {}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 3;
|
|
||||||
let y = move x;
|
|
||||||
debug!("%d", x);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let x: ~int = ~25;
|
||||||
|
loop {
|
||||||
|
take(x); //~ ERROR use of moved value: `x`
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue