Programming Language for Old Timers


by David A. Moon
February 2006 .. September 2008

Comments and criticisms to dave underscore moon atsign alum dot mit dot edu.


Previous page   Table of Contents   Next page


Compile-Time Types

Every expression has a compile-time type, also known as a static type. This enables the compiler to optimize code by doing some or all of the work of function invocation before run-time.

The compile-time type is also used by some compilers for optimizations such as removing unnecessary run-time type checks, inlining type-specific methods, and using special representations for certain types of numbers and other unboxable types.

Method dispatch and the type check operator "is" use dynamic typing, in other words the actual class of the value at run-time, not the compile-time type.

The following rules determine the compile-time type of an expression. These rules are based on the source code classes (the P-expression classes).

The compile-time type of a literal or quotation is the run-time class of the quoted object.

The compile-time type of a name depends on its definition in the current scope. If the definition is fixed and known, the type of the name is the intersection of the compile-time type of its initializing expression and the type restriction of the name in the definition. If the name is a parameter of a method and all call sites are known, the type of the name is the intersection of the union of the compile-time types of all arguments and the type restriction of the name in the parameter list. For a recursive function this can require a least fixed point computation. Note that iteration statements expand into recursive functions with known call sites. For all other names, the compile-time type of the name is the type restriction declared in its definition.

The compile-time type of an invocation depends on whether a known method is being invoked. If so, the type is the compile-time type of the method's body when the parameters have the compile-time types of the arguments at the current call site.

---TBD: Is there a case where the method's parameter list is known but the method's body is not known? In that case, the type is the result type declared in the method's parameter list. The distinction between this case and the preceding case has to do with inlining, which has not yet been clarified.

If a known method is not being invoked, but a method requirement is applicable, then the compile-time type of an invocation is the intersection of the result types of all applicable method requirements.

Otherwise the compile-time type of an invocation is anything.

Note that an invocation of a slot reader is an invocation of a known method when the compile-time type of the argument is the class that owns the slot or a subtype of that class. This is because slot reader methods are sealed. Therefore the compile-time type of an invocation of a slot reader is the declared type restriction of the slot, when the argument class is known at compile-time.

The compile-time type of a definition is the compile-time type of the initialization expression, or is function for a method definition.

The compile-time type of an assignment is the compile-time type of the right-hand side.

The compile-time type of a functation is function.

The compile-time type of a collation, sanitation, or scopation is the compile-time type of the result expression in its body.

The compile-time type of an exitation is the union of the compile-time type of the result expression in its body and the compile-time types of the argument to the exit function at each call site. The type is anything if the exit function escapes so not all call sites are known.

The compile-time type of a conditional is the union of the compile-time types of the then and else arms of the conditional. Certain expressions as the test of a conditional are recognized and modify the compile-time types of one of their arguments in the then and else arms of the conditional in complementary ways. This recognition occurs even if the expression is embedded in a do body or a block or in an invocation of not. Specifically, an "is" test with a name as its left-hand side recognizes that the compile-time type of the name is or is not an instance of the tested type. A true/false test of a name recognizes that the value of the name is or is not false. A name eq false or false eq name test does the same. All of these can reduce a type union to one of its component types within one arm of a conditional. When a name's compile-time type is integer going into the test expression of a conditional, and the name is involved in a test such as <, an integer range type is recognized.

Note that compound conditionals such as and and or macroexpand into nested conditionals and then the above rules apply. Similarly, while macroexpands into a non-escaping function definition containing a conditional recursive call.

For example:

  if x is cons
    print(x.car)                ; must be car slot of cons type

  defun f(x is list or false)
    if x
      print(x.length)           ; x is not false so it must be a list
    else
      print("no x")

  defun g(x) is list or false
    ...body...

  if def temp = g(x) then temp.length else 0
                                ; argument to length must be a list


Previous page   Table of Contents   Next page