Revert "remove alias analysis and replace with borrowck"
18s perf regression compiling rustc with opts This reverts commit 7f6ee0ce0df8af4c21b065cb49b95079ae643f77.
This commit is contained in:
parent
c058f1d992
commit
7ef825bb60
@ -1382,7 +1382,83 @@ gets access to them.
|
|||||||
|
|
||||||
## Safe references
|
## Safe references
|
||||||
|
|
||||||
*This system has recently changed. An explanantion is forthcoming.*
|
There is one catch with this approach: sometimes the compiler can
|
||||||
|
*not* statically guarantee that the argument value at the caller side
|
||||||
|
will survive to the end of the call. Another argument might indirectly
|
||||||
|
refer to it and be used to overwrite it, or a closure might assign a
|
||||||
|
new value to it.
|
||||||
|
|
||||||
|
Fortunately, Rust tasks are single-threaded worlds, which share no
|
||||||
|
data with other tasks, and most data is immutable. This allows most
|
||||||
|
argument-passing situations to be proved safe without further
|
||||||
|
difficulty.
|
||||||
|
|
||||||
|
Take the following program:
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
# fn get_really_big_record() -> int { 1 }
|
||||||
|
# fn myfunc(a: int) {}
|
||||||
|
fn main() {
|
||||||
|
let x = get_really_big_record();
|
||||||
|
myfunc(x);
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Here we know for sure that no one else has access to the `x` variable
|
||||||
|
in `main`, so we're good. But the call could also look like this:
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
# fn myfunc(a: int, b: fn()) {}
|
||||||
|
# fn get_another_record() -> int { 1 }
|
||||||
|
# let mut x = 1;
|
||||||
|
myfunc(x, {|| x = get_another_record(); });
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Now, if `myfunc` first calls its second argument and then accesses its
|
||||||
|
first argument, it will see a different value from the one that was
|
||||||
|
passed to it.
|
||||||
|
|
||||||
|
In such a case, the compiler will insert an implicit copy of `x`,
|
||||||
|
*except* if `x` contains something mutable, in which case a copy would
|
||||||
|
result in code that behaves differently. If copying `x` might be
|
||||||
|
expensive (for example, if it holds a vector), the compiler will emit
|
||||||
|
a warning.
|
||||||
|
|
||||||
|
There are even more tricky cases, in which the Rust compiler is forced
|
||||||
|
to pessimistically assume a value will get mutated, even though it is
|
||||||
|
not sure.
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
fn for_each(v: [mut @int], iter: fn(@int)) {
|
||||||
|
for v.each {|elt| iter(elt); }
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
For all this function knows, calling `iter` (which is a closure that
|
||||||
|
might have access to the vector that's passed as `v`) could cause the
|
||||||
|
elements in the vector to be mutated, with the effect that it can not
|
||||||
|
guarantee that the boxes will live for the duration of the call. So it
|
||||||
|
has to copy them. In this case, this will happen implicitly (bumping a
|
||||||
|
reference count is considered cheap enough to not warn about it).
|
||||||
|
|
||||||
|
## The copy operator
|
||||||
|
|
||||||
|
If the `for_each` function given above were to take a vector of
|
||||||
|
`{mut a: int}` instead of `@int`, it would not be able to
|
||||||
|
implicitly copy, since if the `iter` function changes a copy of a
|
||||||
|
mutable record, the changes won't be visible in the record itself. If
|
||||||
|
we *do* want to allow copies there, we have to explicitly allow it
|
||||||
|
with the `copy` operator:
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
type mutrec = {mut x: int};
|
||||||
|
fn for_each(v: [mut mutrec], iter: fn(mutrec)) {
|
||||||
|
for v.each {|elt| iter(copy elt); }
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Adding a `copy` operator is also the way to muffle warnings about
|
||||||
|
implicit copies.
|
||||||
|
|
||||||
## Other uses of safe references
|
## Other uses of safe references
|
||||||
|
|
||||||
|
@ -204,6 +204,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
|||||||
let (root_map, mutbl_map) = time(
|
let (root_map, mutbl_map) = time(
|
||||||
time_passes, "borrow checking",
|
time_passes, "borrow checking",
|
||||||
bind middle::borrowck::check_crate(ty_cx, method_map, crate));
|
bind middle::borrowck::check_crate(ty_cx, method_map, crate));
|
||||||
|
let (copy_map, _ref_map) =
|
||||||
|
time(time_passes, "alias checking",
|
||||||
|
bind middle::alias::check_crate(ty_cx, crate));
|
||||||
time(time_passes, "kind checking",
|
time(time_passes, "kind checking",
|
||||||
bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
|
bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
|
||||||
time(time_passes, "lint checking",
|
time(time_passes, "lint checking",
|
||||||
@ -213,7 +216,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
|||||||
let outputs = option::get(outputs);
|
let outputs = option::get(outputs);
|
||||||
|
|
||||||
let maps = {mutbl_map: mutbl_map, root_map: root_map,
|
let maps = {mutbl_map: mutbl_map, root_map: root_map,
|
||||||
last_use_map: last_use_map,
|
copy_map: copy_map, last_use_map: last_use_map,
|
||||||
impl_map: impl_map, method_map: method_map,
|
impl_map: impl_map, method_map: method_map,
|
||||||
vtable_map: vtable_map};
|
vtable_map: vtable_map};
|
||||||
|
|
||||||
@ -445,6 +448,14 @@ fn build_session_options(match: getopts::match,
|
|||||||
let sysroot_opt = getopts::opt_maybe_str(match, "sysroot");
|
let sysroot_opt = getopts::opt_maybe_str(match, "sysroot");
|
||||||
let target_opt = getopts::opt_maybe_str(match, "target");
|
let target_opt = getopts::opt_maybe_str(match, "target");
|
||||||
let save_temps = getopts::opt_present(match, "save-temps");
|
let save_temps = getopts::opt_present(match, "save-temps");
|
||||||
|
let borrowck = alt getopts::opt_maybe_str(match, "borrowck") {
|
||||||
|
none { 0u }
|
||||||
|
some("warn") { 1u }
|
||||||
|
some("err") { 2u }
|
||||||
|
some(_) {
|
||||||
|
early_error(demitter, "borrowck may be warn or err")
|
||||||
|
}
|
||||||
|
};
|
||||||
alt output_type {
|
alt output_type {
|
||||||
// unless we're emitting huamn-readable assembly, omit comments.
|
// unless we're emitting huamn-readable assembly, omit comments.
|
||||||
link::output_type_llvm_assembly | link::output_type_assembly {}
|
link::output_type_llvm_assembly | link::output_type_assembly {}
|
||||||
@ -493,7 +504,8 @@ fn build_session_options(match: getopts::match,
|
|||||||
test: test,
|
test: test,
|
||||||
parse_only: parse_only,
|
parse_only: parse_only,
|
||||||
no_trans: no_trans,
|
no_trans: no_trans,
|
||||||
debugging_opts: debugging_opts};
|
debugging_opts: debugging_opts,
|
||||||
|
borrowck: borrowck};
|
||||||
ret sopts;
|
ret sopts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,7 +582,8 @@ fn opts() -> [getopts::opt] {
|
|||||||
optmulti("Z"),
|
optmulti("Z"),
|
||||||
|
|
||||||
optmulti("cfg"), optflag("test"),
|
optmulti("cfg"), optflag("test"),
|
||||||
optflag("lib"), optflag("bin"), optflag("static"), optflag("gc")];
|
optflag("lib"), optflag("bin"), optflag("static"), optflag("gc"),
|
||||||
|
optopt("borrowck")];
|
||||||
}
|
}
|
||||||
|
|
||||||
type output_filenames = @{out_filename: str, obj_filename:str};
|
type output_filenames = @{out_filename: str, obj_filename:str};
|
||||||
|
@ -66,6 +66,9 @@ type options =
|
|||||||
no_trans: bool,
|
no_trans: bool,
|
||||||
|
|
||||||
debugging_opts: uint,
|
debugging_opts: uint,
|
||||||
|
|
||||||
|
// temporary hack: 0=off,1=warn,2=err --> if 2, alias is disabled
|
||||||
|
borrowck: uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
type crate_metadata = {name: str, data: [u8]};
|
type crate_metadata = {name: str, data: [u8]};
|
||||||
@ -178,7 +181,8 @@ fn basic_options() -> @options {
|
|||||||
test: false,
|
test: false,
|
||||||
parse_only: false,
|
parse_only: false,
|
||||||
no_trans: false,
|
no_trans: false,
|
||||||
debugging_opts: 0u
|
debugging_opts: 0u,
|
||||||
|
borrowck: 0u,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ enum astencode_tag { // Reserves 0x50 -- 0x6f
|
|||||||
tag_table_param_bounds,
|
tag_table_param_bounds,
|
||||||
tag_table_inferred_modes,
|
tag_table_inferred_modes,
|
||||||
tag_table_mutbl,
|
tag_table_mutbl,
|
||||||
|
tag_table_copy,
|
||||||
tag_table_last_use,
|
tag_table_last_use,
|
||||||
tag_table_spill,
|
tag_table_spill,
|
||||||
tag_table_method_map,
|
tag_table_method_map,
|
||||||
|
847
src/rustc/middle/alias.rs
Normal file
847
src/rustc/middle/alias.rs
Normal file
@ -0,0 +1,847 @@
|
|||||||
|
import syntax::{ast, ast_util, ast_map};
|
||||||
|
import ast_util::path_to_ident;
|
||||||
|
import ast::{ident, fn_ident, node_id};
|
||||||
|
import syntax::codemap::span;
|
||||||
|
import syntax::visit;
|
||||||
|
import visit::vt;
|
||||||
|
import std::list;
|
||||||
|
import std::map::hashmap;
|
||||||
|
import core::unreachable;
|
||||||
|
import option::is_none;
|
||||||
|
import list::list;
|
||||||
|
import driver::session::session;
|
||||||
|
import pat_util::*;
|
||||||
|
import util::ppaux::ty_to_str;
|
||||||
|
|
||||||
|
// This is not an alias-analyser (though it would merit from becoming one, or
|
||||||
|
// getting input from one, to be more precise). It is a pass that checks
|
||||||
|
// whether aliases are used in a safe way.
|
||||||
|
|
||||||
|
enum copied { not_allowed, copied, not_copied, }
|
||||||
|
enum invalid_reason { overwritten, val_taken, }
|
||||||
|
type invalid = {reason: invalid_reason,
|
||||||
|
node_id: node_id,
|
||||||
|
sp: span, path: @ast::path};
|
||||||
|
|
||||||
|
enum unsafe_ty { contains(ty::t), mutbl_contains(ty::t), }
|
||||||
|
|
||||||
|
type binding = @{node_id: node_id,
|
||||||
|
span: span,
|
||||||
|
root_var: option<node_id>,
|
||||||
|
local_id: uint,
|
||||||
|
unsafe_tys: [unsafe_ty],
|
||||||
|
mut copied: copied};
|
||||||
|
|
||||||
|
// FIXME it may be worthwhile to use a linked list of bindings instead
|
||||||
|
type scope = {bs: [binding],
|
||||||
|
invalid: @mut @list<@invalid>};
|
||||||
|
|
||||||
|
fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option<node_id>,
|
||||||
|
unsafe_tys: [unsafe_ty]) -> binding {
|
||||||
|
alt root_var {
|
||||||
|
some(r_id) { cx.ref_map.insert(id, r_id); }
|
||||||
|
_ {}
|
||||||
|
}
|
||||||
|
ret @{node_id: id, span: span, root_var: root_var,
|
||||||
|
local_id: local_id_of_node(cx, id),
|
||||||
|
unsafe_tys: unsafe_tys,
|
||||||
|
mut copied: not_copied};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum local_info { local(uint), }
|
||||||
|
|
||||||
|
type copy_map = std::map::hashmap<node_id, ()>;
|
||||||
|
type ref_map = std::map::hashmap<node_id, node_id>;
|
||||||
|
|
||||||
|
type ctx = {tcx: ty::ctxt,
|
||||||
|
copy_map: copy_map,
|
||||||
|
ref_map: ref_map,
|
||||||
|
mut silent: bool};
|
||||||
|
|
||||||
|
fn check_crate(tcx: ty::ctxt, crate: @ast::crate) -> (copy_map, ref_map) {
|
||||||
|
// Stores information about function arguments that's otherwise not easily
|
||||||
|
// available.
|
||||||
|
let cx = @{tcx: tcx,
|
||||||
|
copy_map: std::map::int_hash(),
|
||||||
|
ref_map: std::map::int_hash(),
|
||||||
|
mut silent: false};
|
||||||
|
let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _),
|
||||||
|
visit_expr: bind visit_expr(cx, _, _, _),
|
||||||
|
visit_block: bind visit_block(cx, _, _, _)
|
||||||
|
with *visit::default_visitor::<scope>()};
|
||||||
|
let sc = {bs: [], invalid: @mut @list::nil};
|
||||||
|
visit::visit_crate(*crate, sc, visit::mk_vt(v));
|
||||||
|
tcx.sess.abort_if_errors();
|
||||||
|
ret (cx.copy_map, cx.ref_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_fn(cx: @ctx, _fk: visit::fn_kind, decl: ast::fn_decl,
|
||||||
|
body: ast::blk, _sp: span,
|
||||||
|
id: ast::node_id, sc: scope, v: vt<scope>) {
|
||||||
|
visit::visit_fn_decl(decl, sc, v);
|
||||||
|
let fty = ty::node_id_to_type(cx.tcx, id);
|
||||||
|
|
||||||
|
// Blocks need to obey any restrictions from the enclosing scope, and may
|
||||||
|
// be called multiple times.
|
||||||
|
let proto = ty::ty_fn_proto(fty);
|
||||||
|
alt proto {
|
||||||
|
ast::proto_block | ast::proto_any {
|
||||||
|
check_loop(*cx, sc) {|| v.visit_block(body, sc, v);}
|
||||||
|
}
|
||||||
|
ast::proto_box | ast::proto_uniq | ast::proto_bare {
|
||||||
|
let sc = {bs: [], invalid: @mut @list::nil};
|
||||||
|
v.visit_block(body, sc, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) {
|
||||||
|
let mut handled = true;
|
||||||
|
alt ex.node {
|
||||||
|
ast::expr_call(f, args, _) {
|
||||||
|
check_call(cx, sc, f, args, v);
|
||||||
|
visit_expr(cx, f, sc, v);
|
||||||
|
}
|
||||||
|
ast::expr_alt(input, arms, _) { check_alt(*cx, input, arms, sc, v); }
|
||||||
|
ast::expr_path(pt) {
|
||||||
|
check_var(*cx, ex, pt, ex.id, false, sc);
|
||||||
|
handled = false;
|
||||||
|
}
|
||||||
|
ast::expr_swap(lhs, rhs) {
|
||||||
|
check_lval(cx, lhs, sc, v);
|
||||||
|
check_lval(cx, rhs, sc, v);
|
||||||
|
handled = false;
|
||||||
|
}
|
||||||
|
ast::expr_move(dest, src) {
|
||||||
|
visit_expr(cx, src, sc, v);
|
||||||
|
check_lval(cx, dest, sc, v);
|
||||||
|
check_lval(cx, src, sc, v);
|
||||||
|
}
|
||||||
|
ast::expr_assign(dest, src) | ast::expr_assign_op(_, dest, src) {
|
||||||
|
visit_expr(cx, src, sc, v);
|
||||||
|
check_lval(cx, dest, sc, v);
|
||||||
|
}
|
||||||
|
ast::expr_if(c, then, els) { check_if(c, then, els, sc, v); }
|
||||||
|
ast::expr_while(_, _) {
|
||||||
|
check_loop(*cx, sc) {|| visit::visit_expr(ex, sc, v); }
|
||||||
|
}
|
||||||
|
_ { handled = false; }
|
||||||
|
}
|
||||||
|
if !handled { visit::visit_expr(ex, sc, v); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_block(cx: @ctx, b: ast::blk, sc: scope, v: vt<scope>) {
|
||||||
|
let sc = sc;
|
||||||
|
for b.node.stmts.each {|stmt|
|
||||||
|
alt stmt.node {
|
||||||
|
ast::stmt_decl(@{node: ast::decl_item(it), _}, _) {
|
||||||
|
v.visit_item(it, sc, v);
|
||||||
|
}
|
||||||
|
ast::stmt_decl(@{node: ast::decl_local(locs), _}, _) {
|
||||||
|
for locs.each {|loc|
|
||||||
|
alt loc.node.init {
|
||||||
|
some(init) {
|
||||||
|
if init.op == ast::init_move {
|
||||||
|
check_lval(cx, init.expr, sc, v);
|
||||||
|
} else {
|
||||||
|
visit_expr(cx, init.expr, sc, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
none { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::stmt_expr(ex, _) | ast::stmt_semi(ex, _) {
|
||||||
|
v.visit_expr(ex, sc, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visit::visit_expr_opt(b.node.expr, sc, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cant_copy(cx: ctx, b: binding) -> bool {
|
||||||
|
|
||||||
|
if cx.tcx.sess.opts.borrowck == 2u {
|
||||||
|
// borrowck is enabled. disable alias analysis.
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
|
alt b.copied {
|
||||||
|
not_allowed { ret true; }
|
||||||
|
copied { ret false; }
|
||||||
|
not_copied {}
|
||||||
|
}
|
||||||
|
let ty = ty::node_id_to_type(cx.tcx, b.node_id);
|
||||||
|
if ty::type_allows_implicit_copy(cx.tcx, ty) {
|
||||||
|
b.copied = copied;
|
||||||
|
cx.copy_map.insert(b.node_id, ());
|
||||||
|
if copy_is_expensive(cx.tcx, ty) {
|
||||||
|
cx.tcx.sess.span_warn(b.span,
|
||||||
|
"inserting an implicit copy for type " +
|
||||||
|
util::ppaux::ty_to_str(cx.tcx, ty));
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
} else { ret true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME this is a really awful hack
|
||||||
|
fn local_id_for_args(cx: ctx, args: [@ast::expr]) -> uint {
|
||||||
|
for vec::each(args) {|arg|
|
||||||
|
alt arg.node {
|
||||||
|
ast::expr_fn_block(decl, _, _) | ast::expr_fn(_, decl, _, _) |
|
||||||
|
ast::expr_loop_body(@{node: ast::expr_fn_block(decl, _, _), _}) {
|
||||||
|
if decl.inputs.len() > 0u {
|
||||||
|
ret local_id_of_node(cx, decl.inputs[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xFFFFFFFFu
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_call(cx: @ctx, sc: scope, f: @ast::expr, args: [@ast::expr],
|
||||||
|
v: vt<scope>) {
|
||||||
|
let fty = ty::expr_ty(cx.tcx, f);
|
||||||
|
let arg_ts = ty::ty_fn_args(fty);
|
||||||
|
let mut mut_roots: [{arg: @ast::expr, node: node_id}] = [];
|
||||||
|
let mut bindings = [];
|
||||||
|
let mut blocks = [], loc_id = local_id_for_args(*cx, args);
|
||||||
|
vec::iter2(args, arg_ts) {|arg, arg_t|
|
||||||
|
let root = expr_root(*cx, arg, false);
|
||||||
|
alt ty::resolved_mode(cx.tcx, arg_t.mode) {
|
||||||
|
ast::by_mutbl_ref {
|
||||||
|
alt path_def(*cx, arg) {
|
||||||
|
some(def) {
|
||||||
|
let dnum = ast_util::def_id_of_def(def).node;
|
||||||
|
mut_roots += [{arg: arg, node: dnum}];
|
||||||
|
}
|
||||||
|
_ { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::by_ref | ast::by_val | ast::by_move | ast::by_copy {}
|
||||||
|
}
|
||||||
|
alt arg.node {
|
||||||
|
ast::expr_fn_block(*) { blocks += [arg]; }
|
||||||
|
ast::expr_loop_body(b) { blocks += [b]; }
|
||||||
|
_ {
|
||||||
|
let root_var = path_def_id(*cx, root.ex);
|
||||||
|
let arg_copied = alt ty::resolved_mode(cx.tcx, arg_t.mode) {
|
||||||
|
ast::by_move | ast::by_copy { copied }
|
||||||
|
ast::by_mutbl_ref { not_allowed }
|
||||||
|
ast::by_ref | ast::by_val { not_copied }
|
||||||
|
};
|
||||||
|
visit_expr(cx, arg, sc, v);
|
||||||
|
bindings += [@{node_id: arg.id,
|
||||||
|
span: arg.span,
|
||||||
|
root_var: root_var,
|
||||||
|
local_id: loc_id,
|
||||||
|
unsafe_tys: unsafe_set(root.mutbl),
|
||||||
|
mut copied: arg_copied}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let f_may_close = alt f.node {
|
||||||
|
ast::expr_path(_) { def_is_local_or_self(cx.tcx.def_map.get(f.id)) }
|
||||||
|
_ { true }
|
||||||
|
};
|
||||||
|
if f_may_close {
|
||||||
|
let mut i = 0u;
|
||||||
|
for bindings.each {|b|
|
||||||
|
let mut unsfe = vec::len(b.unsafe_tys) > 0u;
|
||||||
|
alt b.root_var {
|
||||||
|
some(rid) {
|
||||||
|
for sc.bs.each {|o|
|
||||||
|
if o.node_id == rid && vec::len(o.unsafe_tys) > 0u {
|
||||||
|
unsfe = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ {}
|
||||||
|
}
|
||||||
|
if unsfe && cant_copy(*cx, b) {
|
||||||
|
err(*cx, f.span, #fmt["function may alias with argument \
|
||||||
|
%u, which is not immutably rooted", i]);
|
||||||
|
}
|
||||||
|
i += 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut j = 0u;
|
||||||
|
for bindings.each {|b|
|
||||||
|
for b.unsafe_tys.each {|unsafe_ty|
|
||||||
|
vec::iteri(arg_ts) {|i, arg_t|
|
||||||
|
let mut_alias =
|
||||||
|
(ast::by_mutbl_ref == ty::arg_mode(cx.tcx, arg_t));
|
||||||
|
alt args[i].node {
|
||||||
|
ast::expr_fn_block(*) | ast::expr_loop_body(_) {}
|
||||||
|
_ {
|
||||||
|
if i != j && ty_can_unsafely_include(
|
||||||
|
*cx, unsafe_ty, arg_t.ty, mut_alias) &&
|
||||||
|
cant_copy(*cx, b) {
|
||||||
|
err(*cx, args[i].span,
|
||||||
|
#fmt["argument %u may alias with argument %u, \
|
||||||
|
which is not immutably rooted", i, j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we're not passing a root by mut alias.
|
||||||
|
for mut_roots.each {|mroot|
|
||||||
|
for bindings.each {|b|
|
||||||
|
if b.node_id != mroot.arg.id {
|
||||||
|
alt b.root_var {
|
||||||
|
some(root) {
|
||||||
|
if mroot.node == root && cant_copy(*cx, b) {
|
||||||
|
err(*cx, mroot.arg.span,
|
||||||
|
"passing a mut reference to a \
|
||||||
|
variable that roots another reference");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
none { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check the bodies of block arguments against the current scope
|
||||||
|
if blocks.len() > 0u {
|
||||||
|
let inner_sc = {bs: bindings + sc.bs, invalid: sc.invalid};
|
||||||
|
for blocks.each {|blk|
|
||||||
|
alt check blk.node {
|
||||||
|
ast::expr_fn_block(_, body, _) {
|
||||||
|
v.visit_block(body, inner_sc, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for bindings.each {|binding|
|
||||||
|
test_scope(*cx, sc, binding, none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[warn(no_non_implicitly_copyable_typarams)]
|
||||||
|
fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope,
|
||||||
|
v: vt<scope>) {
|
||||||
|
v.visit_expr(input, sc, v);
|
||||||
|
let orig_invalid = *sc.invalid;
|
||||||
|
let mut all_invalid = orig_invalid;
|
||||||
|
let root = expr_root(cx, input, true);
|
||||||
|
for arms.each {|a|
|
||||||
|
let mut new_bs = sc.bs;
|
||||||
|
let root_var = path_def_id(cx, root.ex);
|
||||||
|
let pat_id_map = pat_util::pat_id_map(cx.tcx.def_map, a.pats[0]);
|
||||||
|
type info = {
|
||||||
|
id: node_id,
|
||||||
|
mut unsafe_tys: [unsafe_ty],
|
||||||
|
span: span};
|
||||||
|
let mut binding_info: [info] = [];
|
||||||
|
for a.pats.each {|pat|
|
||||||
|
for pattern_roots(cx.tcx, root.mutbl, pat).each {|proot|
|
||||||
|
let canon_id = pat_id_map.get(proot.name);
|
||||||
|
alt vec::find(binding_info, {|x| x.id == canon_id}) {
|
||||||
|
some(s) { s.unsafe_tys += unsafe_set(proot.mutbl); }
|
||||||
|
none {
|
||||||
|
binding_info += [
|
||||||
|
{id: canon_id,
|
||||||
|
mut unsafe_tys: unsafe_set(proot.mutbl),
|
||||||
|
span: proot.span}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for binding_info.each {|info|
|
||||||
|
new_bs += [mk_binding(cx, info.id, info.span, root_var,
|
||||||
|
copy info.unsafe_tys)];
|
||||||
|
};
|
||||||
|
*sc.invalid = orig_invalid;
|
||||||
|
visit::visit_arm(a, {bs: new_bs with sc}, v);
|
||||||
|
all_invalid = join_invalid(all_invalid, *sc.invalid);
|
||||||
|
};
|
||||||
|
*sc.invalid = all_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
|
||||||
|
sc: scope, v: vt<scope>) {
|
||||||
|
let root = expr_root(cx, seq, false);
|
||||||
|
|
||||||
|
// If this is a mut vector, don't allow it to be touched.
|
||||||
|
let seq_t = ty::expr_ty(cx.tcx, seq);
|
||||||
|
let mut cur_mutbl = root.mutbl;
|
||||||
|
alt ty::get(seq_t).struct {
|
||||||
|
ty::ty_vec(mt) {
|
||||||
|
if mt.mutbl != ast::m_imm {
|
||||||
|
cur_mutbl = some(contains(seq_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ {}
|
||||||
|
}
|
||||||
|
let root_var = path_def_id(cx, root.ex);
|
||||||
|
let mut new_bs = sc.bs;
|
||||||
|
for pattern_roots(cx.tcx, cur_mutbl, local.node.pat).each {|proot|
|
||||||
|
new_bs += [mk_binding(cx, proot.id, proot.span, root_var,
|
||||||
|
unsafe_set(proot.mutbl))];
|
||||||
|
}
|
||||||
|
visit::visit_block(blk, {bs: new_bs with sc}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_var(cx: ctx, ex: @ast::expr, p: @ast::path, id: ast::node_id,
|
||||||
|
assign: bool, sc: scope) {
|
||||||
|
let def = cx.tcx.def_map.get(id);
|
||||||
|
if !def_is_local_or_self(def) { ret; }
|
||||||
|
let my_defnum = ast_util::def_id_of_def(def).node;
|
||||||
|
let my_local_id = local_id_of_node(cx, my_defnum);
|
||||||
|
let var_t = ty::expr_ty(cx.tcx, ex);
|
||||||
|
for sc.bs.each {|b|
|
||||||
|
// excludes variables introduced since the alias was made
|
||||||
|
if my_local_id < b.local_id {
|
||||||
|
for b.unsafe_tys.each {|unsafe_ty|
|
||||||
|
if ty_can_unsafely_include(cx, unsafe_ty, var_t, assign) {
|
||||||
|
let inv = @{reason: val_taken, node_id: b.node_id,
|
||||||
|
sp: ex.span, path: p};
|
||||||
|
*sc.invalid = @list::cons(inv, *sc.invalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if b.node_id == my_defnum {
|
||||||
|
test_scope(cx, sc, b, some(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_lval(cx: @ctx, dest: @ast::expr, sc: scope, v: vt<scope>) {
|
||||||
|
alt dest.node {
|
||||||
|
ast::expr_path(p) {
|
||||||
|
let def = cx.tcx.def_map.get(dest.id);
|
||||||
|
let dnum = ast_util::def_id_of_def(def).node;
|
||||||
|
for sc.bs.each {|b|
|
||||||
|
if b.root_var == some(dnum) {
|
||||||
|
let inv = @{reason: overwritten, node_id: b.node_id,
|
||||||
|
sp: dest.span, path: p};
|
||||||
|
*sc.invalid = @list::cons(inv, *sc.invalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ { visit_expr(cx, dest, sc, v); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_if(c: @ast::expr, then: ast::blk, els: option<@ast::expr>,
|
||||||
|
sc: scope, v: vt<scope>) {
|
||||||
|
v.visit_expr(c, sc, v);
|
||||||
|
let orig_invalid = *sc.invalid;
|
||||||
|
v.visit_block(then, sc, v);
|
||||||
|
let then_invalid = *sc.invalid;
|
||||||
|
*sc.invalid = orig_invalid;
|
||||||
|
visit::visit_expr_opt(els, sc, v);
|
||||||
|
*sc.invalid = join_invalid(*sc.invalid, then_invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_loop(cx: ctx, sc: scope, checker: fn()) {
|
||||||
|
let orig_invalid = filter_invalid(*sc.invalid, sc.bs);
|
||||||
|
checker();
|
||||||
|
let new_invalid = filter_invalid(*sc.invalid, sc.bs);
|
||||||
|
// Have to check contents of loop again if it invalidated an alias
|
||||||
|
if list::len(orig_invalid) < list::len(new_invalid) {
|
||||||
|
let old_silent = cx.silent;
|
||||||
|
cx.silent = true;
|
||||||
|
checker();
|
||||||
|
cx.silent = old_silent;
|
||||||
|
}
|
||||||
|
*sc.invalid = new_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_scope(cx: ctx, sc: scope, b: binding, p: option<@ast::path>) {
|
||||||
|
let mut prob = find_invalid(b.node_id, *sc.invalid);
|
||||||
|
alt b.root_var {
|
||||||
|
some(dn) {
|
||||||
|
for sc.bs.each {|other|
|
||||||
|
if !is_none(prob) { break; }
|
||||||
|
if other.node_id == dn {
|
||||||
|
prob = find_invalid(other.node_id, *sc.invalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ {}
|
||||||
|
}
|
||||||
|
if !is_none(prob) && cant_copy(cx, b) {
|
||||||
|
let i = option::get(prob);
|
||||||
|
let msg = alt i.reason {
|
||||||
|
overwritten { "overwriting " + ast_util::path_name(i.path) }
|
||||||
|
val_taken { "taking the value of " + ast_util::path_name(i.path) }
|
||||||
|
};
|
||||||
|
let refname = alt p {
|
||||||
|
some(pt) { "reference " + ast_util::path_name(pt) +
|
||||||
|
", which is still used" }
|
||||||
|
none { "an argument" }
|
||||||
|
};
|
||||||
|
err(cx, i.sp, msg + " will invalidate " + refname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_def(cx: ctx, ex: @ast::expr) -> option<ast::def> {
|
||||||
|
ret alt ex.node {
|
||||||
|
ast::expr_path(_) { some(cx.tcx.def_map.get(ex.id)) }
|
||||||
|
_ { none }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_def_id(cx: ctx, ex: @ast::expr) -> option<ast::node_id> {
|
||||||
|
alt ex.node {
|
||||||
|
ast::expr_path(_) {
|
||||||
|
ret some(ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)).node);
|
||||||
|
}
|
||||||
|
_ { ret none; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ty_can_unsafely_include(cx: ctx, needle: unsafe_ty, haystack: ty::t,
|
||||||
|
mutbl: bool) -> bool {
|
||||||
|
fn get_mutbl(cur: bool, mt: ty::mt) -> bool {
|
||||||
|
ret cur || mt.mutbl != ast::m_imm;
|
||||||
|
}
|
||||||
|
fn helper(tcx: ty::ctxt, needle: unsafe_ty, haystack: ty::t, mutbl: bool)
|
||||||
|
-> bool {
|
||||||
|
if alt needle {
|
||||||
|
contains(ty) { ty == haystack }
|
||||||
|
mutbl_contains(ty) { mutbl && ty == haystack }
|
||||||
|
} { ret true; }
|
||||||
|
alt ty::get(haystack).struct {
|
||||||
|
ty::ty_enum(_, ts) {
|
||||||
|
for ts.tps.each {|t|
|
||||||
|
if helper(tcx, needle, t, mutbl) { ret true; }
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
ty::ty_box(mt) | ty::ty_ptr(mt) | ty::ty_uniq(mt) {
|
||||||
|
ret helper(tcx, needle, mt.ty, get_mutbl(mutbl, mt));
|
||||||
|
}
|
||||||
|
ty::ty_rec(fields) {
|
||||||
|
for fields.each {|f|
|
||||||
|
if helper(tcx, needle, f.mt.ty, get_mutbl(mutbl, f.mt)) {
|
||||||
|
ret true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
ty::ty_tup(ts) {
|
||||||
|
for ts.each {|t| if helper(tcx, needle, t, mutbl) { ret true; } }
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
ty::ty_fn({proto: ast::proto_bare, _}) { ret false; }
|
||||||
|
// These may contain anything.
|
||||||
|
ty::ty_fn(_) | ty::ty_iface(_, _) { ret true; }
|
||||||
|
// A type param may include everything, but can only be
|
||||||
|
// treated as opaque downstream, and is thus safe unless we
|
||||||
|
// saw mut fields, in which case the whole thing can be
|
||||||
|
// overwritten.
|
||||||
|
ty::ty_param(_, _) { ret mutbl; }
|
||||||
|
_ { ret false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret helper(cx.tcx, needle, haystack, mutbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn def_is_local_or_self(d: ast::def) -> bool {
|
||||||
|
alt d {
|
||||||
|
ast::def_local(_, _) | ast::def_arg(_, _) | ast::def_binding(_) |
|
||||||
|
ast::def_upvar(_, _, _) | ast::def_self(_) { true }
|
||||||
|
_ { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_id_of_node(cx: ctx, id: node_id) -> uint {
|
||||||
|
alt cx.tcx.items.find(id) {
|
||||||
|
some(ast_map::node_arg(_, id)) | some(ast_map::node_local(id)) { id }
|
||||||
|
_ { 0u }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heuristic, somewhat random way to decide whether to warn when inserting an
|
||||||
|
// implicit copy.
|
||||||
|
fn copy_is_expensive(tcx: ty::ctxt, ty: ty::t) -> bool {
|
||||||
|
fn score_ty(tcx: ty::ctxt, ty: ty::t) -> uint {
|
||||||
|
ret alt ty::get(ty).struct {
|
||||||
|
ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_int(_) |
|
||||||
|
ty::ty_uint(_) | ty::ty_float(_) | ty::ty_type |
|
||||||
|
ty::ty_ptr(_) { 1u }
|
||||||
|
ty::ty_box(_) | ty::ty_iface(_, _) { 3u }
|
||||||
|
ty::ty_constr(t, _) | ty::ty_res(_, t, _) { score_ty(tcx, t) }
|
||||||
|
ty::ty_fn(_) { 4u }
|
||||||
|
ty::ty_str | ty::ty_vec(_) | ty::ty_param(_, _) { 50u }
|
||||||
|
ty::ty_uniq(mt) { 1u + score_ty(tcx, mt.ty) }
|
||||||
|
ty::ty_enum(_, substs) {
|
||||||
|
substs.tps.foldl(0u) { |sum, t| sum + score_ty(tcx, t) }
|
||||||
|
}
|
||||||
|
ty::ty_tup(ts) {
|
||||||
|
ts.foldl(0u) { |sum, t| sum + score_ty(tcx, t) }
|
||||||
|
}
|
||||||
|
ty::ty_rec(fs) {
|
||||||
|
let mut sum = 0u;
|
||||||
|
for fs.each {|f| sum += score_ty(tcx, f.mt.ty); }
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
_ {
|
||||||
|
tcx.sess.warn(#fmt("score_ty: unexpected type %s",
|
||||||
|
ty_to_str(tcx, ty)));
|
||||||
|
1u // ???
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ret score_ty(tcx, ty) > 8u;
|
||||||
|
}
|
||||||
|
|
||||||
|
type pattern_root = {id: node_id,
|
||||||
|
name: ident,
|
||||||
|
mutbl: option<unsafe_ty>,
|
||||||
|
span: span};
|
||||||
|
|
||||||
|
fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat)
|
||||||
|
-> [pattern_root] {
|
||||||
|
fn walk(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat,
|
||||||
|
&set: [pattern_root]) {
|
||||||
|
alt pat.node {
|
||||||
|
ast::pat_ident(nm, sub)
|
||||||
|
if !pat_util::pat_is_variant(tcx.def_map, pat) {
|
||||||
|
set += [{id: pat.id, name: path_to_ident(nm), mutbl: mutbl,
|
||||||
|
span: pat.span}];
|
||||||
|
option::iter(sub) {|p| walk(tcx, mutbl, p, set); };
|
||||||
|
}
|
||||||
|
ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
|
||||||
|
ast::pat_ident(_, _) | ast::pat_enum(_, none) {}
|
||||||
|
ast::pat_enum(_, some(ps)) | ast::pat_tup(ps) {
|
||||||
|
for ps.each {|p| walk(tcx, mutbl, p, set); }
|
||||||
|
}
|
||||||
|
ast::pat_rec(fs, _) {
|
||||||
|
let ty = ty::node_id_to_type(tcx, pat.id);
|
||||||
|
for fs.each {|f|
|
||||||
|
let m = ty::get_field(ty, f.ident).mt.mutbl != ast::m_imm,
|
||||||
|
c = if m { some(contains(ty)) } else { mutbl };
|
||||||
|
walk(tcx, c, f.pat, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::pat_box(p) {
|
||||||
|
let ty = ty::node_id_to_type(tcx, pat.id);
|
||||||
|
let m = alt ty::get(ty).struct {
|
||||||
|
ty::ty_box(mt) { mt.mutbl != ast::m_imm }
|
||||||
|
_ { tcx.sess.span_bug(pat.span, "box pat has non-box type"); }
|
||||||
|
},
|
||||||
|
c = if m {some(contains(ty)) } else { mutbl };
|
||||||
|
walk(tcx, c, p, set);
|
||||||
|
}
|
||||||
|
ast::pat_uniq(p) {
|
||||||
|
let ty = ty::node_id_to_type(tcx, pat.id);
|
||||||
|
let m = alt ty::get(ty).struct {
|
||||||
|
ty::ty_uniq(mt) { mt.mutbl != ast::m_imm }
|
||||||
|
_ { tcx.sess.span_bug(pat.span, "uniq pat has non-uniq type"); }
|
||||||
|
},
|
||||||
|
c = if m { some(contains(ty)) } else { mutbl };
|
||||||
|
walk(tcx, c, p, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut set = [];
|
||||||
|
walk(tcx, mutbl, pat, set);
|
||||||
|
ret set;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum deref_t { unbox(bool), field, index, }
|
||||||
|
|
||||||
|
type deref = @{mutbl: bool, kind: deref_t, outer_t: ty::t};
|
||||||
|
|
||||||
|
fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool)
|
||||||
|
-> {ex: @ast::expr, mutbl: option<unsafe_ty>} {
|
||||||
|
|
||||||
|
fn maybe_auto_unbox(tcx: ty::ctxt, t: ty::t) -> {t: ty::t, ds: [deref]} {
|
||||||
|
let mut ds = [], t = t;
|
||||||
|
loop {
|
||||||
|
alt ty::get(t).struct {
|
||||||
|
ty::ty_box(mt) | ty::ty_uniq(mt) | ty::ty_rptr(_, mt) {
|
||||||
|
ds += [@{mutbl: mt.mutbl == ast::m_mutbl,
|
||||||
|
kind: unbox(false),
|
||||||
|
outer_t: t}];
|
||||||
|
t = mt.ty;
|
||||||
|
}
|
||||||
|
ty::ty_res(_, inner, substs) {
|
||||||
|
ds += [@{mutbl: false, kind: unbox(false), outer_t: t}];
|
||||||
|
t = ty::subst(tcx, substs, inner);
|
||||||
|
}
|
||||||
|
ty::ty_enum(did, substs) {
|
||||||
|
let variants = ty::enum_variants(tcx, did);
|
||||||
|
if vec::len(*variants) != 1u ||
|
||||||
|
vec::len(variants[0].args) != 1u {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ds += [@{mutbl: false, kind: unbox(false), outer_t: t}];
|
||||||
|
t = ty::subst(tcx, substs, variants[0].args[0]);
|
||||||
|
}
|
||||||
|
_ { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret {t: t, ds: ds};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_root_(tcx: ty::ctxt, ctor_self: option<node_id>,
|
||||||
|
ex: @ast::expr, autoderef: bool) -> {ex: @ast::expr,
|
||||||
|
ds: @[deref]} {
|
||||||
|
let mut ds: [deref] = [], ex = ex;
|
||||||
|
loop {
|
||||||
|
alt copy ex.node {
|
||||||
|
ast::expr_field(base, ident, _) {
|
||||||
|
let auto_unbox =
|
||||||
|
maybe_auto_unbox(tcx, ty::expr_ty(tcx, base));
|
||||||
|
let mut is_mutbl = false;
|
||||||
|
alt ty::get(auto_unbox.t).struct {
|
||||||
|
ty::ty_rec(fields) {
|
||||||
|
for fields.each {|fld|
|
||||||
|
if str::eq(ident, fld.ident) {
|
||||||
|
is_mutbl = fld.mt.mutbl == ast::m_mutbl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ty_class(did, _) {
|
||||||
|
util::common::log_expr(*base);
|
||||||
|
let in_self = alt ctor_self {
|
||||||
|
some(selfid) {
|
||||||
|
alt tcx.def_map.find(base.id) {
|
||||||
|
some(ast::def_self(slfid)) { slfid == selfid }
|
||||||
|
_ { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
none { false }
|
||||||
|
};
|
||||||
|
for ty::lookup_class_fields(tcx, did).each {|fld|
|
||||||
|
if str::eq(ident, fld.ident) {
|
||||||
|
is_mutbl = fld.mutability == ast::class_mutable
|
||||||
|
|| in_self; // all fields can be mutated
|
||||||
|
// in the ctor
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ {}
|
||||||
|
}
|
||||||
|
ds += [@{mutbl:is_mutbl, kind:field, outer_t:auto_unbox.t}];
|
||||||
|
ds += auto_unbox.ds;
|
||||||
|
ex = base;
|
||||||
|
}
|
||||||
|
ast::expr_index(base, _) {
|
||||||
|
let auto_unbox =
|
||||||
|
maybe_auto_unbox(tcx, ty::expr_ty(tcx, base));
|
||||||
|
alt ty::get(auto_unbox.t).struct {
|
||||||
|
ty::ty_evec(mt, _) |
|
||||||
|
ty::ty_vec(mt) {
|
||||||
|
ds +=
|
||||||
|
[@{mutbl: mt.mutbl == ast::m_mutbl,
|
||||||
|
kind: index,
|
||||||
|
outer_t: auto_unbox.t}];
|
||||||
|
}
|
||||||
|
ty::ty_estr(_) |
|
||||||
|
ty::ty_str {
|
||||||
|
ds += [@{mutbl:false, kind:index, outer_t:auto_unbox.t}];
|
||||||
|
}
|
||||||
|
_ { break; }
|
||||||
|
}
|
||||||
|
ds += auto_unbox.ds;
|
||||||
|
ex = base;
|
||||||
|
}
|
||||||
|
ast::expr_unary(op, base) {
|
||||||
|
if op == ast::deref {
|
||||||
|
let base_t = ty::expr_ty(tcx, base);
|
||||||
|
let mut is_mutbl = false, ptr = false;
|
||||||
|
alt ty::get(base_t).struct {
|
||||||
|
ty::ty_box(mt) { is_mutbl = mt.mutbl==ast::m_mutbl; }
|
||||||
|
ty::ty_uniq(mt) { is_mutbl = mt.mutbl==ast::m_mutbl; }
|
||||||
|
ty::ty_res(_, _, _) { }
|
||||||
|
ty::ty_enum(_, _) { }
|
||||||
|
ty::ty_ptr(mt) | ty::ty_rptr(_, mt) {
|
||||||
|
is_mutbl = mt.mutbl==ast::m_mutbl;
|
||||||
|
ptr = true;
|
||||||
|
}
|
||||||
|
_ {
|
||||||
|
tcx.sess.span_bug(
|
||||||
|
base.span,
|
||||||
|
"ill-typed base expression in deref"); }
|
||||||
|
}
|
||||||
|
ds += [@{mutbl: is_mutbl, kind: unbox(ptr && is_mutbl),
|
||||||
|
outer_t: base_t}];
|
||||||
|
ex = base;
|
||||||
|
} else { break; }
|
||||||
|
}
|
||||||
|
_ { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if autoderef {
|
||||||
|
let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, ex));
|
||||||
|
ds += auto_unbox.ds;
|
||||||
|
}
|
||||||
|
ret {ex: ex, ds: @ds};
|
||||||
|
}
|
||||||
|
|
||||||
|
let base_root = expr_root_(cx.tcx, none, ex, autoderef);
|
||||||
|
let mut unsafe_ty = none;
|
||||||
|
for vec::each(*base_root.ds) {|d|
|
||||||
|
if d.mutbl { unsafe_ty = some(contains(d.outer_t)); break; }
|
||||||
|
}
|
||||||
|
ret {ex: base_root.ex, mutbl: unsafe_ty};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unsafe_set(from: option<unsafe_ty>) -> [unsafe_ty] {
|
||||||
|
alt from { some(t) { [t] } _ { [] } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_invalid(id: node_id, lst: @list<@invalid>) -> option<@invalid> {
|
||||||
|
let mut cur = lst;
|
||||||
|
loop {
|
||||||
|
alt *cur {
|
||||||
|
list::nil { ret none; }
|
||||||
|
list::cons(head, tail) {
|
||||||
|
if head.node_id == id { ret some(head); }
|
||||||
|
cur = tail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_invalid(a: @list<@invalid>, b: @list<@invalid>) -> @list<@invalid> {
|
||||||
|
let mut result = a;
|
||||||
|
list::iter(b) {|elt|
|
||||||
|
let mut found = false;
|
||||||
|
list::iter(a) {|e| if e == elt { found = true; } }
|
||||||
|
if !found { result = @list::cons(elt, result); }
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_invalid(src: @list<@invalid>, bs: [binding]) -> @list<@invalid> {
|
||||||
|
let mut out = @list::nil, cur = src;
|
||||||
|
loop {
|
||||||
|
alt *cur {
|
||||||
|
list::cons(head, tail) {
|
||||||
|
let p = vec::position(bs, {|b| b.node_id == head.node_id});
|
||||||
|
if !is_none(p) { out = @list::cons(head, out); }
|
||||||
|
cur = tail;
|
||||||
|
}
|
||||||
|
list::nil {
|
||||||
|
ret out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err(cx: ctx, sp: span, err: str) {
|
||||||
|
if !cx.silent || !cx.tcx.sess.has_errors() {
|
||||||
|
cx.tcx.sess.span_err(sp, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
// mode: rust
|
||||||
|
// fill-column: 78;
|
||||||
|
// indent-tabs-mode: nil
|
||||||
|
// c-basic-offset: 4
|
||||||
|
// buffer-file-coding-system: utf-8-unix
|
||||||
|
// End:
|
@ -52,6 +52,7 @@ export decode_inlined_item;
|
|||||||
type maps = {
|
type 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,
|
||||||
|
copy_map: middle::alias::copy_map,
|
||||||
last_use_map: middle::liveness::last_use_map,
|
last_use_map: middle::liveness::last_use_map,
|
||||||
impl_map: middle::resolve::impl_map,
|
impl_map: middle::resolve::impl_map,
|
||||||
method_map: middle::typeck::method_map,
|
method_map: middle::typeck::method_map,
|
||||||
@ -830,6 +831,12 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
option::iter(maps.copy_map.find(id)) {|_m|
|
||||||
|
ebml_w.tag(c::tag_table_copy) {||
|
||||||
|
ebml_w.id(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
option::iter(maps.last_use_map.find(id)) {|m|
|
option::iter(maps.last_use_map.find(id)) {|m|
|
||||||
ebml_w.tag(c::tag_table_last_use) {||
|
ebml_w.tag(c::tag_table_last_use) {||
|
||||||
ebml_w.id(id);
|
ebml_w.id(id);
|
||||||
@ -936,6 +943,8 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
|||||||
|
|
||||||
if tag == (c::tag_table_mutbl as uint) {
|
if tag == (c::tag_table_mutbl as uint) {
|
||||||
dcx.maps.mutbl_map.insert(id, ());
|
dcx.maps.mutbl_map.insert(id, ());
|
||||||
|
} else if tag == (c::tag_table_copy as uint) {
|
||||||
|
dcx.maps.copy_map.insert(id, ());
|
||||||
} else {
|
} else {
|
||||||
let val_doc = entry_doc[c::tag_table_val];
|
let val_doc = entry_doc[c::tag_table_val];
|
||||||
let val_dsr = ebml::ebml_deserializer(val_doc);
|
let val_dsr = ebml::ebml_deserializer(val_doc);
|
||||||
|
@ -171,12 +171,28 @@ export check_crate, root_map, mutbl_map;
|
|||||||
fn check_crate(tcx: ty::ctxt,
|
fn check_crate(tcx: ty::ctxt,
|
||||||
method_map: typeck::method_map,
|
method_map: typeck::method_map,
|
||||||
crate: @ast::crate) -> (root_map, mutbl_map) {
|
crate: @ast::crate) -> (root_map, mutbl_map) {
|
||||||
|
|
||||||
|
// big hack to keep this off except when I want it on
|
||||||
|
let msg_level = if tcx.sess.opts.borrowck != 0u {
|
||||||
|
tcx.sess.opts.borrowck
|
||||||
|
} else {
|
||||||
|
os::getenv("RUST_BORROWCK").map_default(0u) { |v|
|
||||||
|
option::get(uint::from_str(v))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let bccx = @{tcx: tcx,
|
let bccx = @{tcx: tcx,
|
||||||
method_map: method_map,
|
method_map: method_map,
|
||||||
|
msg_level: msg_level,
|
||||||
root_map: root_map(),
|
root_map: root_map(),
|
||||||
mutbl_map: int_hash()};
|
mutbl_map: int_hash()};
|
||||||
|
|
||||||
let req_maps = gather_loans::gather_loans(bccx, crate);
|
let req_maps = if msg_level > 0u {
|
||||||
|
gather_loans::gather_loans(bccx, crate)
|
||||||
|
} else {
|
||||||
|
{req_loan_map: int_hash(),
|
||||||
|
pure_map: int_hash()}
|
||||||
|
};
|
||||||
check_loans::check_loans(bccx, req_maps, crate);
|
check_loans::check_loans(bccx, req_maps, crate);
|
||||||
ret (bccx.root_map, bccx.mutbl_map);
|
ret (bccx.root_map, bccx.mutbl_map);
|
||||||
}
|
}
|
||||||
@ -186,6 +202,7 @@ fn check_crate(tcx: ty::ctxt,
|
|||||||
|
|
||||||
type borrowck_ctxt = @{tcx: ty::ctxt,
|
type borrowck_ctxt = @{tcx: ty::ctxt,
|
||||||
method_map: typeck::method_map,
|
method_map: typeck::method_map,
|
||||||
|
msg_level: uint,
|
||||||
root_map: root_map,
|
root_map: root_map,
|
||||||
mutbl_map: mutbl_map};
|
mutbl_map: mutbl_map};
|
||||||
|
|
||||||
@ -346,7 +363,11 @@ impl error_methods for borrowck_ctxt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn span_err(s: span, m: str) {
|
fn span_err(s: span, m: str) {
|
||||||
self.tcx.sess.span_err(s, m);
|
if self.msg_level == 1u {
|
||||||
|
self.tcx.sess.span_warn(s, m);
|
||||||
|
} else {
|
||||||
|
self.tcx.sess.span_err(s, m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_note(s: span, m: str) {
|
fn span_note(s: span, m: str) {
|
||||||
|
@ -614,7 +614,25 @@ fn make_phi_bindings(bcx: block, map: [exit_node],
|
|||||||
bcx.fcx.lllocals.insert(node_id, local_mem(local));
|
bcx.fcx.lllocals.insert(node_id, local_mem(local));
|
||||||
} else { success = false; }
|
} else { success = false; }
|
||||||
};
|
};
|
||||||
if !success {
|
if success {
|
||||||
|
// Copy references that the alias analysis considered unsafe
|
||||||
|
for ids.each_value {|node_id|
|
||||||
|
if bcx.ccx().maps.copy_map.contains_key(node_id) {
|
||||||
|
let local = alt bcx.fcx.lllocals.find(node_id) {
|
||||||
|
some(local_mem(x)) { x }
|
||||||
|
_ { bcx.tcx().sess.bug("someone \
|
||||||
|
forgot to document an invariant in \
|
||||||
|
make_phi_bindings"); }
|
||||||
|
};
|
||||||
|
let e_ty = node_id_type(bcx, node_id);
|
||||||
|
let alloc = alloc_ty(bcx, e_ty);
|
||||||
|
bcx = copy_val(bcx, INIT, alloc,
|
||||||
|
load_if_immediate(bcx, local, e_ty), e_ty);
|
||||||
|
add_clean(bcx, alloc, e_ty);
|
||||||
|
bcx.fcx.lllocals.insert(node_id, local_mem(alloc));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
Unreachable(bcx);
|
Unreachable(bcx);
|
||||||
}
|
}
|
||||||
ret success;
|
ret success;
|
||||||
@ -701,7 +719,7 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
|
|||||||
alt pat.node {
|
alt pat.node {
|
||||||
ast::pat_ident(_,inner) {
|
ast::pat_ident(_,inner) {
|
||||||
if pat_is_variant(bcx.tcx().def_map, pat) { ret bcx; }
|
if pat_is_variant(bcx.tcx().def_map, pat) { ret bcx; }
|
||||||
if make_copy {
|
if make_copy || ccx.maps.copy_map.contains_key(pat.id) {
|
||||||
let ty = node_id_type(bcx, pat.id);
|
let ty = node_id_type(bcx, pat.id);
|
||||||
let llty = type_of::type_of(ccx, ty);
|
let llty = type_of::type_of(ccx, ty);
|
||||||
let alloc = alloca(bcx, llty);
|
let alloc = alloca(bcx, llty);
|
||||||
|
@ -2888,11 +2888,22 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
|
|||||||
// to have type lldestty (the callee's expected type).
|
// to have type lldestty (the callee's expected type).
|
||||||
val = llvm::LLVMGetUndef(lldestty);
|
val = llvm::LLVMGetUndef(lldestty);
|
||||||
} else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
|
} else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
|
||||||
|
let mut copied = false;
|
||||||
let imm = ty::type_is_immediate(arg.ty);
|
let imm = ty::type_is_immediate(arg.ty);
|
||||||
#debug[" arg.ty=%s, imm=%b, arg_mode=%?, lv.kind=%?",
|
#debug[" arg.ty=%s, imm=%b, arg_mode=%?, lv.kind=%?",
|
||||||
ty_to_str(bcx.tcx(), arg.ty), imm, arg_mode, lv.kind];
|
ty_to_str(bcx.tcx(), arg.ty), imm, arg_mode, lv.kind];
|
||||||
if arg_mode == ast::by_ref && lv.kind != owned && imm {
|
if arg_mode == ast::by_ref && lv.kind != owned && imm {
|
||||||
val = do_spill_noroot(bcx, val);
|
val = do_spill_noroot(bcx, val);
|
||||||
|
copied = true;
|
||||||
|
}
|
||||||
|
if ccx.maps.copy_map.contains_key(e.id) && lv.kind != temporary {
|
||||||
|
if !copied {
|
||||||
|
let alloc = alloc_ty(bcx, arg.ty);
|
||||||
|
bcx = copy_val(bcx, INIT, alloc,
|
||||||
|
load_if_immediate(bcx, val, arg.ty), arg.ty);
|
||||||
|
val = alloc;
|
||||||
|
} else { bcx = take_ty(bcx, val, arg.ty); }
|
||||||
|
add_clean(bcx, val, arg.ty);
|
||||||
}
|
}
|
||||||
if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
|
if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
|
||||||
val = Load(bcx, val);
|
val = Load(bcx, val);
|
||||||
|
@ -19,13 +19,11 @@ enum reflector = {
|
|||||||
impl methods for reflector {
|
impl methods for reflector {
|
||||||
|
|
||||||
fn c_uint(u: uint) -> ValueRef {
|
fn c_uint(u: uint) -> ValueRef {
|
||||||
let bcx = self.bcx;
|
C_uint(self.bcx.ccx(), u)
|
||||||
C_uint(bcx.ccx(), u)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit(ty_name: str, args: [ValueRef]) {
|
fn visit(ty_name: str, args: [ValueRef]) {
|
||||||
let bcx = self.bcx;
|
let tcx = self.bcx.tcx();
|
||||||
let tcx = bcx.tcx();
|
|
||||||
let mth_idx = option::get(ty::method_idx("visit_" + ty_name,
|
let mth_idx = option::get(ty::method_idx("visit_" + ty_name,
|
||||||
*self.visitor_methods));
|
*self.visitor_methods));
|
||||||
let mth_ty = ty::mk_fn(tcx, self.visitor_methods[mth_idx].fty);
|
let mth_ty = ty::mk_fn(tcx, self.visitor_methods[mth_idx].fty);
|
||||||
|
@ -78,6 +78,7 @@ mod middle {
|
|||||||
mod loan;
|
mod loan;
|
||||||
mod preserve;
|
mod preserve;
|
||||||
}
|
}
|
||||||
|
mod alias;
|
||||||
mod liveness;
|
mod liveness;
|
||||||
mod block_use;
|
mod block_use;
|
||||||
mod kind;
|
mod kind;
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
type point = { x: int, y: int };
|
type point = { x: int, y: int };
|
||||||
|
|
||||||
fn a() {
|
fn a() {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
type point = { x: int, y: int };
|
type point = { x: int, y: int };
|
||||||
|
|
||||||
fn a() {
|
fn a() {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn borrow(_v: &int) {}
|
fn borrow(_v: &int) {}
|
||||||
|
|
||||||
fn borrow_from_arg_imm_ref(&&v: ~int) {
|
fn borrow_from_arg_imm_ref(&&v: ~int) {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
// Note: the borrowck analysis is currently flow-insensitive.
|
// Note: the borrowck analysis is currently flow-insensitive.
|
||||||
// Therefore, some of these errors are marked as spurious and could be
|
// Therefore, some of these errors are marked as spurious and could be
|
||||||
// corrected by a simple change to the analysis. The others are
|
// corrected by a simple change to the analysis. The others are
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn borrow(v: &int, f: fn(x: &int)) {
|
fn borrow(v: &int, f: fn(x: &int)) {
|
||||||
f(v);
|
f(v);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn take(-_v: ~int) {
|
fn take(-_v: ~int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn borrow(v: &int, f: fn(x: &int)) {
|
fn borrow(v: &int, f: fn(x: &int)) {
|
||||||
f(v);
|
f(v);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
type point = { x: int, y: int };
|
type point = { x: int, y: int };
|
||||||
|
|
||||||
impl foo for point {
|
impl foo for point {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
type point = { x: int, y: int };
|
type point = { x: int, y: int };
|
||||||
|
|
||||||
impl foo for point {
|
impl foo for point {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
// Here we check that it is allowed to lend out an element of a
|
// Here we check that it is allowed to lend out an element of a
|
||||||
// (locally rooted) mutable, unique vector, and that we then prevent
|
// (locally rooted) mutable, unique vector, and that we then prevent
|
||||||
// modifications to the contents.
|
// modifications to the contents.
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn want_slice(v: [int]/&) -> int {
|
fn want_slice(v: [int]/&) -> int {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for vec::each(v) { |i| sum += i; }
|
for vec::each(v) { |i| sum += i; }
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
enum cycle {
|
|
||||||
node({mut a: ~cycle}),
|
|
||||||
empty
|
|
||||||
}
|
|
||||||
fn main() {
|
|
||||||
let x = ~node({mut a: ~empty});
|
|
||||||
// Create a cycle!
|
|
||||||
alt check *x { //! NOTE loan of immutable local variable granted here
|
|
||||||
node(y) {
|
|
||||||
y.a <- x; //! ERROR moving out of immutable local variable prohibited due to outstanding loan
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn match_imm_box(v: &const @option<int>) -> int {
|
fn match_imm_box(v: &const @option<int>) -> int {
|
||||||
alt *v {
|
alt *v {
|
||||||
@some(i) {i}
|
@some(i) {i}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn match_ref(&&v: option<int>) -> int {
|
fn match_ref(&&v: option<int>) -> int {
|
||||||
alt v {
|
alt v {
|
||||||
some(i) {
|
some(i) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// xfail-pretty -- comments are infaithfully preserved
|
// xfail-pretty -- comments are infaithfully preserved
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// xfail-pretty -- comments are infaithfully preserved
|
// xfail-pretty -- comments are infaithfully preserved
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
pure fn pure_borrow(_x: &int, _y: ()) {}
|
pure fn pure_borrow(_x: &int, _y: ()) {}
|
||||||
|
|
||||||
fn test1(x: @mut ~int) {
|
fn test1(x: @mut ~int) {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn impure(_i: int) {}
|
fn impure(_i: int) {}
|
||||||
|
|
||||||
// check that unchecked alone does not override borrowck:
|
// check that unchecked alone does not override borrowck:
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn borrow(_v: &int) {}
|
fn borrow(_v: &int) {}
|
||||||
|
|
||||||
fn box_mut(v: @mut ~int) {
|
fn box_mut(v: @mut ~int) {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn borrow(_v: &int) {}
|
fn borrow(_v: &int) {}
|
||||||
|
|
||||||
fn local() {
|
fn local() {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
fn borrow(_v: &int) {}
|
fn borrow(_v: &int) {}
|
||||||
|
|
||||||
fn box_mut(v: &mut ~int) {
|
fn box_mut(v: &mut ~int) {
|
||||||
|
@ -8,6 +8,8 @@ fn f<T>(&o: option<T>) {
|
|||||||
fn main() {
|
fn main() {
|
||||||
f::<int>(option::none);
|
f::<int>(option::none);
|
||||||
//!^ ERROR taking mut reference to static item
|
//!^ ERROR taking mut reference to static item
|
||||||
//!^^ ERROR illegal borrow unless pure: creating mutable alias to aliasable, immutable memory
|
|
||||||
//!^^^ NOTE impure due to access to impure function
|
// Additional errors reported by borrowck:
|
||||||
|
//^^ ERROR illegal borrow unless pure: creating mutable alias to aliasable, immutable memory
|
||||||
|
//^^^ NOTE impure due to access to impure function
|
||||||
}
|
}
|
8
src/test/compile-fail/unsafe-alias-2.rs
Normal file
8
src/test/compile-fail/unsafe-alias-2.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// error-pattern:invalidate reference x
|
||||||
|
|
||||||
|
fn whoknows(x: @mut {mut x: int}) { x.x = 10; }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let box = @mut {mut x: 1};
|
||||||
|
alt *box { x { whoknows(box); log(error, x); } }
|
||||||
|
}
|
10
src/test/compile-fail/unsafe-alias.rs
Normal file
10
src/test/compile-fail/unsafe-alias.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// error-pattern:may alias with argument
|
||||||
|
|
||||||
|
fn foo(x: {mut x: int}, f: fn@()) { log(debug, x); }
|
||||||
|
|
||||||
|
fn whoknows(x: @mut {mut x: int}) { *x = {mut x: 10}; }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let box = @mut {mut x: 1};
|
||||||
|
foo(*box, bind whoknows(box));
|
||||||
|
}
|
8
src/test/compile-fail/unsafe-alt.rs
Normal file
8
src/test/compile-fail/unsafe-alt.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// error-pattern:invalidate reference i
|
||||||
|
|
||||||
|
enum foo { left({mut x: int}), right(bool) }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = left({mut x: 10});
|
||||||
|
alt x { left(i) { x = right(false); copy x; log(debug, i); } _ { } }
|
||||||
|
}
|
8
src/test/compile-fail/unsafe-mutable-alias.rs
Normal file
8
src/test/compile-fail/unsafe-mutable-alias.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// error-pattern:mut reference to a variable that roots another reference
|
||||||
|
|
||||||
|
fn f(a: {mut x: int}, &b: {mut x: int}) -> int {
|
||||||
|
b.x += 1;
|
||||||
|
ret a.x + b.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { let i = {mut x: 4}; log(debug, f(i, i)); }
|
@ -1,6 +1,6 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let x = ~{mut a: ~10, b: ~20};
|
let x = ~{mut a: ~10, b: ~20};
|
||||||
alt x {
|
alt x {
|
||||||
~{a, b} { assert *a == 10; (*x).a = ~30; assert *a == 30; }
|
~{a, b} { assert *a == 10; (*x).a = ~30; assert *a == 10; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let x = @{mut a: @10, b: @20};
|
let x = @{mut a: @10, b: @20};
|
||||||
alt x {
|
alt x {
|
||||||
@{a, b} { assert *a == 10; (*x).a = @30; assert *a == 30; }
|
@{a, b} { assert *a == 10; (*x).a = @30; assert *a == 10; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn want_slice(v: [int]/&) -> int {
|
fn want_slice(v: [int]/&) -> int {
|
||||||
let mut sum = 0;
|
let mut sum = 0;
|
||||||
for vec::each(v) { |i| sum += i; }
|
for vec::each(v) { |i| sum += i; }
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut x = none;
|
let mut x = none;
|
||||||
alt x {
|
alt x {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn borrow(x: &int, f: fn(x: &int)) {
|
fn borrow(x: &int, f: fn(x: &int)) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn borrow(x: &int, f: fn(x: &int)) {
|
fn borrow(x: &int, f: fn(x: &int)) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn switcher(x: option<@int>) {
|
fn switcher(x: option<@int>) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn borrow(x: &int, f: fn(x: &int)) {
|
fn borrow(x: &int, f: fn(x: &int)) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn testfn(cond: bool) {
|
fn testfn(cond: bool) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// xfail-fast (compile-flags unsupported on windows)
|
||||||
|
// compile-flags:--borrowck=err
|
||||||
// exec-env:RUST_POISON_ON_FREE=1
|
// exec-env:RUST_POISON_ON_FREE=1
|
||||||
|
|
||||||
fn borrow(x: &int, f: fn(x: &int)) {
|
fn borrow(x: &int, f: fn(x: &int)) {
|
||||||
|
@ -91,7 +91,7 @@ fn main() {
|
|||||||
intrinsic::visit_ty::<i16>(vv);
|
intrinsic::visit_ty::<i16>(vv);
|
||||||
intrinsic::visit_ty::<[int]>(vv);
|
intrinsic::visit_ty::<[int]>(vv);
|
||||||
|
|
||||||
for (copy v.types).each {|s|
|
for v.types.each {|s|
|
||||||
io::println(#fmt("type: %s", s));
|
io::println(#fmt("type: %s", s));
|
||||||
}
|
}
|
||||||
assert v.types == ["bool", "int", "i8", "i16",
|
assert v.types == ["bool", "int", "i8", "i16",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user