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

@@ -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;