2 import core::{vec, option};
3 import option::{none, some};
7 import bitvectors::{bit_num, seq_preconds, seq_postconds,
9 relax_precond_block, gen};
11 import syntax::ast::*;
12 import syntax::ast_util::*;
14 import util::common::{new_def_hash, log_expr, field_exprs,
15 has_nonlocal_exits, log_stmt};
16 import syntax::codemap::span;
18 fn find_pre_post_mod(_m: _mod) -> _mod {
19 #debug("implement find_pre_post_mod!");
23 fn find_pre_post_native_mod(_m: native_mod) -> native_mod {
24 #debug("implement find_pre_post_native_mod");
28 fn find_pre_post_method(ccx: crate_ctxt, m: @method) {
29 assert (ccx.fm.contains_key(m.id));
31 {enclosing: ccx.fm.get(m.id),
35 find_pre_post_fn(fcx, m.body);
38 fn find_pre_post_item(ccx: crate_ctxt, i: item) {
42 let v: @mutable [node_id] = @mutable [];
47 {constrs: @new_def_hash::<constraint>(),
50 i_return: ninit(0, ""),
51 i_diverge: ninit(0, ""),
56 find_pre_post_expr(fake_fcx, e);
59 assert (ccx.fm.contains_key(i.id));
61 {enclosing: ccx.fm.get(i.id), id: i.id, name: i.ident, ccx: ccx};
62 find_pre_post_fn(fcx, body);
64 item_mod(m) { find_pre_post_mod(m); }
65 item_native_mod(nm) { find_pre_post_native_mod(nm); }
66 item_ty(_, _) | item_tag(_, _) | item_iface(_, _) { ret; }
67 item_res(_, _, body, dtor_id, _) {
69 {enclosing: ccx.fm.get(dtor_id),
73 find_pre_post_fn(fcx, body);
75 item_obj(o, _, _) {for m in o.methods { find_pre_post_method(ccx, m); }}
76 item_impl(_, _, _, ms) { for m in ms { find_pre_post_method(ccx, m); } }
81 /* Finds the pre and postcondition for each expr in <args>;
82 sets the precondition in a to be the result of combining
83 the preconditions for <args>, and the postcondition in a to
84 be the union of all postconditions for <args> */
85 fn find_pre_post_exprs(fcx: fn_ctxt, args: [@expr], id: node_id) {
86 if vec::len::<@expr>(args) > 0u {
87 #debug("find_pre_post_exprs: oper =");
90 fn do_one(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); }
91 for e: @expr in args { do_one(fcx, e); }
93 fn get_pp(ccx: crate_ctxt, &&e: @expr) -> pre_and_post {
96 let pps = vec::map(args, bind get_pp(fcx.ccx, _));
98 set_pre_and_post(fcx.ccx, id, seq_preconds(fcx, pps),
99 seq_postconds(fcx, vec::map(pps, get_post)));
102 fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk,
104 find_pre_post_expr(fcx, index);
105 find_pre_post_block(fcx, body);
106 pat_bindings(l.node.pat) {|p|
107 let ident = alt p.node { pat_bind(id, _) { id } };
108 let v_init = ninit(p.id, ident);
109 relax_precond_block(fcx, bit_num(fcx, v_init) as node_id, body);
110 // Hack: for-loop index variables are frequently ignored,
111 // so we pretend they're used
116 seq_preconds(fcx, [expr_pp(fcx.ccx, index), block_pp(fcx.ccx, body)]);
118 intersect_states(expr_postcond(fcx.ccx, index),
119 block_postcond(fcx.ccx, body));
120 copy_pre_post_(fcx.ccx, id, loop_precond, loop_postcond);
123 // Generates a pre/post assuming that a is the
124 // annotation for an if-expression with consequent conseq
125 // and alternative maybe_alt
126 fn join_then_else(fcx: fn_ctxt, antec: @expr, conseq: blk,
127 maybe_alt: option::t<@expr>, id: node_id, chck: if_ty) {
128 find_pre_post_expr(fcx, antec);
129 find_pre_post_block(fcx, conseq);
134 let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec);
135 gen(fcx, antec.id, c.node);
142 [expr_pp(fcx.ccx, antec),
143 block_pp(fcx.ccx, conseq)]);
144 set_pre_and_post(fcx.ccx, id, precond_res,
145 expr_poststate(fcx.ccx, antec));
149 if check = if_check, then
150 be sure that the predicate implied by antec
151 is *not* true in the alternative
153 find_pre_post_expr(fcx, altern);
154 let precond_false_case =
156 [expr_pp(fcx.ccx, antec), expr_pp(fcx.ccx, altern)]);
157 let postcond_false_case =
159 [expr_postcond(fcx.ccx, antec),
160 expr_postcond(fcx.ccx, altern)]);
162 /* Be sure to set the bit for the check condition here,
163 so that it's *not* set in the alternative. */
166 let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec);
167 gen(fcx, antec.id, c.node);
171 let precond_true_case =
173 [expr_pp(fcx.ccx, antec),
174 block_pp(fcx.ccx, conseq)]);
175 let postcond_true_case =
177 [expr_postcond(fcx.ccx, antec),
178 block_postcond(fcx.ccx, conseq)]);
181 seq_postconds(fcx, [precond_true_case, precond_false_case]);
183 intersect_states(postcond_true_case, postcond_false_case);
184 set_pre_and_post(fcx.ccx, id, precond_res, postcond_res);
189 fn gen_if_local(fcx: fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id,
190 new_var: node_id, pth: @path) {
191 alt node_id_to_def(fcx.ccx, new_var) {
195 find_pre_post_expr(fcx, rhs);
196 let p = expr_pp(fcx.ccx, rhs);
197 set_pre_and_post(fcx.ccx, larger_id, p.precondition,
200 ninit(d_id.node, path_to_ident(fcx.ccx.tcx, pth)));
202 _ { find_pre_post_exprs(fcx, [lhs, rhs], larger_id); }
205 _ { find_pre_post_exprs(fcx, [lhs, rhs], larger_id); }
209 fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr,
211 find_pre_post_expr(fcx, rhs);
214 let post = expr_postcond(fcx.ccx, parent);
215 let tmp = tritv_clone(post);
219 if is_path(rhs) { forget_in_postcond(fcx, parent.id, rhs.id); }
222 forget_in_postcond_still_init(fcx, parent.id, lhs.id);
223 forget_in_postcond_still_init(fcx, parent.id, rhs.id);
226 forget_in_postcond_still_init(fcx, parent.id, lhs.id);
229 // pure and assign_op require the lhs to be init'd
230 let df = node_id_to_def_strict(fcx.ccx.tcx, lhs.id);
235 ninit(d_id.node, path_to_ident(fcx.ccx.tcx, p)));
236 require_and_preserve(i, expr_pp(fcx.ccx, lhs));
243 gen_if_local(fcx, lhs, rhs, parent.id, lhs.id, p);
246 let d = local_node_id_to_local_def_id(fcx, lhs.id);
247 let d1 = local_node_id_to_local_def_id(fcx, rhs.id);
253 {ident: path_to_ident(fcx.ccx.tcx, p), node: id};
255 {ident: path_to_ident(fcx.ccx.tcx, p1), node: id1};
256 copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs,
265 _ {/* do nothing */ }
268 _ { find_pre_post_expr(fcx, lhs); }
272 fn handle_var(fcx: fn_ctxt, rslt: pre_and_post, id: node_id, name: ident) {
273 handle_var_def(fcx, rslt, node_id_to_def_strict(fcx.ccx.tcx, id), name);
276 fn handle_var_def(fcx: fn_ctxt, rslt: pre_and_post, def: def, name: ident) {
277 log(debug, ("handle_var_def: ", def, name));
279 def_local(d_id, _) | def_arg(d_id, _) {
280 use_var(fcx, d_id.node);
281 let i = bit_num(fcx, ninit(d_id.node, name));
282 require_and_preserve(i, rslt);
284 _ {/* nothing to check */ }
288 fn forget_args_moved_in(fcx: fn_ctxt, parent: @expr, modes: [ty::mode],
291 for mode: ty::mode in modes {
293 forget_in_postcond(fcx, parent.id, operands[i].id);
299 fn find_pre_post_expr_fn_upvars(fcx: fn_ctxt, e: @expr) {
300 let rslt = expr_pp(fcx.ccx, e);
302 for def in *freevars::get_freevars(fcx.ccx.tcx, e.id) {
303 log(debug, ("handle_var_def: def=", def));
304 handle_var_def(fcx, rslt, def.def, "upvar");
308 /* Fills in annotations as a side effect. Does not rebuild the expr */
309 fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
310 let enclosing = fcx.enclosing;
311 let num_local_vars = num_constraints(enclosing);
312 fn do_rand_(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); }
316 expr_call(operator, operands, _) {
322 find_pre_post_exprs(fcx, args, e.id);
323 /* see if the call has any constraints on its type */
324 for c: @ty::constr in constraints_expr(fcx.ccx.tcx, operator) {
326 bit_num(fcx, substitute_constr_args(fcx.ccx.tcx, args, c));
327 require(i, expr_pp(fcx.ccx, e));
330 forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
333 /* if this is a failing call, its postcondition sets everything */
334 alt controlflow_expr(fcx.ccx, operator) {
335 noreturn. { set_postcond_false(fcx.ccx, e.id); }
339 expr_vec(args, _) { find_pre_post_exprs(fcx, args, e.id); }
341 let rslt = expr_pp(fcx.ccx, e);
343 handle_var(fcx, rslt, e.id, path_to_ident(fcx.ccx.tcx, p));
345 expr_log(_, lvl, arg) {
346 find_pre_post_exprs(fcx, [lvl, arg], e.id);
348 expr_fn(_, _, _, cap_clause) {
349 find_pre_post_expr_fn_upvars(fcx, e);
351 let use_cap_item = fn@(&&cap_item: @capture_item) {
352 let d = local_node_id_to_local_def_id(fcx, cap_item.id);
353 option::may(d, { |id| use_var(fcx, id) });
355 vec::iter(cap_clause.copies, use_cap_item);
356 vec::iter(cap_clause.moves, use_cap_item);
358 vec::iter(cap_clause.moves) { |cap_item|
359 log(debug, ("forget_in_postcond: ", cap_item));
360 forget_in_postcond(fcx, e.id, cap_item.id);
363 expr_fn_block(_, _) {
364 find_pre_post_expr_fn_upvars(fcx, e);
367 find_pre_post_block(fcx, b);
368 let p = block_pp(fcx.ccx, b);
369 set_pre_and_post(fcx.ccx, e.id, p.precondition, p.postcondition);
371 expr_rec(fields, maybe_base) {
372 let es = field_exprs(fields);
373 alt maybe_base { none. {/* no-op */ } some(b) { es += [b]; } }
374 find_pre_post_exprs(fcx, es, e.id);
376 expr_tup(elts) { find_pre_post_exprs(fcx, elts, e.id); }
378 find_pre_post_expr(fcx, a);
379 copy_pre_post(fcx.ccx, e.id, a);
381 expr_move(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_move); }
382 expr_swap(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_swap); }
383 expr_assign(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_assign); }
384 expr_assign_op(_, lhs, rhs) {
385 /* Different from expr_assign in that the lhs *must*
386 already be initialized */
388 find_pre_post_exprs(fcx, [lhs, rhs], e.id);
389 forget_in_postcond_still_init(fcx, e.id, lhs.id);
391 expr_lit(_) { clear_pp(expr_pp(fcx.ccx, e)); }
392 expr_ret(maybe_val) {
395 clear_precond(fcx.ccx, e.id);
396 set_postcond_false(fcx.ccx, e.id);
399 find_pre_post_expr(fcx, ret_val);
400 set_precondition(node_id_to_ts_ann(fcx.ccx, e.id),
401 expr_precond(fcx.ccx, ret_val));
402 set_postcond_false(fcx.ccx, e.id);
407 find_pre_post_expr(fcx, val);
408 set_pre_and_post(fcx.ccx, e.id, expr_prestate(fcx.ccx, val),
409 false_postcond(num_local_vars));
411 expr_if(antec, conseq, maybe_alt) {
412 join_then_else(fcx, antec, conseq, maybe_alt, e.id, plain_if);
414 expr_ternary(_, _, _) { find_pre_post_expr(fcx, ternary_to_if(e)); }
415 expr_binary(bop, l, r) {
417 find_pre_post_expr(fcx, l);
418 find_pre_post_expr(fcx, r);
420 seq_preconds(fcx, [expr_pp(fcx.ccx, l), expr_pp(fcx.ccx, r)]);
421 set_precondition(node_id_to_ts_ann(fcx.ccx, e.id), overall_pre);
422 set_postcondition(node_id_to_ts_ann(fcx.ccx, e.id),
423 expr_postcond(fcx.ccx, l));
424 } else { find_pre_post_exprs(fcx, [l, r], e.id); }
426 expr_unary(_, operand) {
427 find_pre_post_expr(fcx, operand);
428 copy_pre_post(fcx.ccx, e.id, operand);
430 expr_cast(operand, _) {
431 find_pre_post_expr(fcx, operand);
432 copy_pre_post(fcx.ccx, e.id, operand);
434 expr_while(test, body) {
435 find_pre_post_expr(fcx, test);
436 find_pre_post_block(fcx, body);
437 set_pre_and_post(fcx.ccx, e.id,
439 [expr_pp(fcx.ccx, test),
440 block_pp(fcx.ccx, body)]),
441 intersect_states(expr_postcond(fcx.ccx, test),
442 block_postcond(fcx.ccx, body)));
444 expr_do_while(body, test) {
445 find_pre_post_block(fcx, body);
446 find_pre_post_expr(fcx, test);
449 [block_postcond(fcx.ccx, body),
450 expr_postcond(fcx.ccx, test)]);
451 /* conservative approximation: if the body
452 could break or cont, the test may never be executed */
454 if has_nonlocal_exits(body) {
455 loop_postcond = empty_poststate(num_local_vars);
457 set_pre_and_post(fcx.ccx, e.id,
459 [block_pp(fcx.ccx, body),
460 expr_pp(fcx.ccx, test)]),
463 expr_for(d, index, body) {
464 find_pre_post_loop(fcx, d, index, body, e.id);
466 expr_index(val, sub) { find_pre_post_exprs(fcx, [val, sub], e.id); }
468 find_pre_post_expr(fcx, ex);
469 fn do_an_alt(fcx: fn_ctxt, an_alt: arm) -> pre_and_post {
471 some(e) { find_pre_post_expr(fcx, e); }
474 find_pre_post_block(fcx, an_alt.body);
475 ret block_pp(fcx.ccx, an_alt.body);
478 for a: arm in alts { alt_pps += [do_an_alt(fcx, a)]; }
479 fn combine_pp(antec: pre_and_post, fcx: fn_ctxt, &&pp: pre_and_post,
480 &&next: pre_and_post) -> pre_and_post {
481 union(pp.precondition, seq_preconds(fcx, [antec, next]));
482 intersect(pp.postcondition, next.postcondition);
485 let antec_pp = pp_clone(expr_pp(fcx.ccx, ex));
487 @{precondition: empty_prestate(num_local_vars),
488 postcondition: false_postcond(num_local_vars)};
489 let g = bind combine_pp(antec_pp, fcx, _, _);
490 let alts_overall_pp =
491 vec::foldl(e_pp, alt_pps, g);
492 set_pre_and_post(fcx.ccx, e.id, alts_overall_pp.precondition,
493 alts_overall_pp.postcondition);
495 expr_field(operator, _, _) {
496 find_pre_post_expr(fcx, operator);
497 copy_pre_post(fcx.ccx, e.id, operator);
499 expr_fail(maybe_val) {
502 none. { prestate = empty_prestate(num_local_vars); }
504 find_pre_post_expr(fcx, fail_val);
505 prestate = expr_precond(fcx.ccx, fail_val);
508 set_pre_and_post(fcx.ccx, e.id,
509 /* if execution continues after fail,
510 then everything is true! */
511 prestate, false_postcond(num_local_vars));
514 find_pre_post_expr(fcx, p);
515 copy_pre_post(fcx.ccx, e.id, p);
518 find_pre_post_expr(fcx, p);
519 copy_pre_post(fcx.ccx, e.id, p);
520 /* predicate p holds after this expression executes */
522 let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p);
523 gen(fcx, e.id, c.node);
525 expr_if_check(p, conseq, maybe_alt) {
526 join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check);
534 expr_bind(operator, maybe_args) {
536 let cmodes = callee_modes(fcx, operator.id);
539 for expr_opt: option::t<@expr> in maybe_args {
542 some(expr) { modes += [cmodes[i]]; args += [expr]; }
546 args += [operator]; /* ??? order of eval? */
547 forget_args_moved_in(fcx, e, modes, args);
548 find_pre_post_exprs(fcx, args, e.id);
550 expr_break. { clear_pp(expr_pp(fcx.ccx, e)); }
551 expr_cont. { clear_pp(expr_pp(fcx.ccx, e)); }
552 expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); }
553 expr_anon_obj(anon_obj) {
554 alt anon_obj.inner_obj {
556 find_pre_post_expr(fcx, ex);
557 copy_pre_post(fcx.ccx, e.id, ex);
559 none. { clear_pp(expr_pp(fcx.ccx, e)); }
565 fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
569 stmt_decl(adecl, id) {
571 decl_local(alocals) {
573 let prev_pp = empty_pre_post(num_constraints(fcx.enclosing));
574 for (_, alocal) in alocals {
575 alt alocal.node.init {
577 /* LHS always becomes initialized,
578 whether or not this is a move */
579 find_pre_post_expr(fcx, an_init.expr);
580 pat_bindings(alocal.node.pat) {|p|
581 copy_pre_post(fcx.ccx, p.id, an_init.expr);
583 /* Inherit ann from initializer, and add var being
584 initialized to the postcondition */
585 copy_pre_post(fcx.ccx, id, an_init.expr);
588 alt an_init.expr.node {
589 expr_path(_p) { p = some(_p); }
593 pat_bindings(alocal.node.pat) {|pat|
594 /* FIXME: This won't be necessary when typestate
595 works well enough for pat_bindings to return a
596 refinement-typed thing. */
597 let ident = alt pat.node { pat_bind(n, _) { n } };
600 copy_in_postcond(fcx, id,
601 {ident: ident, node: pat.id},
603 path_to_ident(fcx.ccx.tcx,
605 node: an_init.expr.id},
606 op_to_oper_ty(an_init.op));
610 gen(fcx, id, ninit(pat.id, ident));
613 if an_init.op == init_move && is_path(an_init.expr) {
614 forget_in_postcond(fcx, id, an_init.expr.id);
617 /* Clear out anything that the previous initializer
619 e_pp = expr_pp(fcx.ccx, an_init.expr);
620 tritv_copy(prev_pp.precondition,
621 seq_preconds(fcx, [prev_pp, e_pp]));
622 /* Include the LHSs too, since those aren't in the
623 postconds of the RHSs themselves */
624 pat_bindings(alocal.node.pat) {|pat|
627 set_in_postcond(bit_num(fcx, ninit(pat.id, n)),
632 copy_pre_post_(fcx.ccx, id, prev_pp.precondition,
633 prev_pp.postcondition);
636 pat_bindings(alocal.node.pat) {|p|
637 clear_pp(node_id_to_ts_ann(fcx.ccx, p.id).conditions);
639 clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions);
645 clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions);
646 find_pre_post_item(fcx.ccx, *anitem);
650 stmt_expr(e, id) | stmt_semi(e, id) {
651 find_pre_post_expr(fcx, e);
652 copy_pre_post(fcx.ccx, id, e);
657 fn find_pre_post_block(fcx: fn_ctxt, b: blk) {
658 /* Want to say that if there is a break or cont in this
659 block, then that invalidates the poststate upheld by
660 any of the stmts after it.
661 Given that the typechecker has run, we know any break will be in
662 a block that forms a loop body. So that's ok. There'll never be an
663 expr_break outside a loop body, therefore, no expr_break outside a block.
666 /* Conservative approximation for now: This says that if a block contains
667 *any* breaks or conts, then its postcondition doesn't promise anything.
672 won't have a postcondition that says x is initialized, but that's ok.
675 let nv = num_constraints(fcx.enclosing);
676 fn do_one_(fcx: fn_ctxt, s: @stmt) {
677 find_pre_post_stmt(fcx, *s);
679 #error("pre_post for stmt:");
682 log_pp_err(stmt_pp(fcx.ccx, *s));
685 for s: @stmt in b.node.stmts { do_one_(fcx, s); }
686 fn do_inner_(fcx: fn_ctxt, &&e: @expr) { find_pre_post_expr(fcx, e); }
687 let do_inner = bind do_inner_(fcx, _);
688 option::map::<@expr, ()>(b.node.expr, do_inner);
690 let pps: [pre_and_post] = [];
691 for s: @stmt in b.node.stmts { pps += [stmt_pp(fcx.ccx, *s)]; }
694 some(e) { pps += [expr_pp(fcx.ccx, e)]; }
697 let block_precond = seq_preconds(fcx, pps);
700 for pp: pre_and_post in pps { postconds += [get_post(pp)]; }
702 /* A block may be empty, so this next line ensures that the postconds
703 vector is non-empty. */
704 postconds += [block_precond];
706 let block_postcond = empty_poststate(nv);
707 /* conservative approximation */
709 if !has_nonlocal_exits(b) {
710 block_postcond = seq_postconds(fcx, postconds);
712 set_pre_and_post(fcx.ccx, b.node.id, block_precond, block_postcond);
715 fn find_pre_post_fn(fcx: fn_ctxt, body: blk) {
717 use_var(fcx, tsconstr_to_node_id(fcx.enclosing.i_return));
718 use_var(fcx, tsconstr_to_node_id(fcx.enclosing.i_diverge));
720 find_pre_post_block(fcx, body);
722 // Treat the tail expression as a return statement
724 some(tailexpr) { set_postcond_false(fcx.ccx, tailexpr.id); }
725 none. {/* fallthrough */ }
729 fn fn_pre_post(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
731 ccx: crate_ctxt, v: visit::vt<crate_ctxt>) {
732 visit::visit_fn(fk, decl, body, sp, id, ccx, v);
733 assert (ccx.fm.contains_key(id));
735 {enclosing: ccx.fm.get(id),
737 name: visit::name_of_fn(fk),
739 find_pre_post_fn(fcx, body);
746 // indent-tabs-mode: nil
748 // buffer-file-coding-system: utf-8-unix