1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
\sectionAndLabel{Typestate checkers}{typestate-checker}
In a regular type system, a variable has the same type throughout its
scope.
In a typestate system, a variable's type can change as operations
are performed on it.
The most common example of typestate is for a \<File> object. Assume a file
can be in two states, \<@Open> and \<@Closed>. Calling the \<close()> method
changes the file's state. Any subsequent attempt to read, write, or close
the file will lead to a run-time error. It would be better for the type
system to warn about such problems, or guarantee their absence, at compile
time.
Just as you can extend the Subtyping Checker to create a type-checker, you can
extend a typestate checker to create a type-checker that supports typestate
analysis. An extensible typestate analysis by Adam Warski that builds on
the Checker Framework is available at
\myurl{http://www.warski.org/typestate.html}.
\subsectionAndLabel{Comparison to flow-sensitive type refinement}{typestate-vs-type-refinement}
The Checker Framework's flow-sensitive type refinement
(Section~\ref{type-refinement}) implements a form of typestate analysis.
For example, after code that tests a variable against null, the Nullness
Checker (Chapter~\ref{nullness-checker}) treats the variable's type as
\<@NonNull \emph{T}>, for some \<\emph{T}>\@.
For many type systems, flow-sensitive type refinement is sufficient. But
sometimes, you need full typestate analysis. This section compares the
two.
% (Dependent types and unused variables
(Unused variables
% (Section~\ref{unused-fields-and-dependent-types})
(Section~\ref{unused-fields})
also have similarities
with typestate analysis and can occasionally substitute for it. For
brevity, this discussion omits them.)
A typestate analysis is easier for a user to create or extend.
Flow-sensitive type refinement is built into the Checker Framework and is
optionally extended by each checker. Modifying the rules requires writing
Java code in your checker. By contrast, it is possible to write a simple
typestate checker declaratively, by writing annotations on the methods
(such as \<close()>) that change a reference's typestate.
A typestate analysis can change a reference's type to something that is not
consistent with its original definition. For example, suppose that a
programmer decides that the \<@Open> and \<@Closed> qualifiers are
incomparable --- neither is a subtype of the other. A typestate analysis
can specify that the \<close()> operation converts an \<@Open File> into a
\<@Closed File>. By contrast, flow-sensitive type refinement can only give
a new type that is a subtype of the declared type --- for flow-sensitive
type refinement to be effective, \<@Closed> would need to be a child of
\<@Open> in the qualifier hierarchy (and \<close()> would need to be
treated specially by the checker).
|