Go to the first, previous, next, last section, table of contents.

Resolving method invocations

This section describes the currently used algorithm for selecting which method to actually invoke for a given method invocation.

  1. Resolve the receiver to be an object.
  2. Get for the receiver a list of eligible methods. A method is eligible if
    1. it is defined for the receiver or inherited by the receiver,
    2. the name parts of the invocation fit the name parts of the method, i.e. the order of the name parts provided match the order of the corresponding name parts in the method, and no name part of a non-optional argument is omitted,
    3. its return type is either dynamic or it matches at least one type in the context (if that is non-empty),
    4. it is not a method which is overridden, in a subclass, by another eligible method, and
    5. if the receiver is not super and the types of the arguments and return values exactly match those of an already eligible method.
  3. Build the possible argument types.
  4. For each actual argument, resolve it in the context of its possible types. If it is unresolvable (i.e its set of possible types is empty), this invocation can not be resolved either.
  5. If any of the possible argument types was restricted by the attempt to resolve, eliminate from the eligible method those methods which are no longer applicable. If at least one method was eliminated, rebuild the possible argument types and continue with the previous step.
  6. If the context is a single type or all arguments have been fully resolved (i.e. typed) or all but one method have been eliminated, continue with the next step, otherwise return the list of possible types for this invocation.
  7. If we're expected to fully resolve, i.e. the context is a single type, eliminate those methods from the eligible set which do not fit the expected type best.
  8. Eliminate those methods from the eligible set which do not have the highest number of fully matching arguments.
  9. Eliminate those methods from the eligible set which do not have the highest number of name parts. This favours methods with lots of optional parts over methods with less optional parts. The rationale behind this is that the latter is probably only provided to invoke the former with its default arguments while responding to an additional selector.
  10. If any of the eligible methods involve dynamic typing while some do not, eliminate the former. This is needed, for instance, to have [[stdio out] print 1234] invoke the integer printing routine instead of the dynamic one which would just invoke the integer printing routine for this argument anyway.
  11. If we're down to one method, fully type any remaining arguments which are yet untyped. The type of the invocation is the return type of the method, and the set returned is deduced from the context and the type in the usual way. If the return type is dynamic, the context must specify a single type.
  12. Otherwise, resolving failed and, in the empty context, we return the set of possible return types of the remaining methods; in a non-empty context, return those types in the context which can be matched by at least one of the remaining methods.

An important aspect of this algorithm is that it correctly produces an error for undecidable invocations, such as the invocation of foo (byte (3), byte (4)) while the available methods are foo (int, byte) and foo (byte, int).

A disadvantage of the current setup in the compiler is that an attempt to invoke a method with slightly wrong argument types results in an error indicating that the expression could not be typed, without any positive hints about the possibly applicable methods.

Go to the first, previous, next, last section, table of contents.