implement new borrow ck (disabled by default)

This commit is contained in:
Niko Matsakis 2012-04-26 16:02:01 -07:00
parent 5e7229b72c
commit 50a3dd40ae
32 changed files with 2100 additions and 311 deletions

View File

@ -49,7 +49,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTSYNTAX): \
$$(TCORELIB_DEFAULT$(1)_T_$(2)_H_$(3)) \
$$(TSTDLIB_DEFAULT$(1)_T_$(2)_H_$(3))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< && touch $$@
$$(STAGE$(1)_T_$(2)_H_$(3)) $(BORROWCK) -o $$@ $$< && touch $$@
endef

View File

@ -4,7 +4,7 @@ import session::session;
import syntax::parse;
import syntax::{ast, codemap};
import syntax::attr;
import middle::{trans, resolve, freevars, kind, ty, typeck, fn_usage,
import middle::{trans, resolve, freevars, kind, ty, typeck,
last_use, lint};
import syntax::print::{pp, pprust};
import util::{ppaux, filesearch};
@ -158,12 +158,13 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
bind middle::block_use::check_crate(ty_cx, crate));
time(time_passes, "loop checking",
bind middle::check_loop::check_crate(ty_cx, crate));
time(time_passes, "function usage",
bind fn_usage::check_crate_fn_usage(ty_cx, crate));
time(time_passes, "alt checking",
bind middle::check_alt::check_crate(ty_cx, crate));
time(time_passes, "typestate checking",
bind middle::tstate::ck::check_crate(ty_cx, crate));
let _root_map = time(
time_passes, "borrow checking",
bind middle::borrowck::check_crate(ty_cx, method_map, crate));
let mutbl_map =
time(time_passes, "mutability checking",
bind middle::mutbl::check_crate(ty_cx, crate));
@ -401,6 +402,14 @@ fn build_session_options(match: getopts::match,
let target_opt = getopts::opt_maybe_str(match, "target");
let mut no_asm_comments = getopts::opt_present(match, "no-asm-comments");
let debug_rustc = getopts::opt_present(match, "debug-rustc");
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 {}
@ -455,7 +464,8 @@ fn build_session_options(match: getopts::match,
parse_only: parse_only,
no_trans: no_trans,
no_asm_comments: no_asm_comments,
debug_rustc: debug_rustc};
debug_rustc: debug_rustc,
borrowck: borrowck};
ret sopts;
}
@ -533,7 +543,8 @@ fn opts() -> [getopts::opt] {
optmulti("cfg"), optflag("test"),
optflag("lib"), optflag("bin"), optflag("static"), optflag("gc"),
optflag("no-asm-comments"),
optflag("debug-rustc")];
optflag("debug-rustc"),
optopt("borrowck")];
}
type output_filenames = @{out_filename: str, obj_filename:str};

View File

@ -47,7 +47,8 @@ type options =
parse_only: bool,
no_trans: bool,
no_asm_comments: bool,
debug_rustc: bool};
debug_rustc: bool,
borrowck: uint}; // 0=off,1=warn,2=err
type crate_metadata = {name: str, data: [u8]};
@ -139,6 +140,7 @@ fn basic_options() -> @options {
no_trans: false,
no_asm_comments: false,
debug_rustc: false,
borrowck: 0u,
}
}

View File

@ -844,9 +844,10 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
}
}
option::iter(tcx.borrowings.find(id)) {|_i|
option::iter(tcx.borrowings.find(id)) {|s|
ebml_w.tag(c::tag_table_borrowings) {||
ebml_w.id(id);
ebml_w.wr_tagged_i64(c::tag_table_val as uint, s as i64);
}
}
}
@ -919,8 +920,6 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
dcx.maps.copy_map.insert(id, ());
} else if tag == (c::tag_table_spill as uint) {
dcx.maps.spill_map.insert(id, ());
} else if tag == (c::tag_table_borrowings as uint) {
dcx.tcx.borrowings.insert(id, ());
} else {
let val_doc = entry_doc[c::tag_table_val];
let val_dsr = ebml::ebml_deserializer(val_doc);
@ -952,7 +951,10 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
val_dsr.read_method_origin(xcx));
} else if tag == (c::tag_table_vtable_map as uint) {
dcx.maps.vtable_map.insert(id,
val_dsr.read_vtable_res(xcx));
val_dsr.read_vtable_res(xcx));
} else if tag == (c::tag_table_borrowings as uint) {
let scope_id = ebml::doc_as_i64(val_doc) as int;
dcx.tcx.borrowings.insert(id, scope_id);
} else {
xcx.dcx.tcx.sess.bug(
#fmt["unknown tag found in side tables: %x", tag]);

1334
src/rustc/middle/borrowck.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +0,0 @@
import std::map::hashmap;
import syntax::ast;
import syntax::visit;
import syntax::print::pprust::expr_to_str;
import driver::session::session;
export check_crate_fn_usage;
type fn_usage_ctx = {
tcx: ty::ctxt,
unsafe_fn_legal: bool,
generic_bare_fn_legal: bool
};
fn fn_usage_expr(expr: @ast::expr,
ctx: fn_usage_ctx,
v: visit::vt<fn_usage_ctx>) {
alt expr.node {
ast::expr_path(path) {
if !ctx.unsafe_fn_legal {
alt ctx.tcx.def_map.find(expr.id) {
some(ast::def_fn(_, ast::unsafe_fn)) {
log(error, ("expr=", expr_to_str(expr)));
ctx.tcx.sess.span_fatal(
expr.span,
"unsafe functions can only be called");
}
_ {}
}
}
if !ctx.generic_bare_fn_legal
&& ty::expr_has_ty_params(ctx.tcx, expr) {
alt ty::get(ty::expr_ty(ctx.tcx, expr)).struct {
ty::ty_fn({proto: ast::proto_bare, _}) {
ctx.tcx.sess.span_fatal(
expr.span,
"generic bare functions can only be called or bound");
}
_ { }
}
}
}
ast::expr_call(f, args, _) {
let f_ctx = {unsafe_fn_legal: true,
generic_bare_fn_legal: true with ctx};
v.visit_expr(f, f_ctx, v);
let args_ctx = {unsafe_fn_legal: false,
generic_bare_fn_legal: false with ctx};
visit::visit_exprs(args, args_ctx, v);
}
ast::expr_bind(f, args) {
let f_ctx = {unsafe_fn_legal: false,
generic_bare_fn_legal: true with ctx};
v.visit_expr(f, f_ctx, v);
let args_ctx = {unsafe_fn_legal: false,
generic_bare_fn_legal: false with ctx};
for args.each {|arg|
visit::visit_expr_opt(arg, args_ctx, v);
}
}
_ {
let subctx = {unsafe_fn_legal: false,
generic_bare_fn_legal: false with ctx};
visit::visit_expr(expr, subctx, v);
}
}
}
fn check_crate_fn_usage(tcx: ty::ctxt, crate: @ast::crate) {
let visit =
visit::mk_vt(
@{visit_expr: fn_usage_expr
with *visit::default_visitor()});
let ctx = {
tcx: tcx,
unsafe_fn_legal: false,
generic_bare_fn_legal: false
};
visit::visit_crate(*crate, ctx, visit);
}
// 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

@ -24,6 +24,15 @@ export resolve_deep_var;
export compare_tys;
export fixup_err, fixup_err_to_str;
// Extra information needed to perform an assignment that may borrow.
// The `expr_id` is the is of the expression whose type is being
// assigned, and `borrow_scope` is the region scope to use if the
// value should be borrowed.
type assignment = {
expr_id: ast::node_id,
borrow_scope: ast::node_id
};
type bound<T:copy> = option<T>;
type bounds<T:copy> = {lb: bound<T>, ub: bound<T>};
@ -84,12 +93,12 @@ fn mk_eqty(cx: infer_ctxt, a: ty::t, b: ty::t) -> ures {
indent {|| cx.commit {|| cx.eq_tys(a, b) } }.to_ures()
}
fn mk_assignty(cx: infer_ctxt, a_node_id: ast::node_id,
a: ty::t, b: ty::t) -> ures {
fn mk_assignty(cx: infer_ctxt, anmnt: assignment,
a: ty::t, b: ty::t) -> ures {
#debug["mk_assignty(%? / %s <: %s)",
a_node_id, a.to_str(cx), b.to_str(cx)];
anmnt, a.to_str(cx), b.to_str(cx)];
indent {|| cx.commit {||
cx.assign_tys(a_node_id, a, b)
cx.assign_tys(anmnt, a, b)
} }.to_ures()
}
@ -765,8 +774,7 @@ impl methods for resolve_state {
// this upper-bound might be stricter than what is truly needed.
impl assignment for infer_ctxt {
fn assign_tys(a_node_id: ast::node_id,
a: ty::t, b: ty::t) -> ures {
fn assign_tys(anmnt: assignment, a: ty::t, b: ty::t) -> ures {
fn select(fst: option<ty::t>, snd: option<ty::t>) -> option<ty::t> {
alt fst {
@ -780,8 +788,8 @@ impl assignment for infer_ctxt {
}
}
#debug["assign_tys(a_node_id=%?, %s -> %s)",
a_node_id, a.to_str(self), b.to_str(self)];
#debug["assign_tys(anmnt=%?, %s -> %s)",
anmnt, a.to_str(self), b.to_str(self)];
let _r = indenter();
alt (ty::get(a).struct, ty::get(b).struct) {
@ -794,34 +802,34 @@ impl assignment for infer_ctxt {
let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
let a_bnd = select(a_bounds.ub, a_bounds.lb);
let b_bnd = select(b_bounds.lb, b_bounds.ub);
self.assign_tys_or_sub(a_node_id, a, b, a_bnd, b_bnd)
self.assign_tys_or_sub(anmnt, a, b, a_bnd, b_bnd)
}
(ty::ty_var(a_id), _) {
let {root:_, bounds:a_bounds} = self.get(self.vb, a_id);
let a_bnd = select(a_bounds.ub, a_bounds.lb);
self.assign_tys_or_sub(a_node_id, a, b, a_bnd, some(b))
self.assign_tys_or_sub(anmnt, a, b, a_bnd, some(b))
}
(_, ty::ty_var(b_id)) {
let {root:_, bounds: b_bounds} = self.get(self.vb, b_id);
let b_bnd = select(b_bounds.lb, b_bounds.ub);
self.assign_tys_or_sub(a_node_id, a, b, some(a), b_bnd)
self.assign_tys_or_sub(anmnt, a, b, some(a), b_bnd)
}
(_, _) {
self.assign_tys_or_sub(a_node_id, a, b, some(a), some(b))
self.assign_tys_or_sub(anmnt, a, b, some(a), some(b))
}
}
}
fn assign_tys_or_sub(
a_node_id: ast::node_id,
anmnt: assignment,
a: ty::t, b: ty::t,
a_bnd: option<ty::t>, b_bnd: option<ty::t>) -> ures {
#debug["assign_tys_or_sub(a_node_id=%?, %s -> %s, %s -> %s)",
a_node_id, a.to_str(self), b.to_str(self),
#debug["assign_tys_or_sub(anmnt=%?, %s -> %s, %s -> %s)",
anmnt, a.to_str(self), b.to_str(self),
a_bnd.to_str(self), b_bnd.to_str(self)];
let _r = indenter();
@ -837,34 +845,34 @@ impl assignment for infer_ctxt {
alt (ty::get(a_bnd).struct, ty::get(b_bnd).struct) {
(ty::ty_box(mt_a), ty::ty_rptr(r_b, mt_b)) {
let nr_b = ty::mk_box(self.tcx, mt_b);
self.crosspolinate(a_node_id, a, nr_b, r_b)
self.crosspolinate(anmnt, a, nr_b, r_b)
}
(ty::ty_uniq(mt_a), ty::ty_rptr(r_b, mt_b)) {
let nr_b = ty::mk_uniq(self.tcx, mt_b);
self.crosspolinate(a_node_id, a, nr_b, r_b)
self.crosspolinate(anmnt, a, nr_b, r_b)
}
(ty::ty_estr(vs_a),
ty::ty_estr(ty::vstore_slice(r_b)))
if is_borrowable(vs_a) {
let nr_b = ty::mk_estr(self.tcx, vs_a);
self.crosspolinate(a_node_id, a, nr_b, r_b)
self.crosspolinate(anmnt, a, nr_b, r_b)
}
(ty::ty_str,
ty::ty_estr(ty::vstore_slice(r_b))) {
let nr_b = ty::mk_str(self.tcx);
self.crosspolinate(a_node_id, a, nr_b, r_b)
self.crosspolinate(anmnt, a, nr_b, r_b)
}
(ty::ty_evec(mt_a, vs_a),
ty::ty_evec(mt_b, ty::vstore_slice(r_b)))
if is_borrowable(vs_a) {
let nr_b = ty::mk_evec(self.tcx, mt_b, vs_a);
self.crosspolinate(a_node_id, a, nr_b, r_b)
self.crosspolinate(anmnt, a, nr_b, r_b)
}
(ty::ty_vec(mt_a),
ty::ty_evec(mt_b, ty::vstore_slice(r_b))) {
let nr_b = ty::mk_vec(self.tcx, mt_b);
self.crosspolinate(a_node_id, a, nr_b, r_b)
self.crosspolinate(anmnt, a, nr_b, r_b)
}
_ {
self.sub_tys(a, b)
@ -877,25 +885,25 @@ impl assignment for infer_ctxt {
}
}
fn crosspolinate(a_node_id: ast::node_id,
fn crosspolinate(anmnt: assignment,
a: ty::t,
nr_b: ty::t,
r_b: ty::region) -> ures {
#debug["crosspolinate(a_node_id=%?, a=%s, nr_b=%s, r_b=%s)",
a_node_id, a.to_str(self), nr_b.to_str(self),
#debug["crosspolinate(anmnt=%?, a=%s, nr_b=%s, r_b=%s)",
anmnt, a.to_str(self), nr_b.to_str(self),
r_b.to_str(self)];
indent {||
self.sub_tys(a, nr_b).then {||
let a_scope_id = self.tcx.region_map.parents.get(a_node_id);
let r_a = ty::re_scope(a_scope_id);
#debug["a_scope_id=%?", a_scope_id];
let r_a = ty::re_scope(anmnt.borrow_scope);
#debug["anmnt=%?", anmnt];
sub(self).contraregions(r_a, r_b).chain {|_r|
// if successful, add an entry indicating that
// borrowing occurred
#debug["borrowing expression #%?", a_node_id];
self.tcx.borrowings.insert(a_node_id, ());
#debug["borrowing expression #%?", anmnt];
self.tcx.borrowings.insert(anmnt.expr_id,
anmnt.borrow_scope);
uok()
}
}

View File

@ -136,6 +136,7 @@ import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import syntax::codemap::span;
import syntax::print::pprust;
import util::common::new_def_hash;
import std::list;
@ -151,10 +152,12 @@ type binding = {node_id: ast::node_id,
br: ty::bound_region};
type region_map = {
/* Mapping from a block/function expression to its parent. */
// Mapping from a block/function expression to its parent.
parents: hashmap<ast::node_id,ast::node_id>,
/* Mapping from a local variable to its containing block. */
// Mapping from arguments and local variables to the block in
// which they are declared. Arguments are considered to be declared
// within the body of the function.
local_blocks: hashmap<ast::node_id,ast::node_id>
};
@ -163,7 +166,43 @@ type ctxt = {
def_map: resolve::def_map,
region_map: @region_map,
parent: parent
// These two fields (parent and closure_parent) specify the parent
// scope of the current expression. The parent scope is the
// innermost block, call, or alt expression during the execution
// of which the current expression will be evaluated. Generally
// speaking, the innermost parent scope is also the closest
// suitable ancestor in the AST tree.
//
// However, there are two subtle cases where the parent scope for
// an expression is not strictly derived from the AST. The first
// such exception concerns call arguments and the second concerns
// closures (which, at least today, are always call arguments).
// Consider:
//
// { // block a
// foo( // call b
// x,
// y,
// fn&() {
// // fn body c
// })
// }
//
// Here, the parent of the three argument expressions is
// actually the block `a`, not the call `b`, because they will
// be evaluated before the call conceptually takes place.
// However, the body of the closure is parented by the call
// `b` (it cannot be invoked except during that call, after
// all).
//
// To capture these patterns, we use two fields. The first,
// parent, is the parent scope of a normal expression. The
// second, closure_parent, is the parent scope that a closure body
// ought to use. These only differ in the case of calls, where
// the closure parent is the call, but the parent is the container
// of the call.
parent: parent,
closure_parent: parent
};
// Returns true if `subscope` is equal to or is lexically nested inside
@ -213,18 +252,20 @@ fn nearest_common_ancestor(region_map: @region_map, scope_a: ast::node_id,
// where they diverge. If one vector is a suffix of the other,
// then the corresponding scope is a superscope of the other.
if a_ancestors[a_index] != b_ancestors[b_index] {
ret none;
}
loop {
if a_ancestors[a_index] != b_ancestors[b_index] {
if a_index == a_ancestors.len() {
ret none;
} else {
ret some(a_ancestors[a_index + 1u]);
}
}
// Loop invariant: a_ancestors[a_index] == b_ancestors[b_index]
// for all indices between a_index and the end of the array
if a_index == 0u { ret some(scope_a); }
if b_index == 0u { ret some(scope_b); }
a_index -= 1u;
b_index -= 1u;
if a_ancestors[a_index] != b_ancestors[b_index] {
ret some(a_ancestors[a_index + 1u]);
}
}
}
@ -243,6 +284,7 @@ fn record_parent(cx: ctxt, child_id: ast::node_id) {
alt cx.parent {
none { /* no-op */ }
some(parent_id) {
#debug["parent of node %d is node %d", child_id, parent_id];
cx.region_map.parents.insert(child_id, parent_id);
}
}
@ -253,8 +295,8 @@ fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
record_parent(cx, blk.node.id);
// Descend.
let new_cx: ctxt = {parent: some(blk.node.id)
with cx};
let new_cx: ctxt = {parent: some(blk.node.id),
closure_parent: some(blk.node.id) with cx};
visit::visit_block(blk, new_cx, visitor);
}
@ -286,16 +328,16 @@ fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
record_parent(cx, expr.id);
alt expr.node {
ast::expr_fn(*) | ast::expr_fn_block(*) {
let new_cx = {parent: some(expr.id) with cx};
visit::visit_expr(expr, new_cx, visitor);
}
ast::expr_call(_, _, _) {
let new_cx = {parent: some(expr.id) with cx};
ast::expr_call(*) {
#debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
let new_cx = {closure_parent: some(expr.id) with cx};
visit::visit_expr(expr, new_cx, visitor);
}
ast::expr_alt(subexpr, _, _) {
let new_cx = {parent: some(expr.id) with cx};
#debug["node %d: %s", expr.id, pprust::expr_to_str(expr)];
let new_cx = {parent: some(expr.id),
closure_parent: some(expr.id)
with cx};
visit::visit_expr(expr, new_cx, visitor);
}
_ {
@ -312,20 +354,53 @@ fn resolve_local(local: @ast::local, cx: ctxt, visitor: visit::vt<ctxt>) {
fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
// Items create a new outer block scope as far as we're concerned.
let new_cx: ctxt = {parent: some(item.id) with cx};
let new_cx: ctxt = {closure_parent: some(item.id),
parent: some(item.id) with cx};
visit::visit_item(item, new_cx, visitor);
}
fn resolve_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
sp: span, id: ast::node_id, cx: ctxt,
visitor: visit::vt<ctxt>) {
let fn_cx = alt fk {
visit::fk_item_fn(*) | visit::fk_method(*) | visit::fk_res(*) |
visit::fk_ctor(*) {
// Top-level functions are a root scope.
{parent: some(id), closure_parent: some(id) with cx}
}
visit::fk_anon(*) | visit::fk_fn_block(*) {
// Closures use the closure_parent.
{parent: cx.closure_parent with cx}
}
};
#debug["visiting fn with body %d. cx.parent: %? \
cx.closure_parent: %? fn_cx.parent: %?",
body.node.id, cx.parent,
cx.closure_parent, fn_cx.parent];
for decl.inputs.each { |input|
cx.region_map.local_blocks.insert(
input.id, body.node.id);
}
visit::visit_fn(fk, decl, body, sp, id, fn_cx, visitor);
}
fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
-> @region_map {
let cx: ctxt = {sess: sess,
def_map: def_map,
region_map: @{parents: map::int_hash(),
local_blocks: map::int_hash()},
parent: none};
parent: none,
closure_parent: none};
let visitor = visit::mk_vt(@{
visit_block: resolve_block,
visit_item: resolve_item,
visit_fn: resolve_fn,
visit_arm: resolve_arm,
visit_pat: resolve_pat,
visit_expr: resolve_expr,

View File

@ -18,7 +18,7 @@ fn count_insn(cx: block, category: str) {
if (cx.ccx().sess.opts.count_llvm_insns) {
let h = cx.ccx().stats.llvm_insns;
let mut v = cx.ccx().stats.llvm_insn_ctxt;
let v = cx.ccx().stats.llvm_insn_ctxt;
// Build version of path with cycles removed.

View File

@ -29,6 +29,8 @@ export constr;
export constr_general;
export constr_table;
export ctxt;
export deref, deref_sty;
export index, index_sty;
export def_has_ty_params;
export expr_has_ty_params;
export expr_ty;
@ -126,7 +128,7 @@ export type_is_unique;
export type_is_c_like_enum;
export type_structurally_contains;
export type_structurally_contains_uniques;
export type_autoderef;
export type_autoderef, deref, deref_sty;
export type_param;
export type_needs_unwind_cleanup;
export canon_mode;
@ -228,7 +230,8 @@ type ctxt =
iface_method_cache: hashmap<def_id, @[method]>,
ty_param_bounds: hashmap<ast::node_id, param_bounds>,
inferred_modes: hashmap<ast::node_id, ast::mode>,
borrowings: hashmap<ast::node_id, ()>,
// maps the id of borrowed expr to scope of borrowed ptr
borrowings: hashmap<ast::node_id, ast::node_id>,
normalized_cache: hashmap<t, t>};
enum tbox_flag {
@ -572,6 +575,8 @@ fn mk_float(cx: ctxt) -> t { mk_t(cx, ty_float(ast::ty_f)) }
fn mk_uint(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u)) }
fn mk_u8(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u8)) }
fn mk_mach_int(cx: ctxt, tm: ast::int_ty) -> t { mk_t(cx, ty_int(tm)) }
fn mk_mach_uint(cx: ctxt, tm: ast::uint_ty) -> t { mk_t(cx, ty_uint(tm)) }
@ -1711,25 +1716,63 @@ fn vars_in_type(ty: t) -> [ty_vid] {
rslt
}
// Returns the type and mutability of *t.
//
// The parameter `expl` indicates if this is an *explicit* dereference. Some
// types---notably unsafe ptrs---can only be dereferenced explicitly.
fn deref(cx: ctxt, t: t, expl: bool) -> option<mt> {
deref_sty(cx, get(t).struct, expl)
}
fn deref_sty(cx: ctxt, sty: sty, expl: bool) -> option<mt> {
alt sty {
ty_rptr(_, mt) | ty_box(mt) | ty_uniq(mt) {
some(mt)
}
ty_ptr(mt) if expl {
some(mt)
}
ty_res(_, inner, substs) {
let inner = subst(cx, substs, inner);
some({ty: inner, mutbl: ast::m_imm})
}
ty_enum(did, substs) {
let variants = enum_variants(cx, did);
if vec::len(*variants) == 1u && vec::len(variants[0].args) == 1u {
let v_t = subst(cx, substs, variants[0].args[0]);
some({ty: v_t, mutbl: ast::m_imm})
} else {
none
}
}
_ { none }
}
}
fn type_autoderef(cx: ctxt, t: t) -> t {
let mut t1 = t;
let mut t = t;
loop {
alt get(t1).struct {
ty_box(mt) | ty_uniq(mt) | ty::ty_rptr(_, mt) { t1 = mt.ty; }
ty_res(_, inner, substs) {
t1 = subst(cx, substs, inner);
}
ty_enum(did, substs) {
let variants = enum_variants(cx, did);
if vec::len(*variants) != 1u || vec::len(variants[0].args) != 1u {
break;
}
t1 = subst(cx, substs, variants[0].args[0]);
}
_ { break; }
alt deref(cx, t, false) {
none { ret t; }
some(mt) { t = mt.ty; }
}
}
ret t1;
}
// Returns the type and mutability of t[i]
fn index(cx: ctxt, t: t) -> option<mt> {
index_sty(cx, get(t).struct)
}
fn index_sty(cx: ctxt, sty: sty) -> option<mt> {
alt sty {
ty_vec(mt) | ty_evec(mt, _) { some(mt) }
ty_str | ty_estr(_) { some({ty: mk_u8(cx), mutbl: ast::m_imm}) }
_ { none }
}
}
fn hash_bound_region(br: bound_region) -> uint {

View File

@ -8,7 +8,7 @@ import metadata::csearch;
import driver::session::session;
import util::common::*;
import syntax::codemap::span;
import pat_util::*;
import pat_util::{pat_is_variant, pat_id_map};
import middle::ty;
import middle::ty::{arg, field, node_type_table, mk_nil,
ty_param_bounds_and_ty, lookup_public_fields};
@ -216,9 +216,17 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
})
};
}
ast::def_fn(id, ast::unsafe_fn) {
// Unsafe functions can only be touched in an unsafe context
fcx.require_unsafe(sp, "access to unsafe function");
ret ty::lookup_item_type(fcx.ccx.tcx, id);
}
ast::def_fn(id, _) | ast::def_const(id) |
ast::def_variant(_, id) | ast::def_class(id)
{ ret ty::lookup_item_type(fcx.ccx.tcx, id); }
ast::def_variant(_, id) | ast::def_class(id) {
ret ty::lookup_item_type(fcx.ccx.tcx, id);
}
ast::def_binding(nid) {
assert (fcx.locals.contains_key(nid));
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
@ -1340,6 +1348,29 @@ impl methods for @fn_ctxt {
fn mk_eqty(sub: ty::t, sup: ty::t) -> result<(), ty::type_err> {
infer::mk_eqty(self.infcx, sub, sup)
}
fn require_impure(sp: span) {
alt self.purity {
ast::unsafe_fn { ret; }
ast::impure_fn | ast::crust_fn { ret; }
ast::pure_fn {
self.ccx.tcx.sess.span_err(
sp,
"found impure expression in pure function decl");
}
}
}
fn require_unsafe(sp: span, op: str) {
alt self.purity {
ast::unsafe_fn {/*ok*/}
_ {
self.ccx.tcx.sess.span_err(
sp,
#fmt["%s requires unsafe function or block", op]);
}
}
}
}
fn mk_ty_params(ccx: @crate_ctxt, atps: [ast::ty_param])
@ -1715,13 +1746,14 @@ mod collect {
}
// FIXME This is almost a duplicate of ty::type_autoderef, with structure_of
// instead of ty::struct.
fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
let mut t1 = t;
let mut enum_dids = [];
loop {
alt structure_of(fcx, sp, t1) {
let sty = structure_of(fcx, sp, t1);
// Some extra checks to detect weird cycles and so forth:
alt sty {
ty::ty_box(inner) | ty::ty_uniq(inner) | ty::ty_rptr(_, inner) {
alt ty::get(t1).struct {
ty::ty_var(v1) {
@ -1730,10 +1762,6 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
}
_ { }
}
t1 = inner.ty;
}
ty::ty_res(_, inner, substs) {
t1 = ty::subst(fcx.ccx.tcx, substs, inner);
}
ty::ty_enum(did, substs) {
// Watch out for a type like `enum t = @t`. Such a type would
@ -1745,14 +1773,14 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
ret t1;
}
vec::push(enum_dids, did);
let variants = ty::enum_variants(fcx.ccx.tcx, did);
if vec::len(*variants) != 1u || vec::len(variants[0].args) != 1u {
ret t1;
}
t1 = ty::subst(fcx.ccx.tcx, substs, variants[0].args[0]);
}
_ { ret t1; }
_ { /*ok*/ }
}
// Otherwise, deref if type is derefable:
alt ty::deref_sty(fcx.ccx.tcx, sty, false) {
none { ret t1; }
some(mt) { t1 = mt.ty; }
}
};
}
@ -1812,9 +1840,11 @@ mod demand {
}
// Checks that the type `actual` can be assigned to `expected`.
fn assign(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) {
fn assign(fcx: @fn_ctxt, sp: span, borrow_scope: ast::node_id,
expected: ty::t, expr: @ast::expr) {
let expr_ty = fcx.expr_ty(expr);
alt infer::mk_assignty(fcx.infcx, expr.id, expr_ty, expected) {
let anmnt = {expr_id: expr.id, borrow_scope: borrow_scope};
alt infer::mk_assignty(fcx.infcx, anmnt, expr_ty, expected) {
result::ok(()) { /* ok */ }
result::err(err) {
fcx.report_mismatched_types(sp, expected, expr_ty, err);
@ -2093,7 +2123,7 @@ fn valid_range_bounds(ccx: @crate_ctxt, from: @ast::expr, to: @ast::expr)
type pat_ctxt = {
fcx: @fn_ctxt,
map: pat_util::pat_id_map,
map: pat_id_map,
alt_region: ty::region,
block_region: ty::region,
/* Equal to either alt_region or block_region. */
@ -2265,12 +2295,11 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
}
fcx.write_ty(pat.id, b_ty);
}
ast::pat_ident(name, sub)
if !pat_util::pat_is_variant(tcx.def_map, pat) {
ast::pat_ident(name, sub) if !pat_is_variant(tcx.def_map, pat) {
let vid = lookup_local(pcx.fcx, pat.span, pat.id);
let mut typ = ty::mk_var(tcx, vid);
demand::suptype(pcx.fcx, pat.span, expected, typ);
let canon_id = pcx.map.get(path_to_ident(name));
let canon_id = pcx.map.get(pat_util::path_to_ident(name));
if canon_id != pat.id {
let tv_id = lookup_local(pcx.fcx, pat.span, canon_id);
let ct = ty::mk_var(tcx, tv_id);
@ -2383,27 +2412,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
}
}
fn require_unsafe(sess: session, f_purity: ast::purity, sp: span) {
alt f_purity {
ast::unsafe_fn { ret; }
_ {
sess.span_err(
sp,
"unsafe operation requires unsafe function or block");
}
}
}
fn require_impure(sess: session, f_purity: ast::purity, sp: span) {
alt f_purity {
ast::unsafe_fn { ret; }
ast::impure_fn | ast::crust_fn { ret; }
ast::pure_fn {
sess.span_err(sp, "found impure expression in pure function decl");
}
}
}
fn require_pure_call(ccx: @crate_ctxt, caller_purity: ast::purity,
callee: @ast::expr, sp: span) {
if caller_purity == ast::unsafe_fn { ret; }
@ -2866,9 +2874,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// A generic function to factor out common logic from call and bind
// expressions.
fn check_call_or_bind(
fcx: @fn_ctxt, sp: span, fty: ty::t,
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, fty: ty::t,
args: [option<@ast::expr>]) -> {fty: ty::t, bot: bool} {
let mut bot = false;
let fty = universally_quantify_before_call(fcx, sp, fty);
#debug["check_call_or_bind: after universal quant., fty=%s",
fcx.ty_to_str(fty)];
@ -2916,10 +2926,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// functions. This is so that we have more information about the types
// of arguments when we typecheck the functions. This isn't really the
// right way to do this.
let check_args = fn@(check_blocks: bool) -> bool {
let mut i = 0u;
let mut bot = false;
for args.each {|a_opt|
for [false, true].each { |check_blocks|
for args.eachi {|i, a_opt|
alt a_opt {
some(a) {
let is_block = alt a.node {
@ -2930,18 +2938,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let arg_ty = arg_tys[i];
bot |= check_expr_with_unifier(
fcx, a, some(arg_ty)) {||
demand::assign(fcx, a.span, arg_ty, a);
demand::assign(fcx, a.span, call_expr_id,
arg_ty, a);
};
}
}
none { }
}
i += 1u;
}
ret bot;
};
let bot = check_args(false) | check_args(true);
}
{fty: fty, bot: bot}
}
@ -2965,7 +2970,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Call the generic checker.
let fty = {
let args_opt = args.map { |arg| some(arg) };
let r = check_call_or_bind(fcx, sp, fn_ty, args_opt);
let r = check_call_or_bind(fcx, sp, call_expr_id,
fn_ty, args_opt);
bot |= r.bot;
r.fty
};
@ -3046,7 +3052,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
some(origin) {
let {fty: method_ty, bot: bot} = {
let method_ty = fcx.node_ty(callee_id);
check_call_or_bind(fcx, op_ex.span, method_ty, args)
check_call_or_bind(fcx, op_ex.span, op_ex.id,
method_ty, args)
};
fcx.ccx.method_map.insert(op_ex.id, origin);
some((ty::ty_fn_ret(method_ty), bot))
@ -3257,7 +3264,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
bot |= check_binop(fcx, expr, op, lhs, rhs);
}
ast::expr_assign_op(op, lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
fcx.require_impure(expr.span);
bot |= check_binop(fcx, expr, op, lhs, rhs);
let lhs_t = fcx.expr_ty(lhs);
let result_t = fcx.expr_ty(expr);
@ -3291,30 +3298,37 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
oper_t = ty::mk_uniq(tcx, {ty: oper_t, mutbl: mutbl});
}
ast::deref {
alt structure_of(fcx, expr.span, oper_t) {
ty::ty_box(inner) { oper_t = inner.ty; }
ty::ty_uniq(inner) { oper_t = inner.ty; }
ty::ty_res(_, inner, _) { oper_t = inner; }
ty::ty_enum(id, substs) {
let variants = ty::enum_variants(tcx, id);
if vec::len(*variants) != 1u ||
vec::len(variants[0].args) != 1u {
tcx.sess.span_fatal(expr.span,
"can only dereference enums " +
"with a single variant which has a "
+ "single argument");
let sty = structure_of(fcx, expr.span, oper_t);
// deref'ing an unsafe pointer requires that we be in an unsafe
// context
alt sty {
ty::ty_ptr(*) {
fcx.require_unsafe(
expr.span,
"dereference of unsafe pointer");
}
_ { /*ok*/ }
}
alt ty::deref_sty(tcx, sty, true) {
some(mt) { oper_t = mt.ty }
none {
alt sty {
ty::ty_enum(*) {
tcx.sess.span_fatal(
expr.span,
"can only dereference enums \
with a single variant which has a \
single argument");
}
_ {
tcx.sess.span_fatal(
expr.span,
#fmt["type %s cannot be dereferenced",
fcx.ty_to_str(oper_t)]);
}
}
oper_t = ty::subst(tcx, substs, variants[0].args[0]);
}
ty::ty_ptr(inner) {
oper_t = inner.ty;
require_unsafe(tcx.sess, fcx.purity, expr.span);
}
ty::ty_rptr(_, inner) { oper_t = inner.ty; }
_ {
tcx.sess.span_err(expr.span,
#fmt("Type %s cannot be dereferenced",
ty_to_str(tcx, oper_t)));
}
}
}
@ -3410,21 +3424,20 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
fcx.write_ty(id, fcx.expr_ty(a));
}
ast::expr_move(lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
fcx.require_impure(expr.span);
bot = check_assignment(fcx, expr.span, lhs, rhs, id);
}
ast::expr_assign(lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
fcx.require_impure(expr.span);
bot = check_assignment(fcx, expr.span, lhs, rhs, id);
}
ast::expr_swap(lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
fcx.require_impure(expr.span);
bot = check_assignment(fcx, expr.span, lhs, rhs, id);
}
ast::expr_if(cond, thn, elsopt) {
bot =
check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
check_then_else(fcx, thn, elsopt, id, expr.span);
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
check_then_else(fcx, thn, elsopt, id, expr.span);
}
ast::expr_while(cond, body) {
bot = check_expr_with(fcx, cond, ty::mk_bool(tcx));
@ -3451,7 +3464,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
for arms.each {|arm|
let pcx = {
fcx: fcx,
map: pat_util::pat_id_map(tcx.def_map, arm.pats[0]),
map: pat_id_map(tcx.def_map, arm.pats[0]),
alt_region: ty::re_scope(expr.id),
block_region: ty::re_scope(arm.body.node.id),
pat_region: ty::re_scope(expr.id)
@ -3545,7 +3558,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let {fty, bot: ccob_bot} = {
let fn_ty = fcx.expr_ty(f);
check_call_or_bind(fcx, expr.span, fn_ty, args)
check_call_or_bind(fcx, expr.span, expr.id, fn_ty, args)
};
bot |= ccob_bot;
@ -3792,19 +3805,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let base_t = do_autoderef(fcx, expr.span, raw_base_t);
bot |= check_expr(fcx, idx, none);
let idx_t = fcx.expr_ty(idx);
alt structure_of(fcx, expr.span, base_t) {
ty::ty_evec(mt, _) |
ty::ty_vec(mt) {
alt ty::index_sty(tcx, structure_of(fcx, expr.span, base_t)) {
some(mt) {
require_integral(fcx, idx.span, idx_t);
fcx.write_ty(id, mt.ty);
}
ty::ty_estr(_) |
ty::ty_str {
require_integral(fcx, idx.span, idx_t);
let typ = ty::mk_mach_uint(tcx, ast::ty_u8);
fcx.write_ty(id, typ);
}
_ {
none {
let resolved = structurally_resolved_type(fcx, expr.span,
raw_base_t);
alt lookup_op_method(fcx, expr, resolved, "[]",
@ -3919,7 +3925,7 @@ fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
fcx.ccx.tcx.region_map.local_blocks.get(local.node.id));
let pcx = {
fcx: fcx,
map: pat_util::pat_id_map(fcx.ccx.tcx.def_map, local.node.pat),
map: pat_id_map(fcx.ccx.tcx.def_map, local.node.pat),
alt_region: region,
block_region: region,
pat_region: region

View File

@ -39,12 +39,12 @@ mod middle {
mod ast_map;
mod resolve;
mod typeck;
mod fn_usage;
mod check_loop;
mod check_alt;
mod check_const;
mod lint;
mod mutbl;
mod borrowck;
mod alias;
mod last_use;
mod block_use;

View File

@ -0,0 +1,17 @@
// compile-flags:--borrowck=err
fn borrow(v: &int, f: fn(x: &int)) {
f(v);
}
fn box_imm() {
let mut v = ~3;
borrow(v) { |w| //! NOTE loan of mutable local variable granted here
v = ~4; //! ERROR cannot assign to mutable local variable due to outstanding loan
assert *v == 3;
assert *w == 4;
}
}
fn main() {
}

View File

@ -0,0 +1,26 @@
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn borrow_from_arg_imm_ref(&&v: ~int) {
borrow(v); // ERROR unique value in aliasable, mutable location
}
fn borrow_from_arg_mut_ref(&v: ~int) {
borrow(v); //! ERROR unique value in aliasable, mutable location
}
fn borrow_from_arg_move(-v: ~int) {
borrow(v);
}
fn borrow_from_arg_copy(+v: ~int) {
borrow(v);
}
fn borrow_from_arg_val(++v: ~int) {
borrow(v);
}
fn main() {
}

View File

@ -0,0 +1,96 @@
// 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
// either genuine or would require more advanced changes. The latter
// cases are noted.
fn borrow(_v: &int) {}
fn inc(v: &mut ~int) {
*v = ~(**v + 1);
}
fn post_aliased_const() {
let mut v = ~3;
borrow(v);
let _w = &const v;
}
fn post_aliased_mut() {
// SPURIOUS--flow
let mut v = ~3;
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
let _w = &mut v; //! NOTE prior loan as mutable granted here
}
fn post_aliased_scope(cond: bool) {
// NDM--scope of &
let mut v = ~3;
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
if cond { inc(&mut v); } //! NOTE prior loan as mutable granted here
}
fn loop_aliased_mut() {
let mut v = ~3, w = ~4;
let mut _x = &mut w;
loop {
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
_x = &mut v; //! NOTE prior loan as mutable granted here
}
}
fn while_aliased_mut(cond: bool) {
let mut v = ~3, w = ~4;
let mut _x = &mut w;
while cond {
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
_x = &mut v; //! NOTE prior loan as mutable granted here
}
}
fn while_aliased_mut_cond(cond: bool, cond2: bool) {
let mut v = ~3, w = ~4;
let mut _x = &mut w;
while cond {
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
if cond2 {
_x = &mut v; //! NOTE prior loan as mutable granted here
}
}
}
fn do_while_aliased_mut(cond: bool) {
let mut v = ~3, w = ~4;
let mut _x = &mut w;
do {
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
_x = &mut v; //! NOTE prior loan as mutable granted here
} while cond;
}
fn loop_in_block() {
let mut v = ~3, w = ~4;
let mut _x = &mut w;
uint::range(0u, 10u) {|_i|
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
_x = &mut v; //! NOTE prior loan as mutable granted here
}
}
fn at_most_once_block() {
fn at_most_once(f: fn()) { f() }
// Here, the borrow check has no way of knowing that the block is
// executed at most once.
let mut v = ~3, w = ~4;
let mut _x = &mut w;
at_most_once {||
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
_x = &mut v; //! NOTE prior loan as mutable granted here
}
}
fn main() {}

View File

@ -0,0 +1,21 @@
// compile-flags:--borrowck=err
fn match_imm_box(v: &const @option<int>) -> int {
alt *v {
@some(i) {i}
@none {0}
}
}
fn match_const_box(v: &const @const option<int>) -> int {
alt *v {
@some(i) {
//!^ ERROR enum variant in aliasable, mutable location
i
}
@none {0}
}
}
fn main() {
}

View File

@ -0,0 +1,45 @@
// compile-flags:--borrowck=err
fn match_ref(&&v: option<int>) -> int {
alt v {
some(i) {
//^ ERROR enum variant in aliasable, mutable location
i
}
none {0}
}
}
fn match_ref_unused(&&v: option<int>) {
alt v {
some(_) {}
none {}
}
}
fn match_const_reg(v: &const option<int>) -> int {
alt *v {
some(i) {
//!^ ERROR enum variant in aliasable, mutable location
i
}
none {0}
}
}
fn match_const_reg_unused(v: &const option<int>) {
alt *v {
some(_) {}
none {}
}
}
fn match_imm_reg(v: &option<int>) -> int {
alt *v {
some(i) {i}
none {0}
}
}
fn main() {
}

View File

@ -0,0 +1,14 @@
// compile-flags:--borrowck=err
// xfail-pretty -- comments are infaithfully preserved
fn main() {
let mut x: option<int> = none;
alt x { //! NOTE loan of mutable local variable granted here
none {}
some(i) {
// Not ok: i is an outstanding ptr into x.
x = some(i+1);
//!^ ERROR cannot assign to mutable local variable due to outstanding loan
}
}
}

View File

@ -0,0 +1,16 @@
// compile-flags:--borrowck=err
// xfail-pretty -- comments are infaithfully preserved
fn main() {
let mut x = none;
alt x { //! NOTE loan of mutable local variable granted here
none {
// It is ok to reassign x here, because there is in
// fact no outstanding loan of x!
x = some(0);
}
some(i) {
x = some(1); //! ERROR cannot assign to mutable local variable due to outstanding loan
}
}
}

View File

@ -0,0 +1,55 @@
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn box_mut(v: @mut ~int) {
borrow(*v); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_rec_mut(v: @{mut f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_mut_rec(v: @mut {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_mut_recs(v: @mut {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_imm(v: @~int) {
borrow(*v); // OK
}
fn box_imm_rec(v: @{f: ~int}) {
borrow(v.f); // OK
}
fn box_imm_recs(v: @{f: {g: {h: ~int}}}) {
borrow(v.f.g.h); // OK
}
fn box_const(v: @const ~int) {
borrow(*v); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_rec_const(v: @{const f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_recs_const(v: @{f: {g: {const h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_const_rec(v: @const {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_const_recs(v: @const {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn main() {
}

View File

@ -0,0 +1,52 @@
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn local() {
let mut v = ~3;
borrow(v);
}
fn local_rec() {
let mut v = {f: ~3};
borrow(v.f);
}
fn local_recs() {
let mut v = {f: {g: {h: ~3}}};
borrow(v.f.g.h);
}
fn aliased_imm() { // NDM: Spurious
let mut v = ~3;
let _w = &v; //! ERROR illegal borrow: mutability mismatch, required immutable but found mutable
borrow(v);
}
fn aliased_const() {
let mut v = ~3;
let _w = &const v;
borrow(v);
}
fn aliased_mut() {
let mut v = ~3;
let _w = &mut v; //! NOTE prior loan as mutable granted here
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
}
fn aliased_other() {
let mut v = ~3, w = ~4;
let _x = &mut w;
borrow(v);
}
fn aliased_other_reassign() {
let mut v = ~3, w = ~4;
let mut _x = &mut w;
_x = &mut v; //! NOTE prior loan as mutable granted here
borrow(v); //! ERROR loan of mutable local variable as immutable conflicts with prior loan
}
fn main() {
}

View File

@ -0,0 +1,54 @@
// compile-flags:--borrowck=err
fn borrow(_v: &int) {}
fn box_mut(v: &mut ~int) {
borrow(*v); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_rec_mut(v: &{mut f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_mut_rec(v: &mut {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_mut_recs(v: &mut {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_imm(v: &~int) {
borrow(*v); // OK
}
fn box_imm_rec(v: &{f: ~int}) {
borrow(v.f); // OK
}
fn box_imm_recs(v: &{f: {g: {h: ~int}}}) {
borrow(v.f.g.h); // OK
}
fn box_const(v: &const ~int) {
borrow(*v); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_rec_const(v: &{const f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_recs_const(v: &{f: {g: {const h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_const_rec(v: &const {f: ~int}) {
borrow(v.f); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn box_const_recs(v: &const {f: {g: {h: ~int}}}) {
borrow(v.f.g.h); //! ERROR illegal borrow: unique value in aliasable, mutable location
}
fn main() {
}

View File

@ -1,10 +0,0 @@
// error-pattern: generic bare functions can only be called or bound
// Issue #1038
fn main() {
fn foo<T>() { }
// This wants to build a closure over type int,
// but there's no way to do that while still being a bare function
let f: fn() = foo::<int>;
}

View File

@ -1,13 +0,0 @@
// error-pattern: generic bare functions can only be called or bound
// Issue #1038
fn main() {
fn foo<T>(i: T) { }
// This wants to build a closure over type int,
// but there's no way to do that while still being a bare function
f(foo);
}
fn f(i: fn(&&int)) {
}

View File

@ -1,5 +1,4 @@
// -*- rust -*-
// error-pattern: unsafe functions can only be called
#[abi = "cdecl"]
native mod test {
@ -8,6 +7,7 @@ native mod test {
fn main() {
let x = test::free;
//!^ ERROR access to unsafe function requires unsafe function or block
}

View File

@ -2,10 +2,10 @@
// This generates a ton of error msgs at the moment.
fn broken() -> int {
let mut x = 3;
let mut y = [&x]; //! ERROR reference is not valid
let mut y = [&mut x]; //! ERROR reference is not valid
while x < 10 {
let z = x;
y += [&z];
let mut z = x;
y += [&mut z];
x += 1;
}
vec::foldl(0, y) {|v, p| v + *p }

View File

@ -1,11 +1,9 @@
// -*- rust -*-
// error-pattern: unsafe operation requires unsafe function or block
fn f(p: *u8) {
*p = 0u8;
*p = 0u8; //! ERROR dereference of unsafe pointer requires unsafe function or block
ret;
}
fn main() {
f();
}

View File

@ -0,0 +1,22 @@
// -*- rust -*-
type rec = {f: int};
fn f(p: *rec) -> int {
// Test that * ptrs do not autoderef. There is a deeper reason for
// prohibiting this, beyond making unsafe things annoying (which doesn't
// actually seem desirable to me). The deeper reason is that if you
// have a type like:
//
// enum foo = *foo;
//
// you end up with an infinite auto-deref chain, which is
// currently impossible (in all other cases, infinite auto-derefs
// are prohibited by various checks, such as that the enum is
// instantiable and so forth).
ret p.f; //! ERROR attempted access of field f on type *rec
}
fn main() {
}

View File

@ -1,10 +1,8 @@
// -*- rust -*-
// error-pattern: unsafe operation requires unsafe function or block
fn f(p: *u8) -> u8 {
ret *p;
ret *p; //! ERROR dereference of unsafe pointer requires unsafe function or block
}
fn main() {
f();
}

View File

@ -1,9 +1,8 @@
// -*- rust -*-
// error-pattern: unsafe functions can only be called
unsafe fn f() { ret; }
fn main() {
let x = f;
let x = f; //! ERROR access to unsafe function requires unsafe function or block
x();
}

View File

@ -1,9 +1,9 @@
// -*- rust -*-
// error-pattern: unsafe functions can only be called
unsafe fn f(x: int, y: int) -> int { ret x + y; }
fn main() {
let x = bind f(3, _);
//!^ ERROR access to unsafe function requires unsafe function or block
let y = x(4);
}

View File

@ -0,0 +1,13 @@
// compile-flags:--borrowck=err
fn main() {
let mut x = none;
alt x {
none {
// It is ok to reassign x here, because there is in
// fact no outstanding loan of x!
x = some(0);
}
some(_) { }
}
}