optionally enforce local variable mutability

This commit is contained in:
Niko Matsakis 2012-02-28 19:28:29 -08:00
parent 99f231f347
commit 2dd5b3ace6
20 changed files with 112 additions and 73 deletions

View File

@ -421,6 +421,7 @@ fn build_session_options(match: getopts::match,
let cfg = parse_cfgspecs(getopts::opt_strs(match, "cfg"));
let test = opt_present(match, "test");
let warn_unused_imports = opt_present(match, "warn-unused-imports");
let enforce_mut_vars = opt_present(match, "enforce-mut-vars");
let sopts: @session::options =
@{crate_type: crate_type,
static: static,
@ -444,7 +445,8 @@ fn build_session_options(match: getopts::match,
no_asm_comments: no_asm_comments,
monomorphize: monomorphize,
inline: inline,
warn_unused_imports: warn_unused_imports};
warn_unused_imports: warn_unused_imports,
enforce_mut_vars: enforce_mut_vars};
ret sopts;
}
@ -518,7 +520,8 @@ fn opts() -> [getopts::opt] {
optmulti("cfg"), optflag("test"),
optflag("lib"), optflag("bin"), optflag("static"), optflag("gc"),
optflag("no-asm-comments"),
optflag("warn-unused-imports")];
optflag("warn-unused-imports"),
optflag("enforce-mut-vars")];
}
type output_filenames = @{out_filename: str, obj_filename:str};

View File

@ -47,7 +47,8 @@ type options =
no_asm_comments: bool,
monomorphize: bool,
inline: bool,
warn_unused_imports: bool};
warn_unused_imports: bool,
enforce_mut_vars: bool};
type crate_metadata = {name: str, data: [u8]};

View File

@ -360,7 +360,7 @@ impl of tr for ast::def {
ast::def_native_mod(did) { ast::def_native_mod(did.tr(xcx)) }
ast::def_const(did) { ast::def_const(did.tr(xcx)) }
ast::def_arg(nid, m) { ast::def_arg(xcx.tr_id(nid), m) }
ast::def_local(nid) { ast::def_local(xcx.tr_id(nid)) }
ast::def_local(nid, b) { ast::def_local(xcx.tr_id(nid), b) }
ast::def_variant(e_did, v_did) {
ast::def_variant(e_did.tr(xcx), v_did.tr(xcx))
}

View File

@ -1956,18 +1956,20 @@ fn serialize_114<S: std::serialization::serializer>(s: S,
fn serialize_106<S: std::serialization::serializer>(s: S,
v: syntax::ast::local_) {
s.emit_rec(/*@syntax::ast::ty*//*@syntax::ast::pat*/
s.emit_rec(/*bool*//*@syntax::ast::ty*//*@syntax::ast::pat*/
/*core::option::t<syntax::ast::initializer>*/
/*syntax::ast::node_id*/
{||
{
s.emit_rec_field("ty", 0u,
s.emit_rec_field("is_mutbl", 0u,
{|| serialize_18(s, v.is_mutbl) });
s.emit_rec_field("ty", 1u,
{|| serialize_29(s, v.ty) });
s.emit_rec_field("pat", 1u,
s.emit_rec_field("pat", 2u,
{|| serialize_107(s, v.pat) });
s.emit_rec_field("init", 2u,
s.emit_rec_field("init", 3u,
{|| serialize_114(s, v.init) });
s.emit_rec_field("id", 3u, {|| serialize_27(s, v.id) })
s.emit_rec_field("id", 4u, {|| serialize_27(s, v.id) })
}
});
}
@ -5974,6 +5976,8 @@ fn deserialize_106<S: std::serialization::deserializer>(s: S) ->
s.read_rec(
/*bool*/
/*@syntax::ast::ty*/
/*@syntax::ast::pat*/
@ -5983,13 +5987,16 @@ fn deserialize_106<S: std::serialization::deserializer>(s: S) ->
/*syntax::ast::node_id*/
{||
{ty: s.read_rec_field("ty", 0u, {|| deserialize_29(s) }),
{is_mutbl:
s.read_rec_field("is_mutbl", 0u,
{|| deserialize_18(s) }),
ty: s.read_rec_field("ty", 1u, {|| deserialize_29(s) }),
pat:
s.read_rec_field("pat", 1u, {|| deserialize_107(s) }),
s.read_rec_field("pat", 2u, {|| deserialize_107(s) }),
init:
s.read_rec_field("init", 2u,
s.read_rec_field("init", 3u,
{|| deserialize_114(s) }),
id: s.read_rec_field("id", 3u, {|| deserialize_27(s) }),}
id: s.read_rec_field("id", 4u, {|| deserialize_27(s) }),}
})
}
/*syntax::ast::local*/
@ -8015,7 +8022,7 @@ fn serialize_161<S: std::serialization::serializer>(s: S,
/*syntax::ast::def_id*/
/*syntax::ast::node_id*/
/*syntax::ast::mode<syntax::ast::rmode>*/
/*syntax::ast::node_id*/
/*syntax::ast::node_id*//*bool*/
/*syntax::ast::def_id*//*syntax::ast::def_id*/
/*syntax::ast::def_id*/
/*syntax::ast::prim_ty*/
@ -8112,14 +8119,19 @@ fn serialize_161<S: std::serialization::serializer>(s: S,
}
})
}
syntax::ast::def_local(v0) {
s.emit_enum_variant("syntax::ast::def_local", 6u, 1u,
syntax::ast::def_local(v0, v1) {
s.emit_enum_variant("syntax::ast::def_local", 6u, 2u,
{||
{
s.emit_enum_variant_arg(0u,
{||
serialize_27(s,
v0)
});
s.emit_enum_variant_arg(1u,
{||
serialize_18(s,
v1)
})
}
})
@ -8378,7 +8390,7 @@ fn deserialize_161<S: std::serialization::deserializer>(s: S) ->
/*syntax::ast::node_id*/
/*syntax::ast::mode<syntax::ast::rmode>*/
/*syntax::ast::node_id*/
/*syntax::ast::node_id*//*bool*/
/*syntax::ast::def_id*//*syntax::ast::def_id*/
@ -8451,6 +8463,10 @@ fn deserialize_161<S: std::serialization::deserializer>(s: S) ->
syntax::ast::def_local(s.read_enum_variant_arg(0u,
{||
deserialize_27(s)
}),
s.read_enum_variant_arg(1u,
{||
deserialize_18(s)
}))
}
7u {

View File

@ -255,7 +255,7 @@ fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr])
}
let f_may_close =
alt f.node {
ast::expr_path(_) { def_is_local(cx.tcx.def_map.get(f.id)) }
ast::expr_path(_) { def_is_local_or_self(cx.tcx.def_map.get(f.id)) }
_ { true }
};
if f_may_close {
@ -390,7 +390,7 @@ fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk,
fn check_var(cx: ctx, ex: @ast::expr, p: @ast::path, id: ast::node_id,
assign: bool, sc: scope) {
let def = cx.tcx.def_map.get(id);
if !def_is_local(def) { ret; }
if !def_is_local_or_self(def) { ret; }
let my_defnum = ast_util::def_id_of_def(def).node;
let my_local_id = local_id_of_node(cx, my_defnum);
let var_t = ty::expr_ty(cx.tcx, ex);
@ -539,9 +539,9 @@ fn ty_can_unsafely_include(cx: ctx, needle: unsafe_ty, haystack: ty::t,
ret helper(cx.tcx, needle, haystack, mutbl);
}
fn def_is_local(d: ast::def) -> bool {
fn def_is_local_or_self(d: ast::def) -> bool {
alt d {
ast::def_local(_) | ast::def_arg(_, _) | ast::def_binding(_) |
ast::def_local(_, _) | ast::def_arg(_, _) | ast::def_binding(_) |
ast::def_upvar(_, _, _) | ast::def_self(_) { true }
_ { false }
}

View File

@ -342,7 +342,7 @@ fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> {
alt d {
def_local(id) { some(id) }
def_local(id, _) { some(id) }
def_arg(id, m) {
alt ty::resolved_mode(cx.tcx, m) {
by_copy | by_move { some(id) }

View File

@ -298,6 +298,15 @@ fn is_immutable_def(cx: @ctx, def: def) -> option<str> {
_ { some("upvar") }
};
}
// Note: we should *always* allow all local variables to be assigned
// here and then guarantee in the typestate pass that immutable local
// variables are assigned at most once. But this requires a new kind of
// propagation (def. not assigned), so I didn't do that.
def_local(_, false) if cx.tcx.sess.opts.enforce_mut_vars {
some("immutable local variable")
}
def_binding(_) { some("binding") }
_ { none }
}

View File

@ -957,7 +957,7 @@ fn scope_closes(sc: scope) -> option<node_id> {
fn def_is_local(d: def) -> bool {
alt d {
ast::def_arg(_, _) | ast::def_local(_) | ast::def_binding(_) |
ast::def_arg(_, _) | ast::def_local(_, _) | ast::def_binding(_) |
ast::def_upvar(_, _, _) { true }
_ { false }
}
@ -1235,7 +1235,8 @@ fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint,
&& (i < pos || j < loc_pos) {
alt lookup_in_pat(e, name, loc.node.pat) {
some(nid) {
ret some(ast::def_local(nid));
ret some(ast::def_local(nid,
loc.node.is_mutbl));
}
_ { }
}
@ -1702,7 +1703,7 @@ fn ns_for_def(d: def) -> namespace {
alt d {
ast::def_variant(_, _) { ns_val(definite_enum) }
ast::def_fn(_, _) | ast::def_self(_) |
ast::def_const(_) | ast::def_arg(_, _) | ast::def_local(_) |
ast::def_const(_) | ast::def_arg(_, _) | ast::def_local(_, _) |
ast::def_upvar(_, _, _) | ast::def_self(_) |
ast::def_class_field(_,_) | ast::def_class_method(_,_)
{ ns_val(value_or_enum) }

View File

@ -2253,7 +2253,7 @@ fn trans_local_var(cx: block, def: ast::def) -> local_var_result {
assert (cx.fcx.llargs.contains_key(nid));
ret take_local(cx.fcx.llargs, nid);
}
ast::def_local(nid) | ast::def_binding(nid) {
ast::def_local(nid, _) | ast::def_binding(nid) {
assert (cx.fcx.lllocals.contains_key(nid));
ret take_local(cx.fcx.lllocals, nid);
}

View File

@ -576,7 +576,7 @@ fn expr_to_constr_arg(tcx: ty::ctxt, e: @expr) -> @constr_arg_use {
alt e.node {
expr_path(p) {
alt tcx.def_map.find(e.id) {
some(def_local(nid)) | some(def_arg(nid, _)) |
some(def_local(nid, _)) | some(def_arg(nid, _)) |
some(def_binding(nid)) | some(def_upvar(nid, _, _)) {
ret @respan(p.span,
carg_ident({ident: p.node.idents[0], node: nid}));
@ -786,7 +786,7 @@ enum if_ty { if_check, plain_if, }
fn local_node_id_to_def_id_strict(fcx: fn_ctxt, sp: span, i: node_id) ->
def_id {
alt local_node_id_to_def(fcx, i) {
some(def_local(nid)) | some(def_arg(nid, _)) |
some(def_local(nid, _)) | some(def_arg(nid, _)) |
some(def_upvar(nid, _, _)) {
ret local_def(nid);
}
@ -810,8 +810,8 @@ fn local_node_id_to_def(fcx: fn_ctxt, i: node_id) -> option<def> {
fn local_node_id_to_def_id(fcx: fn_ctxt, i: node_id) -> option<def_id> {
alt local_node_id_to_def(fcx, i) {
some(def_local(nid)) | some(def_arg(nid, _)) | some(def_binding(nid)) |
some(def_upvar(nid, _, _)) {
some(def_local(nid, _)) | some(def_arg(nid, _)) |
some(def_binding(nid)) | some(def_upvar(nid, _, _)) {
some(local_def(nid))
}
_ { none }

View File

@ -185,7 +185,7 @@ fn clear_in_poststate_expr(fcx: fn_ctxt, e: @expr, t: poststate) {
alt vec::last(p.node.idents) {
some(i) {
alt local_node_id_to_def(fcx, e.id) {
some(def_local(nid)) {
some(def_local(nid, _)) {
clear_in_poststate_(bit_num(fcx, ninit(nid, i)), t);
}
some(_) {/* ignore args (for now...) */ }

View File

@ -176,7 +176,7 @@ fn gen_if_local(fcx: fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id,
alt node_id_to_def(fcx.ccx, new_var) {
some(d) {
alt d {
def_local(nid) {
def_local(nid, _) {
find_pre_post_expr(fcx, rhs);
let p = expr_pp(fcx.ccx, rhs);
set_pre_and_post(fcx.ccx, larger_id, p.precondition,
@ -214,7 +214,7 @@ fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr,
// pure and assign_op require the lhs to be init'd
let df = node_id_to_def_strict(fcx.ccx.tcx, lhs.id);
alt df {
def_local(nid) {
def_local(nid, _) {
let i = bit_num(fcx, ninit(nid, path_to_ident(p)));
require_and_preserve(i, expr_pp(fcx.ccx, lhs));
}
@ -259,7 +259,7 @@ fn handle_var(fcx: fn_ctxt, rslt: pre_and_post, id: node_id, name: ident) {
fn handle_var_def(fcx: fn_ctxt, rslt: pre_and_post, def: def, name: ident) {
log(debug, ("handle_var_def: ", def, name));
alt def {
def_local(nid) | def_arg(nid, _) {
def_local(nid, _) | def_arg(nid, _) {
use_var(fcx, nid);
let i = bit_num(fcx, ninit(nid, name));
require_and_preserve(i, rslt);

View File

@ -246,7 +246,7 @@ fn gen_if_local(fcx: fn_ctxt, p: poststate, e: @expr) -> bool {
alt e.node {
expr_path(pth) {
alt fcx.ccx.tcx.def_map.find(e.id) {
some(def_local(nid)) {
some(def_local(nid, _)) {
ret set_in_poststate_ident(fcx, nid, path_to_ident(pth), p);
}
_ { ret false; }

View File

@ -101,7 +101,7 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) ->
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
ret {bounds: @[], ty: typ};
}
ast::def_local(nid) {
ast::def_local(nid, _) {
assert (fcx.locals.contains_key(nid));
let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid));
ret {bounds: @[], ty: typ};

View File

@ -38,7 +38,7 @@ enum def {
def_native_mod(def_id),
def_const(def_id),
def_arg(node_id, mode),
def_local(node_id),
def_local(node_id, bool /* is_mutbl */),
def_variant(def_id /* enum */, def_id /* variant */),
def_ty(def_id),
def_prim_ty(prim_ty),
@ -194,7 +194,8 @@ enum init_op { init_assign, init_move, }
type initializer = {op: init_op, expr: @expr};
type local_ = // FIXME: should really be a refinement on pat
{ty: @ty, pat: @pat, init: option<initializer>, id: node_id};
{is_mutbl: bool, ty: @ty, pat: @pat,
init: option<initializer>, id: node_id};
type local = spanned<local_>;

View File

@ -41,7 +41,7 @@ fn def_id_of_def(d: def) -> def_id {
def_use(id) |
def_class(id) | def_class_field(_, id) | def_class_method(_, id) { id }
def_self(id) | def_arg(id, _) | def_local(id) |
def_self(id) | def_arg(id, _) | def_local(id, _) |
def_upvar(id, _, _) | def_binding(id) {
local_def(id)
}

View File

@ -532,7 +532,8 @@ fn noop_fold_path(&&p: path_, fld: ast_fold) -> path_ {
}
fn noop_fold_local(l: local_, fld: ast_fold) -> local_ {
ret {ty: fld.fold_ty(l.ty),
ret {is_mutbl: l.is_mutbl,
ty: fld.fold_ty(l.ty),
pat: fld.fold_pat(l.pat),
init:
alt l.init {

View File

@ -1346,7 +1346,7 @@ fn parse_else_expr(p: parser) -> @ast::expr {
fn parse_for_expr(p: parser) -> @ast::expr {
let lo = p.last_span.lo;
let decl = parse_local(p, false);
let decl = parse_local(p, false, false);
expect_word(p, "in");
let seq = parse_expr(p);
let body = parse_block_no_value(p);
@ -1568,24 +1568,24 @@ fn parse_pat(p: parser) -> @ast::pat {
ret @{id: p.get_id(), node: pat, span: ast_util::mk_sp(lo, hi)};
}
fn parse_local(p: parser, allow_init: bool) -> @ast::local {
fn parse_local(p: parser, is_mutbl: bool,
allow_init: bool) -> @ast::local {
let lo = p.span.lo;
let pat = parse_pat(p);
let ty = @spanned(lo, lo, ast::ty_infer);
if eat(p, token::COLON) { ty = parse_ty(p, false); }
let init = if allow_init { parse_initializer(p) } else { none };
ret @spanned(lo, p.last_span.hi,
{ty: ty, pat: pat, init: init, id: p.get_id()});
{is_mutbl: is_mutbl, ty: ty, pat: pat,
init: init, id: p.get_id()});
}
fn parse_let(p: parser) -> @ast::decl {
if eat_word(p, "mut") {
/* FIXME */
}
let is_mutbl = eat_word(p, "mut");
let lo = p.span.lo;
let locals = [parse_local(p, true)];
let locals = [parse_local(p, is_mutbl, true)];
while eat(p, token::COMMA) {
locals += [parse_local(p, true)];
locals += [parse_local(p, is_mutbl, true)];
}
ret @spanned(lo, p.last_span.hi, ast::decl_local(locals));
}

View File

@ -1,5 +1,19 @@
#!/bin/sh
function msg {
echo ""
echo ""
echo "****************************************"
echo "* Processing errors encountered *"
echo "* *"
echo "* Dummy versions of the AST encoder *"
echo "* have been left in astencode_gen.rs. *"
echo "* Fix the compilation errors and rerun *"
echo "* this script to generate the real *"
echo "* versions. *"
echo "****************************************"
}
M=src/comp/metadata
GEN_TYPES="syntax::ast::item syntax::ast::def middle::typeck::method_origin \
middle::freevars::freevar_entry syntax::ast::def_id"
@ -7,19 +21,6 @@ GEN_TYPES="syntax::ast::item syntax::ast::def middle::typeck::method_origin \
# Find serializer tool:
for S in build/*/stage1/bin/serializer; do
if [ ! -x "$S" ]; then
echo "serializer excutable not found; try 'make serializer'"
exit 1
fi
# Find rustc:
D=$(dirname "$S")
R="${D}/../../stage0/bin/rustc"
if [ ! -x "$R" ]; then
echo "rustc not found or not executable at path '$R'"
exit 1
fi
echo "Generating src/comp/metadata/astencode_gen.rs"
# First, generate dummy fns so that the compiler can type
@ -33,20 +34,25 @@ for S in build/*/stage1/bin/serializer; do
>> $M/astencode_gen.rs
done
# Find rustc and serializer:
D=$(dirname "$S")
R="${D}/../../stage0/bin/rustc"
if [ ! -x "$R" ]; then
echo "rustc not found or not executable at path '$R'"
msg
exit 1
fi
if [ ! -x "$S" ]; then
echo "serializer excutable not found; try 'make serializer'"
msg
exit 1
fi
# Generate the real code into a temporary file.
if ! "$S" src/comp/rustc.rc $GEN_TYPES > tmp.$$.rs
then
echo ""
echo ""
echo "****************************************"
echo "* Compilation errors encountered *"
echo "* *"
echo "* Dummy versions of the AST encoder *"
echo "* have been left in astencode_gen.rs. *"
echo "* Fix the compilation errors and rerun *"
echo "* this script to generate the real *"
echo "* versions. *"
echo "****************************************"
msg
rm tmp.$$.rs
exit 1
fi

View File

@ -146,7 +146,8 @@ fn build_session() -> (session::session, @mutable bool) {
no_asm_comments: false,
monomorphize: false,
inline: false,
warn_unused_imports: false
warn_unused_imports: false,
enforce_mut_vars: false
};
let codemap = codemap::new_codemap();