Since the decl in a for or for-each loop must always be a local
decl, I changed the AST to express this. Fewer potential match
failures and "the impossible happened" error messages = yay!
Generate appropriate constraints for calls to functions with
preconditions, and reject calls where those constraints don't
hold true in the prestate.
...by which I mean that it works for one test case :-)
Before, all aliases were implicitly mutable, and writing
&mutable was the same as writing &. Now, the two are
distinguished, and assignments to regular aliases are
no longer allowed.
Changed function types to include a list of constraints. Added
code for parsing and pretty-printing constraints. This necessitated
splitting pprust into two files (pprust and ppaux) to break a
circulate dependency, as ty_to_str now needs to print out constraints,
which may include literals, but pprust depended on ty.
The old system tried to ensure that the location an alias pointed at
would retain its type. That turned out to not be strong enough in the
face of aliases to the inside of tags.
The new system instead proves that values pointed to by aliases are
not replaced (or invalidated in some other way) at all. It knows of
two sufficient conditions for this, and tries to prove at least of
them:
A) The alias is 'immutably rooted' in a local, and this local is not
reassigned for the lifetime of the alias. Immutably rooted means
the alias refers to the local itself, or to something reachable
from the local through immutable dereferencing.
B) No value whose type might include the type of the 'inner mutable
element' of the thing the alias refers to (for example, the box in
rec(mutable x = @mutable int)) is from the outer scope is accessed
for the lifetime of the alias. This means for functions, no other
argument types may include the alias's inner mutable type. For alt,
for each, and for, it means the body does not refer to any locals
originating from outside their scope that include this type.
The lifetime of an alias in an alt, for each, or for body is defined
as the range from its definition to its last use, not to the point
where it goes out of scope. This makes working around these
restrictions somewhat less annoying. For example, you can assign to
your alt-ed value you don't refer to any bindings afterwards.
Change ty_fn to have a controlflow field. A 'controlflow' is
essentially a bit of data that says whether or not this function
never returns to the caller (if it never returns, that means it calls
"fail" or another "never-returns" function on every control path).
Also add syntax for annotating functions that never return:
fn foo() -> ! {
fail;
}
for example. Functions marked with ! implicitly have a result type of
ty_bot, which is a new type that this commit also adds.
* 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 prevents insane things like 'auto while = 2', which would parse
in the previous revision, but then break when you tried to mutate it
with 'while = 10'.
Keywords are now only recognized in contexts where they are valid. The
lexer no longer recognizes them, all words are lexed as IDENT tokens,
that get interpreted by the parser.
One step closer to removing fold and having a single, immutable AST.
Resolve still uses fold, because it has to detect and transform
expr_field expressions. If we go through on our plan of moving to a
different syntax for module dereferencing, the parser can spit out
expr_field expressions, and resolve can move to walk.
(I am truly sorry for the things I did in typestate_check.rs. I expect
we'll want to change that to walk as well in the near future, at which
point it should probably pass around a context record, which could
hold the def_map.)
This way, the tag assigned by the parser stays with the node.
I realize ann replacing is probably going away real soon, but
I needed this now for moving the resolve defs out of the AST.
* Cleans up the algorithm
* Move first pass to walk (second still folds)
* Support part of a type/value namespace split
(crate metadata and module indices still need to be taught about this)
* Remove a few blatant inefficiencies (import tables being recreated for
every lookup, most importantly)
This commit reinstates the requirement that the predicate in a
"check" must be a manifest call to a special kind of function
declared with the new "pred" keyword instead of "fn". Preds must
have a boolean return type and can only call other preds; they
can't have any effects (as enforced by the typechecker).
The arguments to a predicate in a check expression must be
slot variables or literals.
Check that the operand in a constraint is an explicit name,
and that the operands are all local variables or literals. Still need
to check that the name refers to a pure function.
This giant commit changes the syntax of Rust to use "assert" for
"check" expressions that didn't mean anything to the typestate
system, and continue using "check" for checks that are used as
part of typestate checking.
Most of the changes are just replacing "check" with "assert" in test
cases and rustc.
(caveat for the latter: it assumes that binary operations are strict;
a TODO is to detect or and and and correctly reflect that they're lazy
in the second argument). I had to add an ann field to ast.block,
resulting in the usual boilerplate changes.
Test cases that currently work (if you uncomment the typestate pass
in the driver) (all these are under test/compile-fail):
fru-typestate
ret-uninit
use-uninit
use-uninit-2
use-uninit-3
Also changed the ts_ann field on statements to be an ann instead,
which explains most of the changes.
As well, got rid of the "warning: no type for expression" error
by filling in annotations for local decls in typeck (not sure whether
this was my fault or not).
Finally, in bitv, added a clone() function to copy a bit vector,
and fixed is_true, is_false, and to_str to not be nonsense.
This makes passing them around cheaper. There is now a table (see
front/codemap.rs) that is needed to transform such an uint into an
actual filename/line/col location.
Also cleans up the span building in the parser a bit.
Nicer parsing of self-calls (expr_self_method nodes inside expr_call
nodes, rather than a separate expr_call_self) makes typechecking
tractable. We can now write self-calls that take arguments and return
values (see: test/run-pass/obj-self-*.rs).
It's still sketchy. I added a typestate annotation field to statements
tagged stmt_decl or stmt_expr, because a stmt_decl statement has a typestate
that's different from that of its child node. This necessitated trivial
changes to a bunch of other files all over to the compiler. I also added a
few small standard library functions, some of which I didn't actually end
up using but which I thought might be useful anyway.
The last few pieces of the hack that lets us use trans.trans_call() to
translate self-calls, plus a fix for the parser buy that was
preventing self-call expressions from getting past parsing.
test/run-pass/obj-self.rs works now (as in it actually prints "hi!"
twice!).
Mostly:
* Merciless refactoring of trans.rs so that trans_call can work for
self-calls as well as other kinds of calls
Also:
* Various changes to go with having idents, rather than exprs, in
expr_call_self AST nodes
* Added missing case for SELF token to token.to_str()
I added a new field to the ast "ann" type for typestate information.
Currently, the field contains a record of a precondition bit vector and
postcondition vector, but I tried to structure things so as to make
it easy to change the representation of the typestate annotation type.
I also had to add annotations to some syntactic forms that didn't have
them before (fail, ret, be...), with all the boilerplate changes
that that would imply.
The main call to the typestate_check entry point is commented out and
the actual pre-postcondition algorithm only has a few cases
implemented, though the overall AST traversal is there. The rest of
the typestate algorithm isn't implemented yet.