Forbid deinitializing upvars in typestate

Closes #1965.
This commit is contained in:
Tim Chevalier 2012-03-17 14:39:15 -07:00
parent 46e358f012
commit 4f4a468e84
3 changed files with 48 additions and 15 deletions

View file

@ -710,6 +710,12 @@ fn find_instance_(pattern: [constr_arg_general_<inst>], descs: [pred_args]) ->
}
type inst = {ident: ident, node: node_id};
enum dest {
local_dest(inst), // RHS is assigned to a local variable
call // RHS is passed to a function
}
type subst = [{from: inst, to: inst}];
fn find_instances(_fcx: fn_ctxt, subst: subst, c: constraint) ->
@ -1064,12 +1070,12 @@ fn ast_constr_to_sp_constr(tcx: ty::ctxt, args: [arg], c: @constr) ->
ret respan(c.span, tconstr);
}
type binding = {lhs: [inst], rhs: option<initializer>};
type binding = {lhs: [dest], rhs: option<initializer>};
fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding {
let mut lhs = [];
pat_bindings(tcx.def_map, loc.node.pat) {|p_id, _s, name|
lhs += [{ident: path_to_ident(name), node: p_id}];
lhs += [local_dest({ident: path_to_ident(name), node: p_id})];
};
{lhs: lhs, rhs: loc.node.init}
}
@ -1106,12 +1112,12 @@ fn callee_arg_init_ops(fcx: fn_ctxt, callee: node_id) -> [init_op] {
}
}
fn anon_bindings(ops: [init_op], es: [@expr]) -> [binding] {
fn arg_bindings(ops: [init_op], es: [@expr]) -> [binding] {
let mut bindings: [binding] = [];
let mut i = 0;
let mut i = 0u;
for ops.each {|op|
bindings += [{lhs: [], rhs: some({op: op, expr: es[i]})}];
i += 1;
bindings += [{lhs: [call], rhs: some({op: op, expr: es[i]})}];
i += 1u;
}
ret bindings;
}

View file

@ -13,6 +13,9 @@ import driver::session::session;
import std::map::hashmap;
fn forbid_upvar(fcx: fn_ctxt, rhs_id: node_id, sp: span, t: oper_type) {
// fcx.ccx.tcx.sess.span_note(sp,
// #fmt("forbid_upvar: checking. %?", t));
alt t {
oper_move {
alt local_node_id_to_def(fcx, rhs_id) {
@ -29,7 +32,7 @@ fn forbid_upvar(fcx: fn_ctxt, rhs_id: node_id, sp: span, t: oper_type) {
}
fn handle_move_or_copy(fcx: fn_ctxt, post: poststate, rhs_path: @path,
rhs_id: node_id, instlhs: inst, init_op: init_op) {
rhs_id: node_id, destlhs: dest, init_op: init_op) {
forbid_upvar(fcx, rhs_id, rhs_path.span, op_to_oper_ty(init_op));
let rhs_d_id = local_node_id_to_def_id(fcx, rhs_id);
@ -38,8 +41,13 @@ fn handle_move_or_copy(fcx: fn_ctxt, post: poststate, rhs_path: @path,
// RHS is a local var
let instrhs =
{ident: path_to_ident(rhs_path), node: rhsid.node};
copy_in_poststate(fcx, post, instlhs, instrhs,
op_to_oper_ty(init_op));
alt destlhs {
local_dest(instlhs) {
copy_in_poststate(fcx, post, instlhs, instrhs,
op_to_oper_ty(init_op));
}
_ {}
}
}
_ {
// not a local -- do nothing
@ -74,15 +82,20 @@ fn seq_states(fcx: fn_ctxt, pres: prestate, bindings: [binding]) ->
changed |=
find_pre_post_state_expr(fcx, post, an_init.expr) || changed;
post = tritv_clone(expr_poststate(fcx.ccx, an_init.expr));
for b.lhs.each {|i|
for b.lhs.each {|d|
alt an_init.expr.node {
expr_path(p) {
handle_move_or_copy(fcx, post, p, an_init.expr.id, i,
handle_move_or_copy(fcx, post, p, an_init.expr.id, d,
an_init.op);
}
_ { }
}
set_in_poststate_ident(fcx, i.node, i.ident, post);
alt d {
local_dest(i) {
set_in_poststate_ident(fcx, i.node, i.ident, post);
}
_ {}
}
}
// Forget the RHS if we just moved it.
@ -91,9 +104,14 @@ fn seq_states(fcx: fn_ctxt, pres: prestate, bindings: [binding]) ->
}
}
none {
for b.lhs.each {|i|
for b.lhs.each {|d|
// variables w/o an initializer
clear_in_poststate_ident_(fcx, i.node, i.ident, post);
alt check d {
// would be an error to pass something uninit'd to a call
local_dest(i) {
clear_in_poststate_ident_(fcx, i.node, i.ident, post);
}
}
}
}
}
@ -198,7 +216,7 @@ fn find_pre_post_state_call(fcx: fn_ctxt, pres: prestate, a: @expr,
fn find_pre_post_state_exprs(fcx: fn_ctxt, pres: prestate, id: node_id,
ops: [init_op], es: [@expr], cf: ret_style) ->
bool {
let rs = seq_states(fcx, pres, anon_bindings(ops, es));
let rs = seq_states(fcx, pres, arg_bindings(ops, es));
let mut changed = rs.changed | set_prestate_ann(fcx.ccx, id, pres);
/* if this is a failing call, it sets everything as initialized */
alt cf {
@ -371,6 +389,8 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
return_val);
}
expr_call(operator, operands, _) {
#debug("hey it's a call");
log_expr(*e);
ret find_pre_post_state_call(fcx, pres, operator, e.id,
callee_arg_init_ops(fcx, operator.id),
operands,

View file

@ -0,0 +1,7 @@
// error-pattern:tried to deinitialize a variable declared in a different
fn test(-x: uint) {}
fn main() {
let i = 3u;
uint::range(0u, 10u) {|_x| test(i)}
}