File: subtyping-checker.tex

package info (click to toggle)
checker-framework-java 3.2.0%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,104 kB
  • sloc: java: 145,916; xml: 839; sh: 518; makefile: 404; perl: 26
file content (355 lines) | stat: -rw-r--r-- 14,332 bytes parent folder | download | duplicates (3)
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