From fccf0652661f8f76875655628e6a61d7e00bd856 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 10 Jun 2011 12:03:50 +0200 Subject: [PATCH] Implement mutable/immutable alias distinction. Before, all aliases were implicitly mutable, and writing &mutable was the same as writing &. Now, the two are distinguished, and assignments to regular aliases are no longer allowed. --- src/comp/front/ast.rs | 2 +- src/comp/front/creader.rs | 8 +++- src/comp/front/parser.rs | 15 ++------ src/comp/middle/alias.rs | 54 +++++++++++++++++++-------- src/comp/middle/metadata.rs | 8 +++- src/comp/middle/trans.rs | 45 +++++++++++----------- src/comp/middle/ty.rs | 2 +- src/comp/middle/typeck.rs | 6 +-- src/comp/pretty/ppaux.rs | 10 ++--- src/comp/pretty/pprust.rs | 12 +++++- src/test/compile-fail/assign-alias.rs | 10 +++++ 11 files changed, 108 insertions(+), 64 deletions(-) create mode 100644 src/test/compile-fail/assign-alias.rs diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index b36a956464c..37d26a6889a 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -203,7 +203,7 @@ fn unop_to_str(unop op) -> str { tag mode { val; - alias; + alias(bool); } type stmt = spanned[stmt_]; diff --git a/src/comp/front/creader.rs b/src/comp/front/creader.rs index 4cb889de16c..c2039624300 100644 --- a/src/comp/front/creader.rs +++ b/src/comp/front/creader.rs @@ -328,8 +328,12 @@ fn parse_ty_fn(@pstate st, str_def sd) -> tup(vec[ty::arg], ty::t, while (peek(st) as char != ']') { auto mode = ty::mo_val; if (peek(st) as char == '&') { - mode = ty::mo_alias; - st.pos = st.pos + 1u; + mode = ty::mo_alias(false); + st.pos += 1u; + if (peek(st) as char == 'm') { + mode = ty::mo_alias(true); + st.pos += 1u; + } } inputs += [rec(mode=mode, ty=parse_ty(st, sd))]; } diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs index d3a8fa9a329..980daa40135 100644 --- a/src/comp/front/parser.rs +++ b/src/comp/front/parser.rs @@ -319,16 +319,10 @@ fn parse_ty_fn(ast::proto proto, &parser p, uint lo) -> ast::ty_ { fn parse_fn_input_ty(&parser p) -> ast::ty_arg { auto lo = p.get_lo_pos(); - auto mode; + auto mode = ast::val; if (p.peek() == token::BINOP(token::AND)) { p.bump(); - mode = ast::alias; - - if (eat_word(p, "mutable")) { - // TODO: handle mutable alias args - } - } else { - mode = ast::val; + mode = ast::alias(eat_word(p, "mutable")); } auto t = parse_ty(p); @@ -598,11 +592,8 @@ fn parse_ty(&parser p) -> @ast::ty { fn parse_arg(&parser p) -> ast::arg { let ast::mode m = ast::val; if (p.peek() == token::BINOP(token::AND)) { - m = ast::alias; p.bump(); - - // TODO: handle mutable alias args - eat_word(p, "mutable"); + m = ast::alias(eat_word(p, "mutable")); } let @ast::ty t = parse_ty(p); let ast::ident i = parse_value_ident(p); diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index d5357491123..4b4b54cad2a 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -23,7 +23,12 @@ type restrict = @rec(vec[def_num] root_vars, vec[uint] depends_on, mutable valid ok); -type scope = vec[restrict]; +type scope = rec(vec[tup(def_num, ast::mode)] args, + vec[restrict] rs); +fn scope(&scope sc, vec[restrict] add) -> scope { + ret rec(args=sc.args, rs=sc.rs + add); +} + type ctx = rec(@ty::ctxt tcx, resolve::def_map dm); @@ -32,13 +37,17 @@ fn check_crate(@ty::ctxt tcx, resolve::def_map dm, &@ast::crate crate) { auto v = @rec(visit_fn = visit_fn, visit_expr = bind visit_expr(cx, _, _, _) with *visit::default_visitor[scope]()); - visit::visit_crate(*crate, [], visit::vtor(v)); + visit::visit_crate(*crate, rec(args=[], rs=[]), visit::vtor(v)); } fn visit_fn(&ast::_fn f, &vec[ast::ty_param] tp, &span sp, &ident name, &ast::def_id d_id, &ast::ann a, &scope sc, &vt[scope] v) { visit::visit_fn_decl(f.decl, sc, v); - vt(v).visit_block(f.body, [], v); + auto args = []; + for (ast::arg arg in f.decl.inputs) { + vec::push(args, tup(arg.id._1, arg.mode)); + } + vt(v).visit_block(f.body, rec(args=args, rs=[]), v); } fn visit_expr(&@ctx cx, &@ast::expr ex, &scope sc, &vt[scope] v) { @@ -179,12 +188,12 @@ fn check_alt(&ctx cx, &@ast::expr input, &vec[ast::arm] arms, auto dnums = arm_defnums(a); auto new_sc = sc; if (vec::len(dnums) > 0u) { - vec::push(new_sc, @rec(root_vars=roots, - block_defnum=dnums.(0), - bindings=dnums, - tys=forbidden_tp, - depends_on=deps(sc, roots), - mutable ok=valid)); + new_sc = scope(sc, [@rec(root_vars=roots, + block_defnum=dnums.(0), + bindings=dnums, + tys=forbidden_tp, + depends_on=deps(sc, roots), + mutable ok=valid)]); } visit::visit_arm(a, new_sc, v); } @@ -225,7 +234,7 @@ fn check_for_each(&ctx cx, &@ast::decl decl, &@ast::expr call, tys=data.unsafe_ts, depends_on=deps(sc, data.root_vars), mutable ok=valid); - visit::visit_block(block, sc + [new_sc], v); + visit::visit_block(block, scope(sc, [new_sc]), v); } } } @@ -261,7 +270,7 @@ fn check_for(&ctx cx, &@ast::decl decl, &@ast::expr seq, tys=unsafe, depends_on=deps(sc, root_def), mutable ok=valid); - visit::visit_block(block, sc + [new_sc], v); + visit::visit_block(block, scope(sc, [new_sc]), v); } fn check_var(&ctx cx, &@ast::expr ex, &ast::path p, ast::ann ann, bool assign, @@ -270,7 +279,7 @@ fn check_var(&ctx cx, &@ast::expr ex, &ast::path p, ast::ann ann, bool assign, if (!def_is_local(def)) { ret; } auto my_defnum = ast::def_id_of_def(def)._1; auto var_t = ty::expr_ty(*cx.tcx, ex); - for (restrict r in sc) { + for (restrict r in sc.rs) { // excludes variables introduced since the alias was made if (my_defnum < r.block_defnum) { for (ty::t t in r.tys) { @@ -287,14 +296,29 @@ fn check_var(&ctx cx, &@ast::expr ex, &ast::path p, ast::ann ann, bool assign, fn check_assign(&@ctx cx, &@ast::expr dest, &@ast::expr src, &scope sc, &vt[scope] v) { visit_expr(cx, src, sc, v); + alt (dest.node) { case (ast::expr_path(?p, ?ann)) { auto dnum = ast::def_id_of_def(cx.dm.get(ann.id))._1; + + for (tup(def_num, ast::mode) arg in sc.args) { + if (arg._0 == dnum && arg._1 == ast::alias(false)) { + cx.tcx.sess.span_err + (dest.span, "assigning to immutable alias"); + } + } + auto var_t = ty::expr_ty(*cx.tcx, dest); - for (restrict r in sc) { + for (restrict r in sc.rs) { if (vec::member(dnum, r.root_vars)) { r.ok = overwritten(dest.span, p); } + for (def_num bnd in r.bindings) { + if (dnum == bnd) { + cx.tcx.sess.span_err + (dest.span, "assigning to immutable alias"); + } + } } check_var(*cx, dest, p, ann, true, sc); } @@ -308,7 +332,7 @@ fn test_scope(&ctx cx, &scope sc, &restrict r, &ast::path p) { auto prob = r.ok; for (uint dep in r.depends_on) { if (prob != valid) { break; } - prob = sc.(dep).ok; + prob = sc.rs.(dep).ok; } if (prob != valid) { auto msg = alt (prob) { @@ -328,7 +352,7 @@ fn test_scope(&ctx cx, &scope sc, &restrict r, &ast::path p) { fn deps(&scope sc, vec[def_num] roots) -> vec[uint] { auto i = 0u; auto result = []; - for (restrict r in sc) { + for (restrict r in sc.rs) { for (def_num dn in roots) { if (vec::member(dn, r.bindings)) { vec::push(result, i); diff --git a/src/comp/middle/metadata.rs b/src/comp/middle/metadata.rs index 4aae74c5a6e..bba97d38c46 100644 --- a/src/comp/middle/metadata.rs +++ b/src/comp/middle/metadata.rs @@ -250,7 +250,13 @@ mod Encode { &ast::controlflow cf, &vec[@ast::constr] constrs) { w.write_char('['); for (ty::arg arg in args) { - if (arg.mode == ty::mo_alias) { w.write_char('&'); } + alt (arg.mode) { + case (ty::mo_alias(?mut)) { + w.write_char('&'); + if (mut) { w.write_char('m'); } + } + case (ty::mo_val) {} + } enc_ty(w, cx, arg.ty); } w.write_char(']'); diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 8c52093963c..ee5e1c84085 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -691,12 +691,12 @@ fn type_of_explicit_args(&@crate_ctxt cx, &span sp, let vec[TypeRef] atys = []; for (ty::arg arg in inputs) { if (ty::type_has_dynamic_size(cx.tcx, arg.ty)) { - assert (arg.mode == ty::mo_alias); + assert (arg.mode != ty::mo_val); atys += [T_typaram_ptr(cx.tn)]; } else { let TypeRef t; alt (arg.mode) { - case (ty::mo_alias) { + case (ty::mo_alias(_)) { t = T_ptr(type_of_inner(cx, sp, arg.ty)); } case (_) { @@ -762,9 +762,9 @@ fn type_of_fn_full(&@crate_ctxt cx, atys += [T_fn_pair(cx.tn, type_of_fn_full(cx, sp, ast::proto_fn, none[TypeRef], - [rec(mode=ty::mo_alias, - ty=output)], - ty::mk_nil(cx.tcx), 0u))]; + [rec(mode=ty::mo_alias(false), + ty=output)], + ty::mk_nil(cx.tcx), 0u))]; } // ... then explicit args. @@ -923,7 +923,7 @@ fn type_of_inner(&@crate_ctxt cx, &span sp, &ty::t t) -> TypeRef { fn type_of_arg(@local_ctxt cx, &span sp, &ty::arg arg) -> TypeRef { alt (ty::struct(cx.ccx.tcx, arg.ty)) { case (ty::ty_param(_)) { - if (arg.mode == ty::mo_alias) { + if (arg.mode != ty::mo_val) { ret T_typaram_ptr(cx.ccx.tn); } } @@ -933,7 +933,7 @@ fn type_of_arg(@local_ctxt cx, &span sp, &ty::arg arg) -> TypeRef { } auto typ; - if (arg.mode == ty::mo_alias) { + if (arg.mode != ty::mo_val) { typ = T_ptr(type_of_inner(cx.ccx, sp, arg.ty)); } else { typ = type_of_inner(cx.ccx, sp, arg.ty); @@ -4063,7 +4063,7 @@ fn trans_for_each(&@block_ctxt cx, auto iter_body_llty = type_of_fn_full(lcx.ccx, cx.sp, ast::proto_fn, none[TypeRef], - [rec(mode=ty::mo_alias, ty=decl_ty)], + [rec(mode=ty::mo_alias(false), ty=decl_ty)], ty::mk_nil(lcx.ccx.tcx), 0u); let ValueRef lliterbody = decl_internal_fastcall_fn(lcx.ccx.llmod, @@ -4840,7 +4840,7 @@ fn trans_bind_thunk(&@local_ctxt cx, } } else if (ty::type_contains_params(cx.ccx.tcx, out_arg.ty)) { - assert (out_arg.mode == ty::mo_alias); + assert (out_arg.mode != ty::mo_val); val = bcx.build.PointerCast(val, llout_arg_ty); } @@ -4853,7 +4853,7 @@ fn trans_bind_thunk(&@local_ctxt cx, let ValueRef passed_arg = llvm::LLVMGetParam(llthunk, a); if (ty::type_contains_params(cx.ccx.tcx, out_arg.ty)) { - assert (out_arg.mode == ty::mo_alias); + assert (out_arg.mode != ty::mo_val); passed_arg = bcx.build.PointerCast(passed_arg, llout_arg_ty); } @@ -5098,7 +5098,7 @@ fn trans_arg_expr(&@block_ctxt cx, auto re = trans_expr(bcx, e); val = re.val; bcx = re.bcx; - } else if (arg.mode == ty::mo_alias) { + } else if (arg.mode != ty::mo_val) { let lval_result lv; if (ty::is_lval(e)) { lv = trans_lval(bcx, e); @@ -5125,7 +5125,7 @@ fn trans_arg_expr(&@block_ctxt cx, bcx = re.bcx; } - if (arg.mode != ty::mo_alias) { + if (arg.mode == ty::mo_val) { bcx = take_ty(bcx, val, e_ty).bcx; } @@ -5930,7 +5930,7 @@ fn trans_put(&@block_ctxt cx, &option::t[@ast::expr] e) -> result { case (none) { } case (some(?x)) { auto e_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, x); - auto arg = rec(mode=ty::mo_alias, ty=e_ty); + auto arg = rec(mode=ty::mo_alias(false), ty=e_ty); auto arg_tys = type_of_explicit_args(cx.fcx.lcx.ccx, x.span, [arg]); auto r = trans_arg_expr(bcx, arg, arg_tys.(0), x); @@ -6194,7 +6194,7 @@ fn mk_spawn_wrapper(&@block_ctxt cx, let TypeRef wrapper_fn_type = type_of_fn(cx.fcx.lcx.ccx, cx.sp, ast::proto_fn, - [rec(mode = ty::mo_alias, ty = args_ty)], + [rec(mode = ty::mo_alias(false), ty = args_ty)], ty::idx_nil, 0u); @@ -6401,7 +6401,7 @@ fn trans_anon_obj(@block_ctxt cx, &span sp, case (some(?fields)) { addtl_fields = fields; for (ast::obj_field f in fields) { - addtl_fn_args += [rec(mode=ast::alias, ty=f.ty, + addtl_fn_args += [rec(mode=ast::alias(false), ty=f.ty, ident=f.ident, id=f.id)]; } } @@ -7045,7 +7045,7 @@ fn copy_args_to_allocas(@fn_ctxt fcx, let uint arg_n = 0u; for (ast::arg aarg in args) { - if (aarg.mode != ast::alias) { + if (aarg.mode == ast::val) { auto arg_t = type_of_arg(bcx.fcx.lcx, fcx.sp, arg_tys.(arg_n)); auto a = alloca(bcx, arg_t); auto argval = bcx.fcx.llargs.get(aarg.id); @@ -7063,7 +7063,7 @@ fn add_cleanups_for_args(&@block_ctxt bcx, vec[ty::arg] arg_tys) { let uint arg_n = 0u; for (ast::arg aarg in args) { - if (aarg.mode != ast::alias) { + if (aarg.mode == ast::val) { auto argval = bcx.fcx.llargs.get(aarg.id); find_scope_cx(bcx).cleanups += [clean(bind drop_slot(_, argval, arg_tys.(arg_n).ty))]; @@ -7329,7 +7329,8 @@ fn trans_obj(@local_ctxt cx, &span sp, &ast::_obj ob, ast::def_id oid, // we're creating. let vec[ast::arg] fn_args = []; for (ast::obj_field f in ob.fields) { - fn_args += [rec(mode=ast::alias, ty=f.ty, ident=f.ident, id=f.id)]; + fn_args += [rec(mode=ast::alias(false), + ty=f.ty, ident=f.ident, id=f.id)]; } auto fcx = new_fn_ctxt(cx, sp, llctor_decl); @@ -7522,10 +7523,10 @@ fn trans_tag_variant(@local_ctxt cx, ast::def_id tag_id, let vec[ast::arg] fn_args = []; auto i = 0u; for (ast::variant_arg varg in variant.node.args) { - fn_args += [rec(mode=ast::alias, - ty=varg.ty, - ident="arg" + uint::to_str(i, 10u), - id=varg.id)]; + fn_args += [rec(mode=ast::alias(false), + ty=varg.ty, + ident="arg" + uint::to_str(i, 10u), + id=varg.id)]; } assert (cx.ccx.item_ids.contains_key(variant.node.id)); diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index acfcb93cb2f..79ac28b1590 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -41,7 +41,7 @@ import util::data::interner; tag mode { mo_val; - mo_alias; + mo_alias(bool); } type arg = rec(mode mode, t ty); diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 98bb1f9de30..fe7d793db7f 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -179,7 +179,7 @@ fn ast_mode_to_mode(ast::mode mode) -> ty::mode { auto ty_mode; alt (mode) { case (ast::val) { ty_mode = mo_val; } - case (ast::alias) { ty_mode = mo_alias; } + case (ast::alias(?mut)) { ty_mode = mo_alias(mut); } } ret ty_mode; } @@ -548,7 +548,7 @@ mod collect { for (ast::obj_field f in obj_info.fields) { auto g = bind getter(cx, _); auto t_field = ast_ty_to_ty(cx.tcx, g, f.ty); - vec::push[arg](t_inputs, rec(mode=ty::mo_alias, ty=t_field)); + vec::push(t_inputs, rec(mode=ty::mo_alias(false), ty=t_field)); } let vec[@ast::constr] constrs = []; @@ -681,7 +681,7 @@ mod collect { let vec[arg] args = []; for (ast::variant_arg va in variant.node.args) { auto arg_ty = ast_ty_to_ty(cx.tcx, f, va.ty); - args += [rec(mode=ty::mo_alias, ty=arg_ty)]; + args += [rec(mode=ty::mo_alias(false), ty=arg_ty)]; } auto tag_t = ty::mk_tag(cx.tcx, tag_id, ty_param_tys); // FIXME: this will be different for constrained types diff --git a/src/comp/pretty/ppaux.rs b/src/comp/pretty/ppaux.rs index 6c1aed947d9..2706eb55832 100644 --- a/src/comp/pretty/ppaux.rs +++ b/src/comp/pretty/ppaux.rs @@ -13,11 +13,11 @@ fn ty_to_str(&ctxt cx, &t typ) -> str { fn fn_input_to_str(&ctxt cx, &rec(middle::ty::mode mode, t ty) input) -> str { - auto s; - alt (input.mode) { - case (mo_val) { s = ""; } - case (mo_alias) { s = "&"; } - } + auto s = alt (input.mode) { + case (mo_val) { "" } + case (mo_alias(false)) { "&" } + case (mo_alias(true)) { "&mutable " } + }; ret s + ty_to_str(cx, input.ty); } diff --git a/src/comp/pretty/pprust.rs b/src/comp/pretty/pprust.rs index 20fe17c48c0..6e1dbf8eba3 100644 --- a/src/comp/pretty/pprust.rs +++ b/src/comp/pretty/pprust.rs @@ -960,7 +960,7 @@ fn print_fn(&ps s, ast::fn_decl decl, ast::proto proto, str name, popen(s); fn print_arg(&ps s, &ast::arg x) { ibox(s, indent_unit); - if (x.mode == ast::alias) {word(s.s, "&");} + print_alias(s, x.mode); print_type(s, *x.ty); space(s.s); word(s.s, x.ident); @@ -977,6 +977,14 @@ fn print_fn(&ps s, ast::fn_decl decl, ast::proto proto, str name, } } +fn print_alias(&ps s, ast::mode m) { + alt (m) { + case (ast::alias(true)) { word_space(s, "&mutable"); } + case (ast::alias(false)) { word(s.s, "&"); } + case (ast::val) {} + } +} + fn print_type_params(&ps s, &vec[ast::ty_param] params) { if (vec::len[ast::ty_param](params) > 0u) { word(s.s, "["); @@ -1095,7 +1103,7 @@ fn print_ty_fn(&ps s, &ast::proto proto, &option::t[str] id, zerobreak(s.s); popen(s); fn print_arg(&ps s, &ast::ty_arg input) { - if (input.node.mode == ast::alias) {word(s.s, "&");} + print_alias(s, input.node.mode); print_type(s, *input.node.ty); } auto f = print_arg; diff --git a/src/test/compile-fail/assign-alias.rs b/src/test/compile-fail/assign-alias.rs new file mode 100644 index 00000000000..a00859e1a52 --- /dev/null +++ b/src/test/compile-fail/assign-alias.rs @@ -0,0 +1,10 @@ +// xfail-stage1 +// error-pattern:assigning to immutable alias + +fn f(&int i) { + i += 2; +} + +fn main() { + f(1); +} \ No newline at end of file