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:
Marijn Haverbeke 2011-08-31 18:45:37 +02:00
parent 34ae491ca9
commit 2d1dec78e7
13 changed files with 292 additions and 290 deletions

View file

@ -165,8 +165,10 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: &istr,
time(time_passes, ~"typestate checking", time(time_passes, ~"typestate checking",
bind middle::tstate::ck::check_crate(ty_cx, crate)); bind middle::tstate::ck::check_crate(ty_cx, crate));
} }
let mut_map = time(time_passes, ~"alias checking", let mut_map = time(time_passes, ~"mutability checking",
bind middle::alias::check_crate(ty_cx, crate)); 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", time(time_passes, ~"kind checking",
bind kind::check_crate(ty_cx, crate)); bind kind::check_crate(ty_cx, crate));
if sess.get_opts().no_trans { ret; } if sess.get_opts().no_trans { ret; }

View file

@ -5,6 +5,7 @@ import ast::ident;
import ast::fn_ident; import ast::fn_ident;
import ast::node_id; import ast::node_id;
import ast::def_id; import ast::def_id;
import mut::{expr_root, mut_field, inner_mut};
import syntax::codemap::span; import syntax::codemap::span;
import syntax::visit; import syntax::visit;
import visit::vt; import visit::vt;
@ -37,40 +38,30 @@ type restrict =
type scope = @[restrict]; type scope = @[restrict];
tag local_info { tag local_info {
arg(ast::mode);
objfield(ast::mutability);
local(uint); local(uint);
} }
type mut_map = std::map::hashmap<node_id, ()>;
type ctx = {tcx: ty::ctxt, type ctx = {tcx: ty::ctxt,
local_map: std::map::hashmap<node_id, local_info>, local_map: std::map::hashmap<node_id, local_info>,
mutable next_local: uint, mutable next_local: uint};
mut_map: mut_map};
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 // Stores information about object fields and function
// arguments that's otherwise not easily available. // arguments that's otherwise not easily available.
let cx = @{tcx: tcx, let cx = @{tcx: tcx,
local_map: std::map::new_int_hash(), local_map: std::map::new_int_hash(),
mutable next_local: 0u, mutable next_local: 0u};
mut_map: std::map::new_int_hash()};
let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _), let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _),
visit_item: bind visit_item(cx, _, _, _),
visit_expr: bind visit_expr(cx, _, _, _), visit_expr: bind visit_expr(cx, _, _, _),
visit_decl: bind visit_decl(cx, _, _, _) visit_decl: bind visit_decl(cx, _, _, _)
with *visit::default_visitor::<scope>()}; with *visit::default_visitor::<scope>()};
visit::visit_crate(*crate, @[], visit::mk_vt(v)); visit::visit_crate(*crate, @[], visit::mk_vt(v));
tcx.sess.abort_if_errors(); tcx.sess.abort_if_errors();
ret cx.mut_map;
} }
fn visit_fn(cx: &@ctx, f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span, 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>) { _name: &fn_ident, id: ast::node_id, sc: &scope, v: &vt<scope>) {
visit::visit_fn_decl(f.decl, sc, v); 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 = let scope =
alt f.proto { 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); 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>) { fn visit_expr(cx: &@ctx, ex: &@ast::expr, sc: &scope, v: &vt<scope>) {
let handled = true; let handled = true;
alt ex.node { 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); check_call(*cx, f, args, sc);
handled = false; 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_alt(input, arms) { check_alt(*cx, input, arms, sc, v); }
ast::expr_put(val) { ast::expr_put(val) {
alt val { alt val {
some(ex) { some(ex) {
let root = expr_root(*cx, ex, false); let root = expr_root(cx.tcx, ex, false);
if mut_field(root.ds) { if mut_field(root.ds) {
cx.tcx.sess.span_err(ex.span, cx.tcx.sess.span_err(ex.span,
~"result of put must be" + ~"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) { ast::expr_move(dest, src) {
check_assign(cx, dest, src, sc, v); 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) { ast::expr_assign(dest, src) | ast::expr_assign_op(_, dest, src) {
check_assign(cx, dest, src, sc, v); 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 { alt loc.node.init {
some(init) { some(init) {
if init.op == ast::init_move { if init.op == ast::init_move {
check_move_rhs(cx, init.expr, sc, v); check_lval(cx, init.expr, sc, v);
} }
} }
none. { } 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) fn check_call(cx: &ctx, f: &@ast::expr, args: &[@ast::expr], sc: &scope)
-> [restrict] { -> [restrict] {
let fty = ty::expr_ty(cx.tcx, f); let fty = ty::type_autoderef(cx.tcx, ty::expr_ty(cx.tcx, f));
let arg_ts = fty_args(cx, fty); let arg_ts = ty::ty_fn_args(cx.tcx, fty);
let mut_roots: [{arg: uint, node: node_id}] = []; let mut_roots: [{arg: uint, node: node_id}] = [];
let restricts = []; let restricts = [];
let i = 0u; let i = 0u;
for arg_t: ty::arg in arg_ts { for arg_t: ty::arg in arg_ts {
if arg_t.mode != ty::mo_val { if arg_t.mode != ty::mo_val {
let arg = args[i]; 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) { if arg_t.mode == ty::mo_alias(true) {
alt path_def(cx, arg) { alt path_def(cx, arg) {
some(def) { some(def) {
let dnum = ast_util::def_id_of_def(def).node; 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}]; 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); 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; 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, fn check_alt(cx: &ctx, input: &@ast::expr, arms: &[ast::arm], sc: &scope,
v: &vt<scope>) { v: &vt<scope>) {
v.visit_expr(input, sc, v); 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 { for a: ast::arm in arms {
let dnums = ast_util::pat_binding_ids(a.pats[0]); let dnums = ast_util::pat_binding_ids(a.pats[0]);
let new_sc = sc; 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, fn check_for(cx: &ctx, local: &@ast::local, seq: &@ast::expr, blk: &ast::blk,
sc: &scope, v: &vt<scope>) { sc: &scope, v: &vt<scope>) {
v.visit_expr(seq, sc, v); 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); let unsafe = inner_mut(root.ds);
// If this is a mutable vector, don't allow it to be touched. // 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>) { fn check_lval(cx: &@ctx, dest: &@ast::expr, sc: &scope, v: &vt<scope>) {
alt dest.node { alt dest.node {
ast::expr_path(p) { ast::expr_path(p) {
let dnum = ast_util::def_id_of_def(cx.tcx.def_map.get(dest.id)).node; let def = cx.tcx.def_map.get(dest.id);
cx.mut_map.insert(dnum, ()); let dnum = ast_util::def_id_of_def(def).node;
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");
}
for r: restrict in *sc { for r: restrict in *sc {
if r.root_var == some(dnum) { if r.root_var == some(dnum) {
r.ok = overwritten(dest.span, p); r.ok = overwritten(dest.span, p);
} }
} }
} }
_ { _ { visit_expr(cx, dest, sc, v); }
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");
}
}
} }
} }
@ -506,20 +377,6 @@ fn check_assign(cx: &@ctx, dest: &@ast::expr, src: &@ast::expr, sc: &scope,
check_lval(cx, dest, sc, v); 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) { fn test_scope(cx: &ctx, sc: &scope, r: &restrict, p: &ast::path) {
let prob = r.ok; let prob = r.ok;
for dep: uint in r.depends_on { for dep: uint in r.depends_on {
@ -561,117 +418,6 @@ fn deps(sc: &scope, root: &option::t<node_id>) -> [uint] {
ret result; 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> { fn path_def(cx: &ctx, ex: &@ast::expr) -> option::t<ast::def> {
ret alt ex.node { ret alt ex.node {
ast::expr_path(_) { some(cx.tcx.def_map.get(ex.id)) } 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: // Local Variables:
// mode: rust // mode: rust
// fill-column: 78; // fill-column: 78;

258
src/comp/middle/mut.rs Normal file
View 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:

View file

@ -678,7 +678,7 @@ fn lookup_in_scope(e: &env, sc: scopes, sp: &span, name: &ident,
scope_loop(local) { scope_loop(local) {
if ns == ns_value { if ns == ns_value {
alt lookup_in_pat(name, local.node.pat) { alt lookup_in_pat(name, local.node.pat) {
some(did) { ret some(ast::def_local(did)); } some(did) { ret some(ast::def_binding(did)); }
_ { } _ { }
} }
} }

View file

@ -6241,7 +6241,7 @@ fn write_abi_version(ccx: &@crate_ctxt) {
} }
fn trans_crate(sess: &session::session, crate: &@ast::crate, tcx: &ty::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 { -> ModuleRef {
let llmod = istr::as_buf(~"rust_out", { |buf| let llmod = istr::as_buf(~"rust_out", { |buf|
llvm::LLVMModuleCreateWithNameInContext(buf, llvm::LLVMModuleCreateWithNameInContext(buf,

View file

@ -149,7 +149,7 @@ type crate_ctxt =
type_sha1s: hashmap<ty::t, istr>, type_sha1s: hashmap<ty::t, istr>,
type_short_names: hashmap<ty::t, istr>, type_short_names: hashmap<ty::t, istr>,
tcx: ty::ctxt, tcx: ty::ctxt,
mut_map: alias::mut_map, mut_map: mut::mut_map,
stats: stats, stats: stats,
upcalls: @upcall::upcalls, upcalls: @upcall::upcalls,
rust_object_type: TypeRef, rust_object_type: TypeRef,

View file

@ -25,6 +25,7 @@ mod middle {
mod resolve; mod resolve;
mod typeck; mod typeck;
mod check_alt; mod check_alt;
mod mut;
mod alias; mod alias;
mod kind; mod kind;
mod freevars; mod freevars;

View file

@ -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 // This is used because same-named variables in alternative patterns need to
// use the node_id of their namesake in the first pattern. // 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 { fn is_exported(i: ident, m: _mod) -> bool {
let nonlocal = true; let nonlocal = true;
for it: @ast::item in m.items { for it: @item in m.items {
if it.ident == i { nonlocal = false; } if it.ident == i { nonlocal = false; }
alt it.node { alt it.node {
item_tag(variants, _) { item_tag(variants, _) {
@ -155,9 +155,9 @@ fn is_exported(i: ident, m: _mod) -> bool {
if !nonlocal { break; } if !nonlocal { break; }
} }
let count = 0u; let count = 0u;
for vi: @ast::view_item in m.view_items { for vi: @view_item in m.view_items {
alt vi.node { alt vi.node {
ast::view_item_export(ids, _) { view_item_export(ids, _) {
for id in ids { if istr::eq(i, id) { ret true; } } for id in ids { if istr::eq(i, id) { ret true; } }
count += 1u; 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 // This is a convenience function to transfor ternary expressions to if
// expressions so that they can be treated the same // 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 { alt e.node {
expr_ternary(cond, then, els) { expr_ternary(cond, then, els) {
let then_blk = block_from_expr(then); let then_blk = block_from_expr(then);

View file

@ -1,3 +1,3 @@
// error-pattern: assignment to non-lvalue // error-pattern: assigning to non-lvalue
fn main() { 5 <-> 3; } fn main() { 5 <-> 3; }

View file

@ -1,6 +1,6 @@
// -*- rust -*- // -*- rust -*-
// error-pattern:assignment to immutable field // error-pattern:assigning to immutable field
type point = {x: int, y: int, z: int}; type point = {x: int, y: int, z: int};

View file

@ -1,4 +1,4 @@
// error-pattern:assigning to immutable obj field // error-pattern:assigning to immutable object field
obj objy(x: int) { obj objy(x: int) {
fn foo() { x = 5; } fn foo() { x = 5; }
} }

View file

@ -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; } fn main() { let r: {x: int} = {x: 1}; r.x = 6; }

View file

@ -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; } fn main() { let v: [int] = [1, 2, 3]; v[1] = 4; }