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 f3e80344f534..c281fc6a9b66 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 c9fe71670c33..dd30cb8839c9 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 000000000000..cb7a85639ecc --- /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 54d08651a750..61bddcf4b945 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 0d3c3bbd8002..8b0cfe195019 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 98a47b7a386c..39a6b25f6ccc 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 aa88d1d4a88a..fe02cac179e9 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 7744e03fe01e..2bad0d1c403f 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 2bb54464157d..27629596ebf9 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 2cfcb31ccbc3..9de010eca93d 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 cc35f5451200..559e69275c7d 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 ad0ceed8ee30..cdac3706c813 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 6f0f7c535040..8b740218815d 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; }