There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected. The most commonly used form of dispatch is
<expr>.<id>(<expr>,...,<expr>)Consider the dispatch
Type checking a dispatch involves several steps. Assume has
static type A. (Recall that this type is not necessarily the
same as the type C above. A is the type inferred by the
type checker; C is the class of the object computed at runtime,
which is potentially any subclass of A.) Class A must
have a method f, the dispatch and the definition of f must
have the same number of arguments, and the static type of the
th actual
parameter must conform to the declared type of the
th formal
parameter.
If f has return type B and B is a class name, then the static type of the dispatch is B. Otherwise, if f has return type SELF_TYPE, then the static type of the dispatch is A. To see why this is sound, note that the self parameter of the method f conforms to type A. Therefore, because f returns SELF_TYPE, we can infer that the result must also conform to A. Inferring accurate static types for dispatch expressions is what justifies including SELF_TYPE in the Cool type system.
The other forms of dispatch are:
<id>(<expr>,...,<expr>) <expr>@<type>.id(<expr>,...,<expr>)The first form is shorthand for self.<id>(<expr>,...,<expr>).
The second form provides a way of accessing methods of parent classes that have been hidden by redefinitions in child classes. Instead of using the class of the leftmost expression to determine the method, the method of the class explicitly specified is used. For example, e@B.f() invokes the method f in class B on the object that is the value of e. For this form of dispatch, the static type to the left of ``@''must conform to the type specified to the right of ``@''.