remove alias analysis and replace with borrowck

cc #2540
This commit is contained in:
Niko Matsakis 2012-06-07 13:56:52 -07:00
parent 8c6d439a81
commit 7f6ee0ce0d
51 changed files with 31 additions and 1134 deletions

View File

@ -1382,83 +1382,7 @@ gets access to them.
## Safe references
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.
*This system has recently changed. An explanantion is forthcoming.*
## Other uses of safe references

View File

@ -204,9 +204,6 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
let (root_map, mutbl_map) = time(
time_passes, "borrow checking",
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",
bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
time(time_passes, "lint checking",
@ -216,7 +213,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
let outputs = option::get(outputs);
let maps = {mutbl_map: mutbl_map, root_map: root_map,
copy_map: copy_map, last_use_map: last_use_map,
last_use_map: last_use_map,
impl_map: impl_map, method_map: method_map,
vtable_map: vtable_map};
@ -448,14 +445,6 @@ fn build_session_options(match: getopts::match,
let sysroot_opt = getopts::opt_maybe_str(match, "sysroot");
let target_opt = getopts::opt_maybe_str(match, "target");
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 {
// unless we're emitting huamn-readable assembly, omit comments.
link::output_type_llvm_assembly | link::output_type_assembly {}
@ -504,8 +493,7 @@ fn build_session_options(match: getopts::match,
test: test,
parse_only: parse_only,
no_trans: no_trans,
debugging_opts: debugging_opts,
borrowck: borrowck};
debugging_opts: debugging_opts};
ret sopts;
}
@ -582,8 +570,7 @@ fn opts() -> [getopts::opt] {
optmulti("Z"),
optmulti("cfg"), optflag("test"),
optflag("lib"), optflag("bin"), optflag("static"), optflag("gc"),
optopt("borrowck")];
optflag("lib"), optflag("bin"), optflag("static"), optflag("gc")];
}
type output_filenames = @{out_filename: str, obj_filename:str};

View File

@ -66,9 +66,6 @@ type options =
no_trans: bool,
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]};
@ -181,8 +178,7 @@ fn basic_options() -> @options {
test: false,
parse_only: false,
no_trans: false,
debugging_opts: 0u,
borrowck: 0u,
debugging_opts: 0u
}
}

View File

@ -116,7 +116,6 @@ enum astencode_tag { // Reserves 0x50 -- 0x6f
tag_table_param_bounds,
tag_table_inferred_modes,
tag_table_mutbl,
tag_table_copy,
tag_table_last_use,
tag_table_spill,
tag_table_method_map,

View File

@ -1,847 +0,0 @@
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:

View File

@ -52,7 +52,6 @@ export decode_inlined_item;
type maps = {
mutbl_map: middle::borrowck::mutbl_map,
root_map: middle::borrowck::root_map,
copy_map: middle::alias::copy_map,
last_use_map: middle::liveness::last_use_map,
impl_map: middle::resolve::impl_map,
method_map: middle::typeck::method_map,
@ -831,12 +830,6 @@ 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|
ebml_w.tag(c::tag_table_last_use) {||
ebml_w.id(id);
@ -943,8 +936,6 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
if tag == (c::tag_table_mutbl as uint) {
dcx.maps.mutbl_map.insert(id, ());
} else if tag == (c::tag_table_copy as uint) {
dcx.maps.copy_map.insert(id, ());
} else {
let val_doc = entry_doc[c::tag_table_val];
let val_dsr = ebml::ebml_deserializer(val_doc);

View File

@ -171,28 +171,12 @@ export check_crate, root_map, mutbl_map;
fn check_crate(tcx: ty::ctxt,
method_map: typeck::method_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,
method_map: method_map,
msg_level: msg_level,
root_map: root_map(),
mutbl_map: int_hash()};
let req_maps = if msg_level > 0u {
gather_loans::gather_loans(bccx, crate)
} else {
{req_loan_map: int_hash(),
pure_map: int_hash()}
};
let req_maps = gather_loans::gather_loans(bccx, crate);
check_loans::check_loans(bccx, req_maps, crate);
ret (bccx.root_map, bccx.mutbl_map);
}
@ -202,7 +186,6 @@ fn check_crate(tcx: ty::ctxt,
type borrowck_ctxt = @{tcx: ty::ctxt,
method_map: typeck::method_map,
msg_level: uint,
root_map: root_map,
mutbl_map: mutbl_map};
@ -363,11 +346,7 @@ impl error_methods for borrowck_ctxt {
}
fn span_err(s: span, m: str) {
if self.msg_level == 1u {
self.tcx.sess.span_warn(s, m);
} else {
self.tcx.sess.span_err(s, m);
}
self.tcx.sess.span_err(s, m);
}
fn span_note(s: span, m: str) {

View File

@ -614,25 +614,7 @@ fn make_phi_bindings(bcx: block, map: [exit_node],
bcx.fcx.lllocals.insert(node_id, local_mem(local));
} else { success = false; }
};
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 {
if !success {
Unreachable(bcx);
}
ret success;
@ -719,7 +701,7 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
alt pat.node {
ast::pat_ident(_,inner) {
if pat_is_variant(bcx.tcx().def_map, pat) { ret bcx; }
if make_copy || ccx.maps.copy_map.contains_key(pat.id) {
if make_copy {
let ty = node_id_type(bcx, pat.id);
let llty = type_of::type_of(ccx, ty);
let alloc = alloca(bcx, llty);

View File

@ -2888,22 +2888,11 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
// to have type lldestty (the callee's expected type).
val = llvm::LLVMGetUndef(lldestty);
} else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
let mut copied = false;
let imm = ty::type_is_immediate(arg.ty);
#debug[" arg.ty=%s, imm=%b, 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 {
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) {
val = Load(bcx, val);

View File

@ -19,11 +19,13 @@ enum reflector = {
impl methods for reflector {
fn c_uint(u: uint) -> ValueRef {
C_uint(self.bcx.ccx(), u)
let bcx = self.bcx;
C_uint(bcx.ccx(), u)
}
fn visit(ty_name: str, args: [ValueRef]) {
let tcx = self.bcx.tcx();
let bcx = self.bcx;
let tcx = bcx.tcx();
let mth_idx = option::get(ty::method_idx("visit_" + ty_name,
*self.visitor_methods));
let mth_ty = ty::mk_fn(tcx, self.visitor_methods[mth_idx].fty);

View File

@ -78,7 +78,6 @@ mod middle {
mod loan;
mod preserve;
}
mod alias;
mod liveness;
mod block_use;
mod kind;

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
type point = { x: int, y: int };
fn a() {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
type point = { x: int, y: int };
fn a() {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn borrow_from_arg_imm_ref(&&v: ~int) {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// Note: the borrowck analysis is currently flow-insensitive.
// Therefore, some of these errors are marked as spurious and could be
// corrected by a simple change to the analysis. The others are

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn borrow(v: &int, f: fn(x: &int)) {
f(v);
}

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn take(-_v: ~int) {
}

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn borrow(v: &int, f: fn(x: &int)) {
f(v);
}

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
type point = { x: int, y: int };
impl foo for point {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
type point = { x: int, y: int };
impl foo for point {

View File

@ -1,6 +1,3 @@
// 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
// (locally rooted) mutable, unique vector, and that we then prevent
// modifications to the contents.

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn want_slice(v: [int]/&) -> int {
let mut sum = 0;
for vec::each(v) { |i| sum += i; }

View File

@ -0,0 +1,13 @@
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
}
};
}

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn match_imm_box(v: &const @option<int>) -> int {
alt *v {
@some(i) {i}

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn match_ref(&&v: option<int>) -> int {
alt v {
some(i) {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// xfail-pretty -- comments are infaithfully preserved
fn main() {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// xfail-pretty -- comments are infaithfully preserved
fn main() {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
pure fn pure_borrow(_x: &int, _y: ()) {}
fn test1(x: @mut ~int) {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn impure(_i: int) {}
// check that unchecked alone does not override borrowck:

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn box_mut(v: @mut ~int) {

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn local() {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn box_mut(v: &mut ~int) {

View File

@ -8,8 +8,6 @@ fn f<T>(&o: option<T>) {
fn main() {
f::<int>(option::none);
//!^ ERROR taking mut reference to static item
// 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
//!^^ ERROR illegal borrow unless pure: creating mutable alias to aliasable, immutable memory
//!^^^ NOTE impure due to access to impure function
}

View File

@ -1,8 +0,0 @@
// 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); } }
}

View File

@ -1,10 +0,0 @@
// 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));
}

View File

@ -1,8 +0,0 @@
// 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); } _ { } }
}

View File

@ -1,8 +0,0 @@
// 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)); }

View File

@ -1,6 +1,6 @@
fn main() {
let x = ~{mut a: ~10, b: ~20};
alt x {
~{a, b} { assert *a == 10; (*x).a = ~30; assert *a == 10; }
~{a, b} { assert *a == 10; (*x).a = ~30; assert *a == 30; }
}
}

View File

@ -1,6 +1,6 @@
fn main() {
let x = @{mut a: @10, b: @20};
alt x {
@{a, b} { assert *a == 10; (*x).a = @30; assert *a == 10; }
@{a, b} { assert *a == 10; (*x).a = @30; assert *a == 30; }
}
}

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn want_slice(v: [int]/&) -> int {
let mut sum = 0;
for vec::each(v) { |i| sum += i; }

View File

@ -1,6 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
fn main() {
let mut x = none;
alt x {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn main() {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn main() {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn borrow(x: &int, f: fn(x: &int)) {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn main() {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn borrow(x: &int, f: fn(x: &int)) {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn switcher(x: option<@int>) {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn borrow(x: &int, f: fn(x: &int)) {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn testfn(cond: bool) {

View File

@ -1,5 +1,3 @@
// xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1
fn borrow(x: &int, f: fn(x: &int)) {

View File

@ -91,7 +91,7 @@ fn main() {
intrinsic::visit_ty::<i16>(vv);
intrinsic::visit_ty::<[int]>(vv);
for v.types.each {|s|
for (copy v.types).each {|s|
io::println(#fmt("type: %s", s));
}
assert v.types == ["bool", "int", "i8", "i16",