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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
|
\htmlhr
\chapterAndLabel{Subtyping Checker}{subtyping-checker}
The Subtyping Checker enforces only subtyping rules. It operates over
annotations specified by a user on the command line. Thus, users can
create a simple type-checker without writing any code beyond definitions of
the type qualifier annotations.
The Subtyping Checker can accommodate all of the type system enhancements that
can be declaratively specified (see Chapter~\ref{creating-a-checker}).
This includes type introduction rules via the
\refqualclass{framework/qual}{QualifierForLiterals} meta-annotation , and other features such as
type refinement (Section~\ref{type-refinement}) and
qualifier polymorphism (Section~\ref{qualifier-polymorphism}).
The Subtyping Checker is also useful to type system designers who wish to
experiment with a checker before writing code; the Subtyping Checker
demonstrates the functionality that a checker inherits from the Checker
Framework.
If you need typestate analysis, then you can extend a typestate checker.
For more details (including a definition of ``typestate''), see
Chapter~\ref{typestate-checker}.
See Section~\ref{faq-typestate} for a simpler alternative.
For type systems that require special checks (e.g., warning about
dereferences of possibly-null values), you will need to write code and
extend the framework as discussed in Chapter~\ref{creating-a-checker}.
\sectionAndLabel{Using the Subtyping Checker}{subtyping-using}
\begin{sloppypar}
The Subtyping Checker is used in the same way as other checkers (using the
\code{-processor org.checkerframework.common.subtyping.SubtypingChecker} option; see Chapter~\ref{using-a-checker}), except that it
requires an additional annotation processor argument via the standard
``\code{-A}'' switch. You must provide one or both of the two following
command-line arguments:
\end{sloppypar}
\begin{itemize}
\item
Provide the fully-qualified class name(s) of the annotation(s) in the custom
type system through the \code{-Aquals} option, using a comma-no-space-separated
notation:
\begin{alltt}
javac -classpath \textit{/full/path/to/myProject/bin}:\textit{/full/path/to/myLibrary/bin} \ttbs
-processor org.checkerframework.common.subtyping.SubtypingChecker \ttbs
-Aquals=\textit{myPackage.qual.MyQual},\textit{myPackage.qual.OtherQual} MyFile.java ...
\end{alltt}
\item
Provide the fully-qualified paths to a set of directories that contain the
annotations in the custom type system through the \code{-AqualDirs} option,
using a colon-no-space-separated notation. For example:
\begin{alltt}
javac -classpath \textit{/full/path/to/myProject/bin}:\textit{/full/path/to/myLibrary/bin} \ttbs
-processor org.checkerframework.common.subtyping.SubtypingChecker \ttbs
-AqualDirs=\textit{/full/path/to/myProject/bin}:\textit{/full/path/to/myLibrary/bin} MyFile.java
\end{alltt}
\end{itemize}
\subsectionAndLabel{Compiling your qualifiers and your project}{subtyping-compiling}
The annotations listed in \code{-Aquals} or \code{-AqualDirs} must be accessible to
the compiler during compilation in the classpath. In other words, they must
already be compiled (and, typically, be on the javac classpath)
before you run the Subtyping Checker with \code{javac}. It
is not sufficient to supply their source files on the command line.
\subsectionAndLabel{Suppressing warnings from the Subtyping Checker}{subtyping-suppressing}
To suppress a warning issued by the Subtyping Checker, use a
\sunjavadocanno{java.base/java/lang/SuppressWarnings.html}{SuppressWarnings}
annotation, with the argument being the unqualified, uncapitalized name of
any of the annotations passed to \code{-Aquals}. This will suppress all
warnings, regardless of which of the annotations is involved in the
warning. (As a matter of style, you should choose one of the annotations
as your \code{@SuppressWarnings} key and stick with it.)
\sectionAndLabel{Subtyping Checker example\label{encrypted-example}}{subtyping-example}
Consider a hypothetical \code{Encrypted} type qualifier, which denotes that the
representation of an object (such as a \code{String}, \code{CharSequence}, or
\code{byte[]}) is encrypted. To use the Subtyping Checker for the \code{Encrypted}
type system, follow three steps.
\begin{enumerate}
\item
Define two annotations for the \code{Encrypted} and \code{PossiblyUnencrypted} qualifiers:
% alltt because it uses \textit
\begin{alltt}
package \textit{myPackage}.qual;
import org.checkerframework.framework.qual.DefaultFor;
import org.checkerframework.framework.qual.SubtypeOf;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Denotes that the representation of an object is encrypted.
*/
@SubtypeOf(PossiblyUnencrypted.class)
@DefaultFor(\{TypeUseLocation.LOWER_BOUND\})
@Target(\{ElementType.TYPE_USE, ElementType.TYPE_PARAMETER\})
public @interface Encrypted \{\}
\end{alltt}
~
% alltt because it uses \textit
\begin{alltt}
package \textit{myPackage}.qual;
import org.checkerframework.framework.qual.DefaultQualifierInHierarchy;
import org.checkerframework.framework.qual.SubtypeOf;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Denotes that the representation of an object might not be encrypted.
*/
@DefaultQualifierInHierarchy
@SubtypeOf(\{\})
@Target(\{ElementType.TYPE_USE, ElementType.TYPE_PARAMETER\})
public @interface PossiblyUnencrypted \{\}
\end{alltt}
Note that all custom annotations must have the
\<@Target(ElementType.TYPE\_USE)> meta-annotation.
See Section~\ref{creating-define-type-qualifiers}.
Don't forget to compile these classes:
\begin{Verbatim}
$ javac myPackage/qual/Encrypted.java myPackage/qual/PossiblyUnencrypted.java
\end{Verbatim}
The resulting \<.class> files should either be on your classpath, or on the
processor path (set via the \<-processorpath> command-line option to javac).
\item
Write \code{@Encrypted} annotations in your program (say, in file
\code{YourProgram.java}):
\begin{alltt}
import \textit{myPackage}.qual.Encrypted;
...
public @Encrypted String encrypt(String text) \{
// ...
\}
// Only send encrypted data!
public void sendOverInternet(@Encrypted String msg) \{
// ...
\}
void sendText() \{
// ...
@Encrypted String ciphertext = encrypt(plaintext);
sendOverInternet(ciphertext);
// ...
\}
void sendPassword() \{
String password = getUserPassword();
sendOverInternet(password);
\}
\end{alltt}
You may also need to add \code{@SuppressWarnings} annotations to the
\code{encrypt} and \code{decrypt} methods. Analyzing them is beyond the
capability of any realistic type system.
\item
Invoke the compiler with the Subtyping Checker, specifying the
\code{@Encrypted} annotation using the \code{-Aquals} option.
You should add the \code{Encrypted} classfile to the processor classpath:
\begin{alltt}
javac -processorpath \textit{myqualpath} -processor org.checkerframework.common.subtyping.SubtypingChecker \
-Aquals=\textit{myPackage.qual.Encrypted},\textit{myPackage.qual.PossiblyUnencrypted} YourProgram.java
YourProgram.java:42: incompatible types.
found : @myPackage.qual.PossiblyUnencrypted java.lang.String
required: @myPackage.qual.Encrypted java.lang.String
sendOverInternet(password);
^
\end{alltt}
\item
You can also provide the fully-qualified paths to a set of directories
that contain the qualifiers using the \code{-AqualDirs} option, and add
the directories to the boot classpath, for example:
\begin{alltt}
javac -classpath \textit{/full/path/to/myProject/bin}:\textit{/full/path/to/myLibrary/bin} \ttbs
-processor org.checkerframework.common.subtyping.SubtypingChecker \ttbs
-AqualDirs=\textit{/full/path/to/myProject/bin}:\textit{/full/path/to/myLibrary/bin} YourProgram.java
\end{alltt}
\begin{sloppypar}
Note that in these two examples, the compiled class file of the
\<myPackage.qual.Encrypted> and \<myPackage.qual.PossiblyUnencrypted> annotations
must exist in either the \<myProject/bin> directory or the \<myLibrary/bin>
directory. The following placement of the class files will work with the above
commands:
\end{sloppypar}
\begin{alltt}
.../myProject/bin/myPackage/qual/Encrypted.class
.../myProject/bin/myPackage/qual/PossiblyUnencrypted.class
\end{alltt}
\end{enumerate}
Also, see the example project in the \<docs/examples/subtyping-extension> directory.
\sectionAndLabel{Type aliases and typedefs}{subtyping-type-alias}
A type alias or typedef is a type that shares the same representation as
another type but is conceptually distinct from it. For example, some
strings in your program may be street addresses; others may be passwords;
and so on. You wish to indicate, for each string, which one it is, and to
avoid mixing up the different types of strings. Likewise, you could
distinguish integers that are offsets from those that are absolute values.
Creating a new type makes your code easier to understand by conveying the
intended use of each variable. It also prevents errors that come from
using the wrong type or from mixing incompatible types in an operation.
If you want to create a type alias or typedef, you have multiple options:
a regular Java subtype,
the Units Checker (\chapterpageref{units-checker}),
the Fake Enum Checker (\chapterpageref{fenum-checker}), or
the Subtyping Checker.
A Java subtype is easy to create and does not require a tool such as the
Checker Framework; for instance, you would declare \<class Address extends
String>. There are a number of limitations to this ``pseudo-typedef'',
however~\cite{Goetz2006:typedef}.
Primitive types and final types (including \<String>) cannot be extended.
Equality and identity tests can return incorrect results when a wrapper
object is used. Existing return types in code would need to be changed,
which is easy with an annotation but disruptive to change the Java type.
Therefore, it is best to avoid the pseudo-typedef antipattern.
The Units Checker (\chapterpageref{units-checker}) is useful for the
particular case of units of measurement, such as kilometers verses miles.
The Fake Enum Checker (\chapterpageref{fenum-checker})
builds in a set of assumptions. If those fit your
use case, then it's easiest to use the Fake Enum Checker (though you can
achieve them using the Subtyping Checker). The Fake Enum Checker forbids
mixing of fenums of different types, or fenums and unannotated types. For
instance, binary operations other than string concatenations are forbidden,
such as \<NORTH+1>, \<NORTH+MONDAY>, and \<NORTH==MONDAY>. However,
\<NORTH+SOUTH> is permitted.
By default, the Subtyping Checker does not forbid any operations.
If you choose to use the Subtyping Checker, then you have an additional
design choice to make about the type system. In the general case, your
type system will look something like Figure~\ref{fig-typedef-hierarchy}.
\begin{figure}
\includeimage{typedef}{3.5cm}
\caption{Type system for a type alias or typedef type system.
The type system designer may choose to omit some of these types, but
this is the general case.
The type system designer's choice of defaults affects the interpretation
of unannotated code, which affects the guarantees given for unannotated code.
\label{fig-typedef-hierarchy}}
\end{figure}
References whose type is \<@MyType> are known to store only values from
your new type. There is no such guarantee for \<@MyTypeUnknown> and
\<@NotMyType>, but those types mean different things. An expression of type
\<@NotMyType> is guaranteed never to evaluate to a value of your new type.
An expression of type \<@MyTypeUnknown> may evaluate to any value ---
including values of your new type and values not of your new type.
(\<@MyTypeBottom> is the type of \<null> and is also used for dead code and
erroneous situations; it can be ignored for this
discussion.)
A key choice for the type system designer is which type is the default.
That is, if a programmer does not write \<@MyType> on a given type use,
should that type use be interpreted as \<@MyTypeUnknown> or as
\<@NotMyType>?
\begin{itemize}
\item
If unannotated types are interpreted as \<@NotMyType>, then the type system
enforces very strong separation between your new type and all other types.
Values of your type will never mix with values of other types. If you
don't see \<@MyType> written explicitly on a type, you will know that
it does not contain values of your type.
\item
If unannotated types are interpreted as \<@MyTypeUnknown>, then
a generic, unannotated type may contain a value of your new type.
In this case, \<@NotMyType> does not need to exist, and \<@MyTypeBottom>
may or may not exist in your type system.
\end{itemize}
A downside of the stronger guarantee that comes from using \<@NotMyType> as
the default is the need to write additional annotations.
For example, if \<@NotMyType> is the default, this code does not typecheck:
\begin{Verbatim}
void method(Object o) { ... }
<U> void use(List<U> list) {
method(list.get(0));
}
\end{Verbatim}
Because (implicit) upper bounds are interpreted as the top type (see
Section~\ref{generics-defaults}), this is interpreted as
\begin{Verbatim}
void method(@NotMyType Object o) { ... }
<@U extends @MyTypeUnknown Object> void use(List<U> list) {
// type error: list.get(0) has type @MyTypeUnknown, method expects @NotMyType
method(list.get(0));
}
\end{Verbatim}
To make the code type-check, it is necessary to write an explicit
annotation, either to restrict \<use>'s argument or to expand \<method>'s
parameter type.
% LocalWords: TODO ImplicitFor Aquals sourcepath java NonNull AqualDirs
% LocalWords: CharSequence classpath nullness quals SuppressWarnings classfile
% LocalWords: uncapitalized processorpath Warski MyFile YourProgram qual
%% LocalWords: bootclasspath PossiblyUnencrypted myProject myLibrary msg
%% LocalWords: ElementType myqualpath sendOverInternet myPackage typedefs
%% LocalWords: SubtypeOf LiteralKind DefaultFor TypeUseLocation fenum
%% LocalWords: DefaultQualifierInHierarchy sendText sendPassword MyType
% LocalWords: getUserPassword CompassDirection MyTypeUnknown NotMyType
% LocalWords: MyTypeBottom typecheck ciphertext plaintext decrypt fenums
%% LocalWords: typedef antipattern
|