To a first approximation, type checking in Cool can be thought of as
a bottom-up algorithm: the type of an expression is computed from the
(previously computed) types of
's subexpressions. For example,
an integer 1 has type Int; there are no subexpressions in
this case. As another example, if
has type
, then
the expression
has
type
.
A complication arises in the case of an expression , where
is an object identifier. It is not possible to say what the type of
is in a strictly bottom-up algorithm; we need to know the type
declared for
in the larger expression. Such a declaration
must exist for every object identifier in valid Cool programs.
To capture information about the types of identifiers, we use a
type environment. The environment consists of three parts:
a method environment , an object environment
, and
the name of the current class in which the expression appears.
The method environment and object environment are both functions (also
called mappings). The object environment is a function of the
form
Two mappings are required instead of one because object names and method names do not clash--i.e., there may be a method and an object identifier of the same name.
The third component of the type environment is the name of the current class, which is needed for type rules involving SELF_TYPE.
Every expression is type checked in a type environment;
the subexpressions of
may be type checked in the same environment or,
if
introduces a new object identifier, in a modified environment.
For example, consider the expression
let c : Int <- 33 in ...