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
@ -165,8 +165,10 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: &istr,
|
||||
time(time_passes, ~"typestate checking",
|
||||
bind middle::tstate::ck::check_crate(ty_cx, crate));
|
||||
}
|
||||
let mut_map = time(time_passes, ~"alias checking",
|
||||
bind middle::alias::check_crate(ty_cx, crate));
|
||||
let mut_map = time(time_passes, ~"mutability checking",
|
||||
bind middle::mut::check_crate(ty_cx, crate));
|
||||
time(time_passes, ~"alias checking",
|
||||
bind middle::alias::check_crate(ty_cx, crate));
|
||||
time(time_passes, ~"kind checking",
|
||||
bind kind::check_crate(ty_cx, crate));
|
||||
if sess.get_opts().no_trans { ret; }
|
||||
|
@ -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,
|
||||
|
@ -25,6 +25,7 @@ mod middle {
|
||||
mod resolve;
|
||||
mod typeck;
|
||||
mod check_alt;
|
||||
mod mut;
|
||||
mod alias;
|
||||
mod kind;
|
||||
mod freevars;
|
||||
|
@ -46,7 +46,7 @@ fn def_id_of_def(d: def) -> def_id {
|
||||
}
|
||||
}
|
||||
|
||||
type pat_id_map = std::map::hashmap<istr, ast::node_id>;
|
||||
type pat_id_map = std::map::hashmap<istr, node_id>;
|
||||
|
||||
// This is used because same-named variables in alternative patterns need to
|
||||
// use the node_id of their namesake in the first pattern.
|
||||
@ -142,7 +142,7 @@ fn ty_mach_to_str(tm: ty_mach) -> istr {
|
||||
|
||||
fn is_exported(i: ident, m: _mod) -> bool {
|
||||
let nonlocal = true;
|
||||
for it: @ast::item in m.items {
|
||||
for it: @item in m.items {
|
||||
if it.ident == i { nonlocal = false; }
|
||||
alt it.node {
|
||||
item_tag(variants, _) {
|
||||
@ -155,9 +155,9 @@ fn is_exported(i: ident, m: _mod) -> bool {
|
||||
if !nonlocal { break; }
|
||||
}
|
||||
let count = 0u;
|
||||
for vi: @ast::view_item in m.view_items {
|
||||
for vi: @view_item in m.view_items {
|
||||
alt vi.node {
|
||||
ast::view_item_export(ids, _) {
|
||||
view_item_export(ids, _) {
|
||||
for id in ids { if istr::eq(i, id) { ret true; } }
|
||||
count += 1u;
|
||||
}
|
||||
@ -202,7 +202,7 @@ fn obj_field_from_anon_obj_field(f: &anon_obj_field) -> obj_field {
|
||||
|
||||
// This is a convenience function to transfor ternary expressions to if
|
||||
// expressions so that they can be treated the same
|
||||
fn ternary_to_if(e: &@expr) -> @ast::expr {
|
||||
fn ternary_to_if(e: &@expr) -> @expr {
|
||||
alt e.node {
|
||||
expr_ternary(cond, then, els) {
|
||||
let then_blk = block_from_expr(then);
|
||||
|
@ -1,3 +1,3 @@
|
||||
// error-pattern: assignment to non-lvalue
|
||||
// error-pattern: assigning to non-lvalue
|
||||
|
||||
fn main() { 5 <-> 3; }
|
||||
|
@ -1,6 +1,6 @@
|
||||
// -*- rust -*-
|
||||
|
||||
// error-pattern:assignment to immutable field
|
||||
// error-pattern:assigning to immutable field
|
||||
|
||||
type point = {x: int, y: int, z: int};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// error-pattern:assigning to immutable obj field
|
||||
// error-pattern:assigning to immutable object field
|
||||
obj objy(x: int) {
|
||||
fn foo() { x = 5; }
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
// error-pattern: assignment to immutable field
|
||||
// error-pattern: assigning to immutable field
|
||||
fn main() { let r: {x: int} = {x: 1}; r.x = 6; }
|
||||
|
@ -1,2 +1,2 @@
|
||||
// error-pattern:assignment to immutable vec content
|
||||
// error-pattern:assigning to immutable vec content
|
||||
fn main() { let v: [int] = [1, 2, 3]; v[1] = 4; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user