Skip to content

Typestate #337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
658 changes: 534 additions & 124 deletions src/comp/middle/typestate_check.rs

Large diffs are not rendered by default.

106 changes: 97 additions & 9 deletions src/comp/util/common.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import std.map;
import std.map.hashmap;
import std._uint;
import std._int;
import std._vec;
import std.option.none;
import front.ast;
import util.typestate_ann.ts_ann;

import middle.fold;
import middle.fold.respan;

import std.io.stdout;
import std.io.str_writer;
import std.io.string_writer;
Expand All @@ -16,6 +21,7 @@ import pretty.pp.mkstate;
type filename = str;
type span = rec(uint lo, uint hi);
type spanned[T] = rec(T node, span span);
type flag = hashmap[str, ()];

tag ty_mach {
ty_i8;
Expand Down Expand Up @@ -98,9 +104,9 @@ fn uistr(uint i) -> str {

fn elt_expr(&ast.elt e) -> @ast.expr { ret e.expr; }

fn elt_exprs(vec[ast.elt] elts) -> vec[@ast.expr] {
fn elt_exprs(&vec[ast.elt] elts) -> vec[@ast.expr] {
auto f = elt_expr;
be _vec.map[ast.elt, @ast.expr](f, elts);
ret _vec.map[ast.elt, @ast.expr](f, elts);
}

fn field_expr(&ast.field f) -> @ast.expr { ret f.expr; }
Expand All @@ -115,26 +121,41 @@ fn plain_ann() -> ast.ann {
none[vec[@middle.ty.t]], none[@ts_ann]);
}

fn log_expr(&ast.expr e) -> () {
fn expr_to_str(&ast.expr e) -> str {
let str_writer s = string_writer();
auto out_ = mkstate(s.get_writer(), 80u);
auto out = @rec(s=out_,
comments=none[vec[front.lexer.cmnt]],
mutable cur_cmnt=0u);

print_expr(out, @e);
log(s.get_str());
ret s.get_str();
}

fn log_block(&ast.block b) -> () {
fn log_expr(&ast.expr e) -> () {
log(expr_to_str(e));
}

fn log_expr_err(&ast.expr e) -> () {
log_err(expr_to_str(e));
}

fn block_to_str(&ast.block b) -> str {
let str_writer s = string_writer();
auto out_ = mkstate(s.get_writer(), 80u);
auto out = @rec(s=out_,
comments=none[vec[front.lexer.cmnt]],
mutable cur_cmnt=0u);

print_block(out, b);
log(s.get_str());
ret s.get_str();
}

fn log_block(&ast.block b) -> () {
log(block_to_str(b));
}

fn log_block_err(&ast.block b) -> () {
log_err(block_to_str(b));
}

fn log_ann(&ast.ann a) -> () {
Expand All @@ -148,7 +169,7 @@ fn log_ann(&ast.ann a) -> () {
}
}

fn log_stmt(ast.stmt st) -> () {
fn stmt_to_str(&ast.stmt st) -> str {
let str_writer s = string_writer();
auto out_ = mkstate(s.get_writer(), 80u);
auto out = @rec(s=out_,
Expand All @@ -163,9 +184,76 @@ fn log_stmt(ast.stmt st) -> () {
}
case (_) { /* do nothing */ }
}
log(s.get_str());
ret s.get_str();
}

fn log_stmt(&ast.stmt st) -> () {
log(stmt_to_str(st));
}

fn log_stmt_err(&ast.stmt st) -> () {
log_err(stmt_to_str(st));
}

fn decl_lhs(@ast.decl d) -> ast.def_id {
alt (d.node) {
case (ast.decl_local(?l)) {
ret l.id;
}
case (ast.decl_item(?an_item)) {
alt (an_item.node) {
case (ast.item_const(_,_,_,?d,_)) {
ret d;
}
case (ast.item_fn(_,_,_,?d,_)) {
ret d;
}
case (ast.item_mod(_,_,?d)) {
ret d;
}
case (ast.item_native_mod(_,_,?d)) {
ret d;
}
case (ast.item_ty(_,_,_,?d,_)) {
ret d;
}
case (ast.item_tag(_,_,_,?d,_)) {
ret d;
}
case (ast.item_obj(_,_,_,?d,_)) {
ret d.ctor; /* This doesn't really make sense */
}
}
}
}
}

fn has_nonlocal_exits(&ast.block b) -> bool {
/* overkill, but just passing around a mutable bool doesn't seem
to work in rustboot */
auto has_exits = new_str_hash[()]();

fn set_break(&flag f, &span sp, ast.ann a) -> @ast.expr {
f.insert("foo", ());
ret @respan(sp, ast.expr_break(a));
}
fn set_cont(&flag f, &span sp, ast.ann a) -> @ast.expr {
f.insert("foo", ());
ret @respan(sp, ast.expr_cont(a));
}
fn check_b(&flag f) -> bool {
ret (f.size() == 0u);
}

auto fld0 = fold.new_identity_fold[flag]();

fld0 = @rec(fold_expr_break = bind set_break(_,_,_),
fold_expr_cont = bind set_cont(_,_,_),
keep_going = bind check_b(_) with *fld0);
fold.fold_block[flag](has_exits, fld0, b);

ret (has_exits.size() > 0u);
}
//
// Local Variables:
// mode: rust
Expand Down
21 changes: 18 additions & 3 deletions src/comp/util/typestate_ann.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ fn empty_poststate(uint num_vars) -> poststate {
be true_precond(num_vars);
}

fn false_postcond(uint num_vars) -> postcond {
be bitv.create(num_vars, true);
}

fn empty_pre_post(uint num_vars) -> pre_and_post {
ret(rec(precondition=empty_prestate(num_vars),
postcondition=empty_poststate(num_vars)));
Expand Down Expand Up @@ -119,7 +123,7 @@ fn set_postcondition(&ts_ann a, &postcond p) -> () {

// Sets all the bits in a's prestate to equal the
// corresponding bit in p's prestate.
fn set_prestate(&ts_ann a, &prestate p) -> bool {
fn set_prestate(@ts_ann a, &prestate p) -> bool {
ret bitv.copy(a.states.prestate, p);
}

Expand All @@ -139,6 +143,13 @@ fn extend_poststate(&poststate p, &poststate new) -> bool {
ret bitv.union(p, new);
}

// Clears the given bit in p
fn relax_prestate(uint i, &prestate p) -> bool {
auto was_set = bitv.get(p, i);
bitv.set(p, i, false);
ret was_set;
}

fn ann_precond(&ts_ann a) -> precond {
ret a.conditions.precondition;
}
Expand All @@ -148,8 +159,12 @@ fn ann_prestate(&ts_ann a) -> prestate {
}

fn pp_clone(&pre_and_post p) -> pre_and_post {
ret rec(precondition=bitv.clone(p.precondition),
postcondition=bitv.clone(p.postcondition));
ret rec(precondition=clone(p.precondition),
postcondition=clone(p.postcondition));
}

fn clone(prestate p) -> prestate {
ret bitv.clone(p);
}

// returns true if a implies b
Expand Down
15 changes: 15 additions & 0 deletions src/lib/_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,21 @@ fn plus_option[T](&vec[T] v, &option.t[T] o) -> () {
}
}

fn cat_options[T](&vec[option.t[T]] v) -> vec[T] {
let vec[T] res = vec();

for (option.t[T] o in v) {
alt (o) {
case (none[T]) { }
case (some[T](?t)) {
res += vec(t);
}
}
}

ret res;
}

// TODO: Remove in favor of built-in "freeze" operation when it's implemented.
fn freeze[T](vec[mutable T] v) -> vec[T] {
let vec[T] result = vec();
Expand Down
22 changes: 22 additions & 0 deletions src/test/compile-fail/break-uninit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// xfail-boot
// xfail-stage0
// error-pattern:Unsatisfied precondition

fn foo() -> int {
let int x;
let int i;

do {
i = 0;
break;
x = 0;
} while ((x = 0) != 0);

log(x);

ret 17;
}

fn main() {
log(foo());
}
22 changes: 22 additions & 0 deletions src/test/compile-fail/break-uninit2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// xfail-boot
// xfail-stage0
// error-pattern:Unsatisfied precondition

fn foo() -> int {
let int x;
let int i;

do {
i = 0;
break;
x = 0;
} while (1 != 2);

log(x);

ret 17;
}

fn main() {
log(foo());
}
21 changes: 21 additions & 0 deletions src/test/run-pass/use-uninit-alt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
fn foo[T](&myoption[T] o) -> int {
let int x = 5;

alt (o) {
case (none[T]) { }
case (some[T](?t)) {
x += 1;
}
}

ret x;
}

tag myoption[T] {
none;
some(T);
}

fn main() {
log(5);
}
21 changes: 21 additions & 0 deletions src/test/run-pass/use-uninit-alt2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
fn foo[T](&myoption[T] o) -> int {
let int x;

alt (o) {
case (none[T]) { fail; }
case (some[T](?t)) {
x = 5;
}
}

ret x;
}

tag myoption[T] {
none;
some(T);
}

fn main() {
log(5);
}