Move mutability checking into its own pass.
Having it in the alias pass was slightly more efficient (finding expression roots has to be done in both passes), but further muddled up the already complex alias checker. Also factors out some duplication in the mutability-checking code.
This commit is contained in:
parent
34ae491ca9
commit
2d1dec78e7
13 changed files with 292 additions and 290 deletions
|
|
@ -5,6 +5,7 @@ import ast::ident;
|
|||
import ast::fn_ident;
|
||||
import ast::node_id;
|
||||
import ast::def_id;
|
||||
import mut::{expr_root, mut_field, inner_mut};
|
||||
import syntax::codemap::span;
|
||||
import syntax::visit;
|
||||
import visit::vt;
|
||||
|
|
@ -37,40 +38,30 @@ type restrict =
|
|||
type scope = @[restrict];
|
||||
|
||||
tag local_info {
|
||||
arg(ast::mode);
|
||||
objfield(ast::mutability);
|
||||
local(uint);
|
||||
}
|
||||
|
||||
type mut_map = std::map::hashmap<node_id, ()>;
|
||||
type ctx = {tcx: ty::ctxt,
|
||||
local_map: std::map::hashmap<node_id, local_info>,
|
||||
mutable next_local: uint,
|
||||
mut_map: mut_map};
|
||||
mutable next_local: uint};
|
||||
|
||||
fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) -> mut_map {
|
||||
fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) {
|
||||
// Stores information about object fields and function
|
||||
// arguments that's otherwise not easily available.
|
||||
let cx = @{tcx: tcx,
|
||||
local_map: std::map::new_int_hash(),
|
||||
mutable next_local: 0u,
|
||||
mut_map: std::map::new_int_hash()};
|
||||
mutable next_local: 0u};
|
||||
let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _),
|
||||
visit_item: bind visit_item(cx, _, _, _),
|
||||
visit_expr: bind visit_expr(cx, _, _, _),
|
||||
visit_decl: bind visit_decl(cx, _, _, _)
|
||||
with *visit::default_visitor::<scope>()};
|
||||
visit::visit_crate(*crate, @[], visit::mk_vt(v));
|
||||
tcx.sess.abort_if_errors();
|
||||
ret cx.mut_map;
|
||||
}
|
||||
|
||||
fn visit_fn(cx: &@ctx, f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span,
|
||||
_name: &fn_ident, id: ast::node_id, sc: &scope, v: &vt<scope>) {
|
||||
visit::visit_fn_decl(f.decl, sc, v);
|
||||
for arg_: ast::arg in f.decl.inputs {
|
||||
cx.local_map.insert(arg_.id, arg(arg_.mode));
|
||||
}
|
||||
let scope =
|
||||
alt f.proto {
|
||||
|
||||
|
|
@ -104,18 +95,6 @@ fn visit_fn(cx: &@ctx, f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span,
|
|||
v.visit_block(f.body, scope, v);
|
||||
}
|
||||
|
||||
fn visit_item(cx: &@ctx, i: &@ast::item, sc: &scope, v: &vt<scope>) {
|
||||
alt i.node {
|
||||
ast::item_obj(o, _, _) {
|
||||
for f: ast::obj_field in o.fields {
|
||||
cx.local_map.insert(f.id, objfield(f.mut));
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
visit::visit_item(i, sc, v);
|
||||
}
|
||||
|
||||
fn visit_expr(cx: &@ctx, ex: &@ast::expr, sc: &scope, v: &vt<scope>) {
|
||||
let handled = true;
|
||||
alt ex.node {
|
||||
|
|
@ -123,15 +102,11 @@ fn visit_expr(cx: &@ctx, ex: &@ast::expr, sc: &scope, v: &vt<scope>) {
|
|||
check_call(*cx, f, args, sc);
|
||||
handled = false;
|
||||
}
|
||||
ast::expr_be(cl) {
|
||||
check_tail_call(*cx, cl);
|
||||
visit::visit_expr(cl, sc, v);
|
||||
}
|
||||
ast::expr_alt(input, arms) { check_alt(*cx, input, arms, sc, v); }
|
||||
ast::expr_put(val) {
|
||||
alt val {
|
||||
some(ex) {
|
||||
let root = expr_root(*cx, ex, false);
|
||||
let root = expr_root(cx.tcx, ex, false);
|
||||
if mut_field(root.ds) {
|
||||
cx.tcx.sess.span_err(ex.span,
|
||||
~"result of put must be" +
|
||||
|
|
@ -157,7 +132,7 @@ fn visit_expr(cx: &@ctx, ex: &@ast::expr, sc: &scope, v: &vt<scope>) {
|
|||
}
|
||||
ast::expr_move(dest, src) {
|
||||
check_assign(cx, dest, src, sc, v);
|
||||
check_move_rhs(cx, src, sc, v);
|
||||
check_lval(cx, src, sc, v);
|
||||
}
|
||||
ast::expr_assign(dest, src) | ast::expr_assign_op(_, dest, src) {
|
||||
check_assign(cx, dest, src, sc, v);
|
||||
|
|
@ -182,7 +157,7 @@ fn visit_decl(cx: &@ctx, d: &@ast::decl, sc: &scope, v: &vt<scope>) {
|
|||
alt loc.node.init {
|
||||
some(init) {
|
||||
if init.op == ast::init_move {
|
||||
check_move_rhs(cx, init.expr, sc, v);
|
||||
check_lval(cx, init.expr, sc, v);
|
||||
}
|
||||
}
|
||||
none. { }
|
||||
|
|
@ -196,47 +171,22 @@ fn visit_decl(cx: &@ctx, d: &@ast::decl, sc: &scope, v: &vt<scope>) {
|
|||
|
||||
fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope)
|
||||
-> [restrict] {
|
||||
let fty = ty::expr_ty(cx.tcx, f);
|
||||
let arg_ts = fty_args(cx, fty);
|
||||
let fty = ty::type_autoderef(cx.tcx, ty::expr_ty(cx.tcx, f));
|
||||
let arg_ts = ty::ty_fn_args(cx.tcx, fty);
|
||||
let mut_roots: [{arg: uint, node: node_id}] = [];
|
||||
let restricts = [];
|
||||
let i = 0u;
|
||||
for arg_t: ty::arg in arg_ts {
|
||||
if arg_t.mode != ty::mo_val {
|
||||
let arg = args[i];
|
||||
let root = expr_root(cx, arg, false);
|
||||
let root = expr_root(cx.tcx, arg, false);
|
||||
if arg_t.mode == ty::mo_alias(true) {
|
||||
alt path_def(cx, arg) {
|
||||
some(def) {
|
||||
let dnum = ast_util::def_id_of_def(def).node;
|
||||
if def_is_local(def, true) {
|
||||
if is_immutable_alias(cx, sc, dnum) {
|
||||
cx.tcx.sess.span_err(
|
||||
arg.span,
|
||||
~"passing an immutable alias \
|
||||
by mutable alias");
|
||||
} else if is_immutable_objfield(cx, dnum) {
|
||||
cx.tcx.sess.span_err(
|
||||
arg.span,
|
||||
~"passing an immutable object \
|
||||
field by mutable alias");
|
||||
}
|
||||
cx.mut_map.insert(dnum, ());
|
||||
} else {
|
||||
cx.tcx.sess.span_err(
|
||||
arg.span,
|
||||
~"passing a static item by mutable alias");
|
||||
}
|
||||
mut_roots += [{arg: i, node: dnum}];
|
||||
}
|
||||
_ {
|
||||
if !mut_field(root.ds) {
|
||||
let m =
|
||||
~"passing a temporary value or \
|
||||
immutable field by mutable alias";
|
||||
cx.tcx.sess.span_err(arg.span, m);
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
let root_var = path_def_id(cx, root.ex);
|
||||
|
|
@ -318,45 +268,10 @@ fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope)
|
|||
ret restricts;
|
||||
}
|
||||
|
||||
fn check_tail_call(cx: &ctx, call: &@ast::expr) {
|
||||
let args;
|
||||
let f = alt call.node { ast::expr_call(f, args_) { args = args_; f } };
|
||||
let i = 0u;
|
||||
for arg_t: ty::arg in fty_args(cx, ty::expr_ty(cx.tcx, f)) {
|
||||
if arg_t.mode != ty::mo_val {
|
||||
let mut_a = arg_t.mode == ty::mo_alias(true);
|
||||
let ok = true;
|
||||
alt args[i].node {
|
||||
ast::expr_path(_) {
|
||||
let def = cx.tcx.def_map.get(args[i].id);
|
||||
let dnum = ast_util::def_id_of_def(def).node;
|
||||
alt cx.local_map.find(dnum) {
|
||||
some(arg(ast::alias(mut))) {
|
||||
if mut_a && !mut {
|
||||
cx.tcx.sess.span_err(args[i].span,
|
||||
~"passing an immutable \
|
||||
alias by mutable alias");
|
||||
}
|
||||
}
|
||||
_ { ok = !def_is_local(def, false); }
|
||||
}
|
||||
}
|
||||
_ { ok = false; }
|
||||
}
|
||||
if !ok {
|
||||
cx.tcx.sess.span_err(args[i].span,
|
||||
~"can not pass a local value by \
|
||||
alias to a tail call");
|
||||
}
|
||||
}
|
||||
i += 1u;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_alt(cx: &ctx, input: &@ast::expr, arms: &[ast::arm], sc: &scope,
|
||||
v: &vt<scope>) {
|
||||
v.visit_expr(input, sc, v);
|
||||
let root = expr_root(cx, input, true);
|
||||
let root = expr_root(cx.tcx, input, true);
|
||||
for a: ast::arm in arms {
|
||||
let dnums = ast_util::pat_binding_ids(a.pats[0]);
|
||||
let new_sc = sc;
|
||||
|
|
@ -389,7 +304,7 @@ fn check_for_each(cx: &ctx, local: &@ast::local, call: &@ast::expr,
|
|||
fn check_for(cx: &ctx, local: &@ast::local, seq: &@ast::expr, blk: &ast::blk,
|
||||
sc: &scope, v: &vt<scope>) {
|
||||
v.visit_expr(seq, sc, v);
|
||||
let root = expr_root(cx, seq, false);
|
||||
let root = expr_root(cx.tcx, seq, false);
|
||||
let unsafe = inner_mut(root.ds);
|
||||
|
||||
// If this is a mutable vector, don't allow it to be touched.
|
||||
|
|
@ -444,59 +359,15 @@ fn check_var(cx: &ctx, ex: &@ast::expr, p: &ast::path, id: ast::node_id,
|
|||
fn check_lval(cx: &@ctx, dest: &@ast::expr, sc: &scope, v: &vt<scope>) {
|
||||
alt dest.node {
|
||||
ast::expr_path(p) {
|
||||
let dnum = ast_util::def_id_of_def(cx.tcx.def_map.get(dest.id)).node;
|
||||
cx.mut_map.insert(dnum, ());
|
||||
if is_immutable_alias(*cx, sc, dnum) {
|
||||
cx.tcx.sess.span_err(dest.span, ~"assigning to immutable alias");
|
||||
} else if is_immutable_objfield(*cx, dnum) {
|
||||
cx.tcx.sess.span_err(dest.span,
|
||||
~"assigning to immutable obj field");
|
||||
}
|
||||
let def = cx.tcx.def_map.get(dest.id);
|
||||
let dnum = ast_util::def_id_of_def(def).node;
|
||||
for r: restrict in *sc {
|
||||
if r.root_var == some(dnum) {
|
||||
r.ok = overwritten(dest.span, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ {
|
||||
let root = expr_root(*cx, dest, false);
|
||||
if vec::len(*root.ds) == 0u {
|
||||
cx.tcx.sess.span_err(dest.span, ~"assignment to non-lvalue");
|
||||
} else if !root.ds[0].mut {
|
||||
let name =
|
||||
alt root.ds[0].kind {
|
||||
unbox. { ~"box" }
|
||||
field. { ~"field" }
|
||||
index. { ~"vec content" }
|
||||
};
|
||||
cx.tcx.sess.span_err(dest.span,
|
||||
~"assignment to immutable " + name);
|
||||
}
|
||||
visit_expr(cx, dest, sc, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_move_rhs(cx: &@ctx, src: &@ast::expr, sc: &scope, v: &vt<scope>) {
|
||||
alt src.node {
|
||||
ast::expr_path(p) {
|
||||
alt cx.tcx.def_map.get(src.id) {
|
||||
ast::def_obj_field(_, _) {
|
||||
cx.tcx.sess.span_err(src.span,
|
||||
~"may not move out of an obj field");
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
check_lval(cx, src, sc, v);
|
||||
}
|
||||
_ {
|
||||
let root = expr_root(*cx, src, false);
|
||||
|
||||
// Not a path and no-derefs means this is a temporary.
|
||||
if vec::len(*root.ds) != 0u {
|
||||
cx.tcx.sess.span_err(src.span, ~"moving out of a data structure");
|
||||
}
|
||||
}
|
||||
_ { visit_expr(cx, dest, sc, v); }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -506,20 +377,6 @@ fn check_assign(cx: &@ctx, dest: &@ast::expr, src: &@ast::expr, sc: &scope,
|
|||
check_lval(cx, dest, sc, v);
|
||||
}
|
||||
|
||||
|
||||
fn is_immutable_alias(cx: &ctx, sc: &scope, dnum: node_id) -> bool {
|
||||
alt cx.local_map.find(dnum) {
|
||||
some(arg(ast::alias(false))) { ret true; }
|
||||
_ { }
|
||||
}
|
||||
for r: restrict in *sc { if vec::member(dnum, r.bindings) { ret true; } }
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn is_immutable_objfield(cx: &ctx, dnum: node_id) -> bool {
|
||||
ret cx.local_map.find(dnum) == some(objfield(ast::imm));
|
||||
}
|
||||
|
||||
fn test_scope(cx: &ctx, sc: &scope, r: &restrict, p: &ast::path) {
|
||||
let prob = r.ok;
|
||||
for dep: uint in r.depends_on {
|
||||
|
|
@ -561,117 +418,6 @@ fn deps(sc: &scope, root: &option::t<node_id>) -> [uint] {
|
|||
ret result;
|
||||
}
|
||||
|
||||
tag deref_t { unbox; field; index; }
|
||||
|
||||
type deref = @{mut: bool, kind: deref_t, outer_t: ty::t};
|
||||
|
||||
|
||||
// Finds the root (the thing that is dereferenced) for the given expr, and a
|
||||
// vec of dereferences that were used on this root. Note that, in this vec,
|
||||
// the inner derefs come in front, so foo.bar.baz becomes rec(ex=foo,
|
||||
// ds=[field(baz),field(bar)])
|
||||
fn expr_root(cx: &ctx, ex: @ast::expr, autoderef: bool) ->
|
||||
{ex: @ast::expr, ds: @[deref]} {
|
||||
fn maybe_auto_unbox(cx: &ctx, t: ty::t) -> {t: ty::t, ds: [deref]} {
|
||||
let ds = [];
|
||||
while true {
|
||||
alt ty::struct(cx.tcx, t) {
|
||||
ty::ty_box(mt) {
|
||||
ds += [@{mut: mt.mut != ast::imm, kind: unbox, outer_t: t}];
|
||||
t = mt.ty;
|
||||
}
|
||||
ty::ty_uniq(mt) {
|
||||
ds += [@{mut: false, kind: unbox, outer_t: t}];
|
||||
}
|
||||
ty::ty_res(_, inner, tps) {
|
||||
ds += [@{mut: false, kind: unbox, outer_t: t}];
|
||||
t = ty::substitute_type_params(cx.tcx, tps, inner);
|
||||
}
|
||||
ty::ty_tag(did, tps) {
|
||||
let variants = ty::tag_variants(cx.tcx, did);
|
||||
if vec::len(variants) != 1u ||
|
||||
vec::len(variants[0].args) != 1u {
|
||||
break;
|
||||
}
|
||||
ds += [@{mut: false, kind: unbox, outer_t: t}];
|
||||
t =
|
||||
ty::substitute_type_params(cx.tcx, tps,
|
||||
variants[0].args[0]);
|
||||
}
|
||||
_ { break; }
|
||||
}
|
||||
}
|
||||
ret {t: t, ds: ds};
|
||||
}
|
||||
let ds: [deref] = [];
|
||||
while true {
|
||||
alt { ex.node } {
|
||||
ast::expr_field(base, ident) {
|
||||
let auto_unbox = maybe_auto_unbox(cx, ty::expr_ty(cx.tcx, base));
|
||||
let mut = false;
|
||||
alt ty::struct(cx.tcx, auto_unbox.t) {
|
||||
ty::ty_rec(fields) {
|
||||
for fld: ty::field in fields {
|
||||
if istr::eq(ident, fld.ident) {
|
||||
mut = fld.mt.mut != ast::imm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ty_obj(_) { }
|
||||
}
|
||||
ds += [@{mut: mut, kind: field, outer_t: auto_unbox.t}];
|
||||
ds += auto_unbox.ds;
|
||||
ex = base;
|
||||
}
|
||||
ast::expr_index(base, _) {
|
||||
let auto_unbox = maybe_auto_unbox(cx, ty::expr_ty(cx.tcx, base));
|
||||
alt ty::struct(cx.tcx, auto_unbox.t) {
|
||||
ty::ty_vec(mt) {
|
||||
ds +=
|
||||
[@{mut: mt.mut != ast::imm,
|
||||
kind: index,
|
||||
outer_t: auto_unbox.t}];
|
||||
}
|
||||
}
|
||||
ds += auto_unbox.ds;
|
||||
ex = base;
|
||||
}
|
||||
ast::expr_unary(op, base) {
|
||||
if op == ast::deref {
|
||||
let base_t = ty::expr_ty(cx.tcx, base);
|
||||
let mut = false;
|
||||
alt ty::struct(cx.tcx, base_t) {
|
||||
ty::ty_box(mt) { mut = mt.mut != ast::imm; }
|
||||
ty::ty_uniq(_) { }
|
||||
ty::ty_res(_, _, _) { }
|
||||
ty::ty_tag(_, _) { }
|
||||
ty::ty_ptr(mt) { mut = mt.mut != ast::imm; }
|
||||
}
|
||||
ds += [@{mut: mut, kind: unbox, outer_t: base_t}];
|
||||
ex = base;
|
||||
} else { break; }
|
||||
}
|
||||
_ { break; }
|
||||
}
|
||||
}
|
||||
if autoderef {
|
||||
let auto_unbox = maybe_auto_unbox(cx, ty::expr_ty(cx.tcx, ex));
|
||||
ds += auto_unbox.ds;
|
||||
}
|
||||
ret {ex: ex, ds: @ds};
|
||||
}
|
||||
|
||||
fn mut_field(ds: &@[deref]) -> bool {
|
||||
for d: deref in *ds { if d.mut { ret true; } }
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn inner_mut(ds: &@[deref]) -> option::t<ty::t> {
|
||||
for d: deref in *ds { if d.mut { ret some(d.outer_t); } }
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn path_def(cx: &ctx, ex: &@ast::expr) -> option::t<ast::def> {
|
||||
ret alt ex.node {
|
||||
ast::expr_path(_) { some(cx.tcx.def_map.get(ex.id)) }
|
||||
|
|
@ -749,11 +495,6 @@ fn def_is_local(d: &ast::def, objfields_count: bool) -> bool {
|
|||
};
|
||||
}
|
||||
|
||||
fn fty_args(cx: &ctx, fty: ty::t) -> [ty::arg] {
|
||||
ret alt ty::struct(cx.tcx, ty::type_autoderef(cx.tcx, fty)) {
|
||||
ty::ty_fn(_, args, _, _, _) | ty::ty_native_fn(_, args, _) { args }
|
||||
};
|
||||
}
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
|
|
|
|||
258
src/comp/middle/mut.rs
Normal file
258
src/comp/middle/mut.rs
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
import std::{vec, istr, option};
|
||||
import option::{some, none};
|
||||
import syntax::ast::*;
|
||||
import syntax::visit;
|
||||
import syntax::ast_util;
|
||||
|
||||
tag deref_t { unbox; field; index; }
|
||||
|
||||
type deref = @{mut: bool, kind: deref_t, outer_t: ty::t};
|
||||
|
||||
// Finds the root (the thing that is dereferenced) for the given expr, and a
|
||||
// vec of dereferences that were used on this root. Note that, in this vec,
|
||||
// the inner derefs come in front, so foo.bar[1] becomes rec(ex=foo,
|
||||
// ds=[index,field])
|
||||
fn expr_root(tcx: &ty::ctxt, ex: @expr, autoderef: bool)
|
||||
-> {ex: @expr, ds: @[deref]} {
|
||||
fn maybe_auto_unbox(tcx: &ty::ctxt, t: ty::t) -> {t: ty::t, ds: [deref]} {
|
||||
let ds = [];
|
||||
while true {
|
||||
alt ty::struct(tcx, t) {
|
||||
ty::ty_box(mt) {
|
||||
ds += [@{mut: mt.mut != imm, kind: unbox, outer_t: t}];
|
||||
t = mt.ty;
|
||||
}
|
||||
ty::ty_uniq(mt) {
|
||||
ds += [@{mut: false, kind: unbox, outer_t: t}];
|
||||
}
|
||||
ty::ty_res(_, inner, tps) {
|
||||
ds += [@{mut: false, kind: unbox, outer_t: t}];
|
||||
t = ty::substitute_type_params(tcx, tps, inner);
|
||||
}
|
||||
ty::ty_tag(did, tps) {
|
||||
let variants = ty::tag_variants(tcx, did);
|
||||
if vec::len(variants) != 1u ||
|
||||
vec::len(variants[0].args) != 1u {
|
||||
break;
|
||||
}
|
||||
ds += [@{mut: false, kind: unbox, outer_t: t}];
|
||||
t =
|
||||
ty::substitute_type_params(tcx, tps,
|
||||
variants[0].args[0]);
|
||||
}
|
||||
_ { break; }
|
||||
}
|
||||
}
|
||||
ret {t: t, ds: ds};
|
||||
}
|
||||
let ds: [deref] = [];
|
||||
while true {
|
||||
alt { ex.node } {
|
||||
expr_field(base, ident) {
|
||||
let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, base));
|
||||
let mut = false;
|
||||
alt ty::struct(tcx, auto_unbox.t) {
|
||||
ty::ty_rec(fields) {
|
||||
for fld: ty::field in fields {
|
||||
if istr::eq(ident, fld.ident) {
|
||||
mut = fld.mt.mut != imm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ty::ty_obj(_) { }
|
||||
}
|
||||
ds += [@{mut: mut, kind: field, outer_t: auto_unbox.t}];
|
||||
ds += auto_unbox.ds;
|
||||
ex = base;
|
||||
}
|
||||
expr_index(base, _) {
|
||||
let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, base));
|
||||
alt ty::struct(tcx, auto_unbox.t) {
|
||||
ty::ty_vec(mt) {
|
||||
ds +=
|
||||
[@{mut: mt.mut != imm,
|
||||
kind: index,
|
||||
outer_t: auto_unbox.t}];
|
||||
}
|
||||
}
|
||||
ds += auto_unbox.ds;
|
||||
ex = base;
|
||||
}
|
||||
expr_unary(op, base) {
|
||||
if op == deref {
|
||||
let base_t = ty::expr_ty(tcx, base);
|
||||
let mut = false;
|
||||
alt ty::struct(tcx, base_t) {
|
||||
ty::ty_box(mt) { mut = mt.mut != imm; }
|
||||
ty::ty_uniq(_) { }
|
||||
ty::ty_res(_, _, _) { }
|
||||
ty::ty_tag(_, _) { }
|
||||
ty::ty_ptr(mt) { mut = mt.mut != imm; }
|
||||
}
|
||||
ds += [@{mut: mut, kind: unbox, 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};
|
||||
}
|
||||
|
||||
fn mut_field(ds: &@[deref]) -> bool {
|
||||
for d: deref in *ds { if d.mut { ret true; } }
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn inner_mut(ds: &@[deref]) -> option::t<ty::t> {
|
||||
for d: deref in *ds { if d.mut { ret some(d.outer_t); } }
|
||||
ret none;
|
||||
}
|
||||
|
||||
// Actual mut-checking pass
|
||||
|
||||
type mut_map = std::map::hashmap<node_id, ()>;
|
||||
type ctx = {tcx: ty::ctxt, mut_map: mut_map};
|
||||
|
||||
fn check_crate(tcx: ty::ctxt, crate: &@crate) -> mut_map {
|
||||
let cx = @{tcx: tcx, mut_map: std::map::new_int_hash()};
|
||||
let v = @{visit_expr: bind visit_expr(cx, _, _, _),
|
||||
visit_decl: bind visit_decl(cx, _, _, _)
|
||||
with *visit::default_visitor::<()>()};
|
||||
visit::visit_crate(*crate, (), visit::mk_vt(v));
|
||||
ret cx.mut_map;
|
||||
}
|
||||
|
||||
tag msg { msg_assign; msg_move_out; msg_mut_alias; }
|
||||
|
||||
fn mk_err(cx: &@ctx, span: &syntax::codemap::span, msg: msg, name: &istr) {
|
||||
cx.tcx.sess.span_err(span, alt msg {
|
||||
msg_assign. { ~"assigning to " + name }
|
||||
msg_move_out. { ~"moving out of " + name }
|
||||
msg_mut_alias. { ~"passing " + name + ~" by mutable alias" }
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_decl(cx: &@ctx, d: &@decl, e: &(), v: &visit::vt<()>) {
|
||||
visit::visit_decl(d, e, v);
|
||||
alt d.node {
|
||||
decl_local(locs) {
|
||||
for loc: @local in locs {
|
||||
alt loc.node.init {
|
||||
some(init) {
|
||||
if init.op == init_move {
|
||||
check_move_rhs(cx, init.expr);
|
||||
}
|
||||
}
|
||||
none. { }
|
||||
}
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(cx: &@ctx, ex: &@expr, e: &(), v: &visit::vt<()>) {
|
||||
alt ex.node {
|
||||
expr_call(f, args) {
|
||||
check_call(cx, f, args);
|
||||
}
|
||||
expr_swap(lhs, rhs) {
|
||||
check_lval(cx, lhs, msg_assign);
|
||||
check_lval(cx, rhs, msg_assign);
|
||||
}
|
||||
expr_move(dest, src) {
|
||||
check_lval(cx, dest, msg_assign);
|
||||
check_move_rhs(cx, src);
|
||||
}
|
||||
expr_assign(dest, src) | expr_assign_op(_, dest, src) {
|
||||
check_lval(cx, dest, msg_assign);
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
visit::visit_expr(ex, e, v);
|
||||
}
|
||||
|
||||
fn check_lval(cx: &@ctx, dest: &@expr, msg: msg) {
|
||||
alt dest.node {
|
||||
expr_path(p) {
|
||||
let def = cx.tcx.def_map.get(dest.id);
|
||||
alt is_immutable_def(def) {
|
||||
some(name) { mk_err(cx, dest.span, msg, name); }
|
||||
_ {}
|
||||
}
|
||||
cx.mut_map.insert(ast_util::def_id_of_def(def).node, ());
|
||||
}
|
||||
_ {
|
||||
let root = expr_root(cx.tcx, dest, false);
|
||||
if vec::len(*root.ds) == 0u {
|
||||
mk_err(cx, dest.span, msg, ~"non-lvalue");
|
||||
} else if !root.ds[0].mut {
|
||||
let name = alt root.ds[0].kind {
|
||||
mut::unbox. { ~"immutable box" }
|
||||
mut::field. { ~"immutable field" }
|
||||
mut::index. { ~"immutable vec content" }
|
||||
};
|
||||
mk_err(cx, dest.span, msg, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_move_rhs(cx: &@ctx, src: &@expr) {
|
||||
alt src.node {
|
||||
expr_path(p) {
|
||||
alt cx.tcx.def_map.get(src.id) {
|
||||
def_obj_field(_, _) {
|
||||
mk_err(cx, src.span, msg_move_out, ~"object field");
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
check_lval(cx, src, msg_move_out);
|
||||
}
|
||||
_ {
|
||||
let root = expr_root(cx.tcx, src, false);
|
||||
// Not a path and no-derefs means this is a temporary.
|
||||
if vec::len(*root.ds) != 0u {
|
||||
cx.tcx.sess.span_err(src.span, ~"moving out of a data structure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_call(cx: &@ctx, f: &@expr, args: &[@expr]) {
|
||||
let arg_ts = ty::ty_fn_args(cx.tcx, ty::type_autoderef
|
||||
(cx.tcx, ty::expr_ty(cx.tcx, f)));
|
||||
let i = 0u;
|
||||
for arg_t: ty::arg in arg_ts {
|
||||
if arg_t.mode == ty::mo_alias(true) {
|
||||
check_lval(cx, args[i], msg_mut_alias);
|
||||
}
|
||||
i += 1u;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_immutable_def(def: &def) -> option::t<istr> {
|
||||
ret alt def {
|
||||
def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) |
|
||||
def_use(_) { some(~"static item") }
|
||||
def_obj_field(_, imm.) { some(~"immutable object field") }
|
||||
def_arg(_, alias(false)) { some(~"immutable alias") }
|
||||
def_binding(_) { some(~"binding") }
|
||||
_ { none }
|
||||
};
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
||||
|
|
@ -678,7 +678,7 @@ fn lookup_in_scope(e: &env, sc: scopes, sp: &span, name: &ident,
|
|||
scope_loop(local) {
|
||||
if ns == ns_value {
|
||||
alt lookup_in_pat(name, local.node.pat) {
|
||||
some(did) { ret some(ast::def_local(did)); }
|
||||
some(did) { ret some(ast::def_binding(did)); }
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6241,7 +6241,7 @@ fn write_abi_version(ccx: &@crate_ctxt) {
|
|||
}
|
||||
|
||||
fn trans_crate(sess: &session::session, crate: &@ast::crate, tcx: &ty::ctxt,
|
||||
output: &istr, amap: &ast_map::map, mut_map: alias::mut_map)
|
||||
output: &istr, amap: &ast_map::map, mut_map: mut::mut_map)
|
||||
-> ModuleRef {
|
||||
let llmod = istr::as_buf(~"rust_out", { |buf|
|
||||
llvm::LLVMModuleCreateWithNameInContext(buf,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ type crate_ctxt =
|
|||
type_sha1s: hashmap<ty::t, istr>,
|
||||
type_short_names: hashmap<ty::t, istr>,
|
||||
tcx: ty::ctxt,
|
||||
mut_map: alias::mut_map,
|
||||
mut_map: mut::mut_map,
|
||||
stats: stats,
|
||||
upcalls: @upcall::upcalls,
|
||||
rust_object_type: TypeRef,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue