From 2d1dec78e7fd2fa0a569f797d147d5940e81f3d0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 31 Aug 2011 18:45:37 +0200 Subject: [PATCH] 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. --- src/comp/driver/rustc.rs | 6 +- src/comp/middle/alias.rs | 291 +----------------- src/comp/middle/mut.rs | 258 ++++++++++++++++ src/comp/middle/resolve.rs | 2 +- src/comp/middle/trans.rs | 2 +- src/comp/middle/trans_common.rs | 2 +- src/comp/rustc.rc | 1 + src/comp/syntax/ast_util.rs | 10 +- src/test/compile-fail/swap-no-lval.rs | 2 +- .../writing-through-read-alias.rs | 2 +- .../compile-fail/writing-to-immutable-obj.rs | 2 +- .../compile-fail/writing-to-immutable-rec.rs | 2 +- .../compile-fail/writing-to-immutable-vec.rs | 2 +- 13 files changed, 292 insertions(+), 290 deletions(-) create mode 100644 src/comp/middle/mut.rs diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index f3e80344f53..c281fc6a9b6 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -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; } diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index c9fe71670c3..dd30cb8839c 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -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; type ctx = {tcx: ty::ctxt, local_map: std::map::hashmap, - 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::()}; 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) { 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) { - 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) { let handled = true; alt ex.node { @@ -123,15 +102,11 @@ fn visit_expr(cx: &@ctx, ex: &@ast::expr, sc: &scope, v: &vt) { 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) { } 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) { 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) { 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) { 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) { 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) { 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) { - 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) -> [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 { - 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 { 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; diff --git a/src/comp/middle/mut.rs b/src/comp/middle/mut.rs new file mode 100644 index 00000000000..cb7a85639ec --- /dev/null +++ b/src/comp/middle/mut.rs @@ -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 { + 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; +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 { + 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: diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 54d08651a75..61bddcf4b94 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -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)); } _ { } } } diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 0d3c3bbd800..8b0cfe19501 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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, diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index 98a47b7a386..39a6b25f6cc 100644 --- a/src/comp/middle/trans_common.rs +++ b/src/comp/middle/trans_common.rs @@ -149,7 +149,7 @@ type crate_ctxt = type_sha1s: hashmap, type_short_names: hashmap, tcx: ty::ctxt, - mut_map: alias::mut_map, + mut_map: mut::mut_map, stats: stats, upcalls: @upcall::upcalls, rust_object_type: TypeRef, diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index aa88d1d4a88..fe02cac179e 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -25,6 +25,7 @@ mod middle { mod resolve; mod typeck; mod check_alt; + mod mut; mod alias; mod kind; mod freevars; diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index 7744e03fe01..2bad0d1c403 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -46,7 +46,7 @@ fn def_id_of_def(d: def) -> def_id { } } -type pat_id_map = std::map::hashmap; +type pat_id_map = std::map::hashmap; // 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); diff --git a/src/test/compile-fail/swap-no-lval.rs b/src/test/compile-fail/swap-no-lval.rs index 2bb54464157..27629596ebf 100644 --- a/src/test/compile-fail/swap-no-lval.rs +++ b/src/test/compile-fail/swap-no-lval.rs @@ -1,3 +1,3 @@ -// error-pattern: assignment to non-lvalue +// error-pattern: assigning to non-lvalue fn main() { 5 <-> 3; } diff --git a/src/test/compile-fail/writing-through-read-alias.rs b/src/test/compile-fail/writing-through-read-alias.rs index 2cfcb31ccbc..9de010eca93 100644 --- a/src/test/compile-fail/writing-through-read-alias.rs +++ b/src/test/compile-fail/writing-through-read-alias.rs @@ -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}; diff --git a/src/test/compile-fail/writing-to-immutable-obj.rs b/src/test/compile-fail/writing-to-immutable-obj.rs index cc35f545120..559e69275c7 100644 --- a/src/test/compile-fail/writing-to-immutable-obj.rs +++ b/src/test/compile-fail/writing-to-immutable-obj.rs @@ -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; } } diff --git a/src/test/compile-fail/writing-to-immutable-rec.rs b/src/test/compile-fail/writing-to-immutable-rec.rs index ad0ceed8ee3..cdac3706c81 100644 --- a/src/test/compile-fail/writing-to-immutable-rec.rs +++ b/src/test/compile-fail/writing-to-immutable-rec.rs @@ -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; } diff --git a/src/test/compile-fail/writing-to-immutable-vec.rs b/src/test/compile-fail/writing-to-immutable-vec.rs index 6f0f7c53504..8b740218815 100644 --- a/src/test/compile-fail/writing-to-immutable-vec.rs +++ b/src/test/compile-fail/writing-to-immutable-vec.rs @@ -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; }