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
    ...
 & = & T \\
O[T/c](d) & = & O(d)\ \ \mbox {\rm if\ }d \neq c
\end{eqnarray*}](img68.png)