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 is important when accessing slots, because only the slots declared in or inherited by the compile-time class are accessible. If the compile-time type is not itself a class, the compile-time class is the most derived class that contains all instances of the compile-time type.

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). For clarity the rules are expressed as if expressions only had one value, but in reality when multiple values are used each value has its own compile-time type.

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.

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

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 or slot-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, collation-first, 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 slot-reference is the declared type of the slot if it is real and assignable, the intersection of the declared type of the slot and the compile-time type of its initialization expression if the slot is real and fixed, or the compile-time type of the read: expression if the slot is virtual.

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)                ;; car slot of cons type is available

  def 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")

  def g(x, result: y is list or false) ...

  if def temp = g(x) then temp.length else 0


Previous page   Table of Contents   Next page