Skip to content

Commit 287d493

Browse files
committed
---
yaml --- r: 4317 b: refs/heads/master c: 5cf5f50 h: refs/heads/master i: 4315: 573056b v: v3
1 parent a8ff68e commit 287d493

File tree

7 files changed

+118
-60
lines changed

7 files changed

+118
-60
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
---
2-
refs/heads/master: c9b16ac4c28ab5696d318dc7e414c73e27f9d631
2+
refs/heads/master: 5cf5f5024d5efcbb0b399433fa2117e1146292c2

trunk/src/comp/middle/tstate/auxiliary.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -238,21 +238,58 @@ type norm_constraint = {bit_num: uint, c: sp_constr};
238238

239239
type constr_map = @std::map::hashmap[def_id, constraint];
240240

241+
/* Contains stuff that has to be computed up front */
241242
type fn_info =
242-
/* list, accumulated during pre/postcondition
243-
computation, of all local variables that may be
244-
used*/
245-
// Doesn't seem to work without the @ --
246-
// bug?
247243
{constrs: constr_map,
248244
num_constraints: uint,
249245
cf: controlflow,
246+
/* For easy access, the fn_info stores two special constraints for each
247+
function. i_return holds if all control paths in this function terminate
248+
in either a return expression, or an appropriate tail expression.
249+
i_diverge holds if all control paths in this function terminate in a fail
250+
or diverging call.
251+
252+
It might be tempting to use a single constraint C for both properties,
253+
where C represents i_return and !C represents i_diverge. This is
254+
inadvisable, because then the sense of the bit depends on context. If we're
255+
inside a ! function, that reverses the sense of the bit: C would be
256+
i_diverge and !C would be i_return. That's awkward, because we have to
257+
pass extra context around to functions that shouldn't care.
258+
259+
Okay, suppose C represents i_return and !C represents i_diverge, regardless
260+
of context. Consider this code:
261+
262+
if (foo) { ret; } else { fail; }
263+
264+
C is true in the consequent and false in the alternative. What's T `join`
265+
F, then? ? doesn't work, because this code should definitely-return if the
266+
context is a returning function (and be definitely-rejected if the context
267+
is a ! function). F doesn't work, because then the code gets incorrectly
268+
rejected if the context is a returning function. T would work, but it
269+
doesn't make sense for T `join` F to be T (consider init constraints, for
270+
example).;
271+
272+
So we need context. And so it seems clearer to just have separate
273+
constraints.
274+
*/
275+
i_return: tsconstr,
276+
i_diverge: tsconstr,
277+
/* list, accumulated during pre/postcondition
278+
computation, of all local variables that may be
279+
used */
280+
// Doesn't seem to work without the @ -- bug
250281
used_vars: @mutable node_id[]};
251282

252283
fn tsconstr_to_def_id(t: &tsconstr) -> def_id {
253284
alt t { ninit(id, _) { local_def(id) } npred(_, id, _) { id } }
254285
}
255286

287+
fn tsconstr_to_node_id(t: &tsconstr) -> node_id {
288+
alt t { ninit(id, _) { id }
289+
npred(_, id, _) {
290+
fail "tsconstr_to_node_id called on pred constraint" } }
291+
}
292+
256293
/* mapping from node ID to typestate annotation */
257294
type node_ann_table = @mutable ts_ann[mutable ];
258295

@@ -261,7 +298,10 @@ type node_ann_table = @mutable ts_ann[mutable ];
261298
type fn_info_map = @std::map::hashmap[node_id, fn_info];
262299

263300
type fn_ctxt =
264-
{enclosing: fn_info, id: node_id, name: ident, ccx: crate_ctxt};
301+
{enclosing: fn_info,
302+
id: node_id,
303+
name: ident,
304+
ccx: crate_ctxt};
265305

266306
type crate_ctxt = {tcx: ty::ctxt, node_anns: node_ann_table, fm: fn_info_map};
267307

trunk/src/comp/middle/tstate/ck.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import std::option::none;
4040
import aux::*;
4141
import syntax::print::pprust::ty_to_str;
4242
import util::common::log_stmt_err;
43-
import bitvectors::promises;
43+
import bitvectors::*;
4444
import annotate::annotate_crate;
4545
import collect_locals::mk_f_to_fn_info;
4646
import pre_post_conditions::fn_pre_post;
@@ -141,24 +141,23 @@ fn check_states_against_conditions(fcx: &fn_ctxt, f: &_fn,
141141

142142
/* Check that the return value is initialized */
143143
let post = aux::block_poststate(fcx.ccx, f.body);
144-
let ret_c: tsconstr = ninit(fcx.id, fcx.name);
145-
if f.proto == ast::proto_fn && !promises(fcx, post, ret_c) &&
146-
!type_is_nil(fcx.ccx.tcx, ret_ty_of_fn(fcx.ccx.tcx, id)) &&
147-
f.decl.cf == return {
148-
fcx.ccx.tcx.sess.span_note(f.body.span,
144+
if f.proto == ast::proto_fn &&
145+
!promises(fcx, post, fcx.enclosing.i_return) &&
146+
!type_is_nil(fcx.ccx.tcx, ret_ty_of_fn(fcx.ccx.tcx, id)) &&
147+
f.decl.cf == return {
148+
fcx.ccx.tcx.sess.span_err(f.body.span,
149149
"In function " + fcx.name +
150150
", not all control paths \
151151
return a value");
152152
fcx.ccx.tcx.sess.span_fatal(f.decl.output.span,
153153
"see declared return type of '" +
154154
ty_to_str(*f.decl.output) + "'");
155155
} else if (f.decl.cf == noreturn) {
156-
157-
158156
// check that this really always fails
159-
// the fcx.id bit means "returns" for a returning fn,
160-
// "diverges" for a non-returning fn
161-
if !promises(fcx, post, ret_c) {
157+
// Note that it's ok for i_diverge and i_return to both be true.
158+
// In fact, i_diverge implies i_return. (But not vice versa!)
159+
160+
if !promises(fcx, post, fcx.enclosing.i_diverge) {
162161
fcx.ccx.tcx.sess.span_fatal(f.body.span,
163162
"In non-returning function " +
164163
fcx.name +

trunk/src/comp/middle/tstate/collect_locals.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,28 @@ fn mk_fn_info(ccx: &crate_ctxt, f: &_fn, tp: &ty_param[], f_sp: &span,
109109
next = add_constraint(cx.tcx, sc, next, res_map);
110110
}
111111

112-
/* add a pseudo-entry for the function's return value
113-
we can safely use the function's name itself for this purpose */
112+
/* add the special i_diverge and i_return constraints
113+
(see the type definition for auxiliary::fn_info for an explanation) */
114+
115+
// use the name of the function for the "return" constraint
116+
next = add_constraint(cx.tcx, respan(f_sp, ninit(id, name)), next,
117+
res_map);
118+
// and the name of the function, with a '!' appended to it, for the
119+
// "diverges" constraint
120+
let diverges_id = ccx.tcx.sess.next_node_id();
121+
let diverges_name = name + "!";
122+
add_constraint(cx.tcx, respan(f_sp, ninit(diverges_id, diverges_name)),
123+
next, res_map);
114124

115-
add_constraint(cx.tcx, respan(f_sp, ninit(id, name)), next, res_map);
116125
let v: @mutable node_id[] = @mutable ~[];
117126
let rslt =
118127
{constrs: res_map,
119128
num_constraints:
120-
ivec::len(*cx.cs) + ivec::len(f.decl.constraints) + 1u,
129+
// add 2 to account for the i_return and i_diverge constraints
130+
ivec::len(*cx.cs) + ivec::len(f.decl.constraints) + 2u,
121131
cf: f.decl.cf,
132+
i_return: ninit(id, name),
133+
i_diverge: ninit(diverges_id, diverges_name),
122134
used_vars: v};
123135
ccx.fm.insert(id, rslt);
124136
log name + " has " + std::uint::str(num_constraints(rslt)) +

trunk/src/comp/middle/tstate/pre_post_conditions.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ fn find_pre_post_item(ccx: &crate_ctxt, i: &item) {
7979
{constrs: @new_def_hash[constraint](),
8080
num_constraints: 0u,
8181
cf: return,
82+
// just bogus
83+
i_return: ninit(0, ""),
84+
i_diverge: ninit(0, ""),
8285
used_vars: v},
8386
id: 0,
8487
name: "",
@@ -692,7 +695,8 @@ fn find_pre_post_block(fcx: &fn_ctxt, b: blk) {
692695

693696
fn find_pre_post_fn(fcx: &fn_ctxt, f: &_fn) {
694697
// hack
695-
use_var(fcx, fcx.id);
698+
use_var(fcx, tsconstr_to_node_id(fcx.enclosing.i_return));
699+
use_var(fcx, tsconstr_to_node_id(fcx.enclosing.i_diverge));
696700

697701
find_pre_post_block(fcx, f.body);
698702

trunk/src/comp/middle/tstate/states.rs

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,13 @@
11
import syntax::print::pprust::path_to_str;
2+
import util::ppaux::ty_to_str;
23
import std::ivec;
34
import std::option;
45
import std::option::get;
56
import std::option::is_none;
67
import std::option::none;
78
import std::option::some;
89
import std::option::maybe;
9-
import tstate::ann::set_in_poststate_;
10-
import tstate::ann::pre_and_post;
11-
import tstate::ann::get_post;
12-
import tstate::ann::postcond;
13-
import tstate::ann::empty_pre_post;
14-
import tstate::ann::empty_poststate;
15-
import tstate::ann::clear_in_poststate;
16-
import tstate::ann::intersect;
17-
import tstate::ann::empty_prestate;
18-
import tstate::ann::prestate;
19-
import tstate::ann::poststate;
20-
import tstate::ann::false_postcond;
21-
import tstate::ann::ts_ann;
22-
import tstate::ann::set_prestate;
23-
import tstate::ann::set_poststate;
10+
import ann::*;
2411
import aux::*;
2512
import tritv::tritv_clone;
2613
import tritv::tritv_set;
@@ -189,9 +176,8 @@ fn find_pre_post_state_exprs(fcx: &fn_ctxt, pres: &prestate, id: node_id,
189176
/* if this is a failing call, it sets everything as initialized */
190177
alt cf {
191178
noreturn. {
192-
changed |=
193-
set_poststate_ann(fcx.ccx, id,
194-
false_postcond(num_constraints(fcx.enclosing)));
179+
let post = false_postcond(num_constraints(fcx.enclosing));
180+
changed |= set_poststate_ann(fcx.ccx, id, post);
195181
}
196182
_ { changed |= set_poststate_ann(fcx.ccx, id, rs.post); }
197183
}
@@ -403,15 +389,14 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
403389
/* normally, everything is true if execution continues after
404390
a ret expression (since execution never continues locally
405391
after a ret expression */
392+
// FIXME should factor this out
393+
let post = false_postcond(num_constrs);
394+
// except for the "diverges" bit...
395+
kill_poststate_(fcx, fcx.enclosing.i_diverge, post);
406396

407-
set_poststate_ann(fcx.ccx, e.id, false_postcond(num_constrs));
408-
/* return from an always-failing function clears the return bit */
397+
set_poststate_ann(fcx.ccx, e.id, post);
409398

410-
alt fcx.enclosing.cf {
411-
noreturn. { kill_poststate(fcx, e.id, ninit(fcx.id, fcx.name)); }
412-
_ { }
413-
}
414-
alt maybe_ret_val {
399+
alt maybe_ret_val {
415400
none. {/* do nothing */ }
416401
some(ret_val) {
417402
changed |= find_pre_post_state_expr(fcx, pres, ret_val);
@@ -421,7 +406,10 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
421406
}
422407
expr_be(val) {
423408
let changed = set_prestate_ann(fcx.ccx, e.id, pres);
424-
set_poststate_ann(fcx.ccx, e.id, false_postcond(num_constrs));
409+
let post = false_postcond(num_constrs);
410+
// except for the "diverges" bit...
411+
kill_poststate_(fcx, fcx.enclosing.i_diverge, post);
412+
set_poststate_ann(fcx.ccx, e.id, post);
425413
ret changed | find_pre_post_state_expr(fcx, pres, val);
426414
}
427415
expr_if(antec, conseq, maybe_alt) {
@@ -558,12 +546,20 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
558546
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
559547
}
560548
expr_fail(maybe_fail_val) {
549+
// FIXME Should factor out this code,
550+
// which also appears in find_pre_post_state_exprs
551+
/* if execution continues after fail, then everything is true!
552+
woo! */
553+
let post = false_postcond(num_constrs);
554+
alt fcx.enclosing.cf {
555+
noreturn. {
556+
kill_poststate_(fcx, ninit(fcx.id, fcx.name), post);
557+
}
558+
_ {}
559+
}
561560
ret set_prestate_ann(fcx.ccx, e.id, pres) |
562-
/* if execution continues after fail, then everything is true!
563-
woo! */
564-
set_poststate_ann(fcx.ccx, e.id, false_postcond(num_constrs))
565-
|
566-
alt maybe_fail_val {
561+
set_poststate_ann(fcx.ccx, e.id, post)
562+
| alt maybe_fail_val {
567563
none. { false }
568564
some(fail_val) {
569565
find_pre_post_state_expr(fcx, pres, fail_val)
@@ -710,9 +706,11 @@ fn find_pre_post_state_block(fcx: &fn_ctxt, pres0: &prestate, b: &blk) ->
710706

711707
fn find_pre_post_state_fn(fcx: &fn_ctxt, f: &_fn) -> bool {
712708

713-
let num_local_vars = num_constraints(fcx.enclosing);
714-
// make sure the return bit starts out False
715-
clear_in_prestate_ident(fcx, fcx.id, fcx.name, f.body.node.id);
709+
let num_constrs = num_constraints(fcx.enclosing);
710+
// make sure the return and diverge bits start out False
711+
kill_prestate(fcx, f.body.node.id, fcx.enclosing.i_return);
712+
kill_prestate(fcx, f.body.node.id, fcx.enclosing.i_diverge);
713+
716714
// Instantiate any constraints on the arguments so we can use them
717715
let block_pre = block_prestate(fcx.ccx, f.body);
718716
let tsc;
@@ -728,15 +726,16 @@ fn find_pre_post_state_fn(fcx: &fn_ctxt, f: &_fn) -> bool {
728726
some(tailexpr) {
729727
let tailty = expr_ty(fcx.ccx.tcx, tailexpr);
730728

731-
732729
// Since blocks and alts and ifs that don't have results
733730
// implicitly result in nil, we have to be careful to not
734731
// interpret nil-typed block results as the result of a
735732
// function with some other return type
736733
if !type_is_nil(fcx.ccx.tcx, tailty) &&
737734
!type_is_bot(fcx.ccx.tcx, tailty) {
738-
let p = false_postcond(num_local_vars);
739-
set_poststate_ann(fcx.ccx, f.body.node.id, p);
735+
let post = false_postcond(num_constrs);
736+
// except for the "diverges" bit...
737+
kill_poststate_(fcx, fcx.enclosing.i_diverge, post);
738+
set_poststate_ann(fcx.ccx, f.body.node.id, post);
740739
}
741740
}
742741
none. {/* fallthrough */ }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// xfail-stage0
2+
// error-pattern: some control paths may return
3+
fn f() -> ! { 3 }
4+
fn main(){}

0 commit comments

Comments
 (0)