Started adding support for return checking and non-returning function annotations

* Reorganized typestate into several modules.

* Made typestate check that any function with a non-nil return type
  returns a value. For now, the check is a warning and not an error
  (see next item).

* Added a "bot" type (prettyprinted as _|_), for constructs like be, ret, break, cont, and
  fail that don't locally return a value that can be inspected. "bot"
  is distinct from "nil". There is no concrete syntax for _|_, while
  the concrete syntax for the nil type is ().

* Added support to the parser for a ! annotation on functions whose
  result type is _|_. Such a function is required to have either a
  fail or a call to another ! function that is reached in all control
  flow paths. The point of this annotation is to mark functions like
  unimpl() and span_err(), so that an alt with a call to err() in one
  case isn't a false positive for the return-value checker. I haven't
  actually annotated anything with it yet.

* Random bugfixes:

* * Fixed bug in trans::trans_binary that was throwing away the
    cleanups for nested subexpressions of an and or or
    (tests: box-inside-if and box-inside-if2).

** In typeck, unify the expected type arguments of a tag with the
   actual specified arguments.
This commit is contained in:
Tim Chevalier 2011-05-14 19:02:30 -07:00
parent c75125fcce
commit 971b5d5151
22 changed files with 3276 additions and 51 deletions

View file

@ -1,4 +1,3 @@
import std::map::hashmap;
import std::option;
import std::_str;
@ -7,7 +6,7 @@ import util::common::span;
import util::common::spanned;
import util::common::ty_mach;
import util::common::filename;
import util::typestate_ann::ts_ann;
import middle::tstate::ann::ts_ann;
type ident = str;
@ -323,6 +322,12 @@ type ty_method = rec(proto proto, ident ident,
type ty = spanned[ty_];
tag ty_ {
ty_nil;
ty_bot; /* return type of ! functions and type of
ret/fail/break/cont. there is no syntax
for this type. */
/* bot represents the value of functions that don't return a value
locally to their context. in contrast, things like log that do
return, but don't return a meaningful value, have result type nil. */
ty_bool;
ty_int;
ty_uint;
@ -354,12 +359,19 @@ type constr = spanned[constr_];
type arg = rec(mode mode, @ty ty, ident ident, def_id id);
type fn_decl = rec(vec[arg] inputs,
@ty output,
purity purity);
purity purity,
controlflow cf);
tag purity {
pure_fn; // declared with "pred"
impure_fn; // declared with "fn"
}
tag controlflow {
noreturn; // functions with return type _|_ that always
// raise an error or exit (i.e. never return to the caller)
return; // everything else
}
type _fn = rec(fn_decl decl,
proto proto,
block body);

View file

@ -23,6 +23,11 @@ tag file_type {
SOURCE_FILE;
}
tag ty_or_bang {
a_ty(@ast::ty);
a_bang;
}
state type parser =
state obj {
fn peek() -> token::token;
@ -448,6 +453,13 @@ fn parse_ty_constrs(@ast::ty t, parser p) -> @ast::ty {
ret t;
}
fn parse_ty_or_bang(parser p) -> ty_or_bang {
alt (p.peek()) {
case (token::NOT) { p.bump(); ret a_bang; }
case (_) { ret a_ty(parse_ty(p)); }
}
}
fn parse_ty(parser p) -> @ast::ty {
auto lo = p.get_lo_pos();
auto hi = lo;
@ -1713,7 +1725,7 @@ fn parse_fn_decl(parser p, ast::purity purity) -> ast::fn_decl {
some(token::COMMA),
pf, p);
let @ast::ty output;
let ty_or_bang res;
// FIXME: dropping constrs on the floor at the moment.
// pick them up when they're used by typestate pass.
@ -1721,12 +1733,23 @@ fn parse_fn_decl(parser p, ast::purity purity) -> ast::fn_decl {
if (p.peek() == token::RARROW) {
p.bump();
output = parse_ty(p);
res = parse_ty_or_bang(p);
} else {
output = @spanned(inputs.span.lo, inputs.span.hi, ast::ty_nil);
res = a_ty(@spanned(inputs.span.lo, inputs.span.hi, ast::ty_nil));
}
alt (res) {
case (a_ty(?t)) {
ret rec(inputs=inputs.node, output=t,
purity=purity, cf=ast::return);
}
case (a_bang) {
ret rec(inputs=inputs.node,
output=@spanned(p.get_lo_pos(),
p.get_hi_pos(), ast::ty_bot),
purity=purity, cf=ast::noreturn);
}
}
// FIXME
ret rec(inputs=inputs.node, output=output, purity=purity);
}
fn parse_fn(parser p, ast::proto proto, ast::purity purity) -> ast::_fn {
@ -1778,11 +1801,12 @@ fn parse_dtor(parser p) -> @ast::method {
let vec[ast::arg] inputs = vec();
let @ast::ty output = @spanned(lo, lo, ast::ty_nil);
let ast::fn_decl d = rec(inputs=inputs,
output=output,
purity=ast::impure_fn);
output=output,
purity=ast::impure_fn,
cf=ast::return);
let ast::_fn f = rec(decl = d,
proto = ast::proto_fn,
body = b);
proto = ast::proto_fn,
body = b);
let ast::method_ m = rec(ident="drop",
meth=f,
id=p.next_def_id(),