TC-4 Samples

Type checking is invoked by --types-compute. As for the computation of bindings, this option only handles programs with no object construct. To perform the type-checking of programs with objects, use --object-types-compute.

Implementing overloaded functions in Tiger is an option, which requires the implementation of a different type checker, triggered by --overfun-types-compute (see TC-A, Ad Hoc Polymorphism (Function Overloading)). The option --typed/-T makes sure one of them was run.

int-plus-string.tig
1 + "2"
tc int-plus-string.tig
$ tc int-plus-string.tig

$ echo $?
0
tc -T int-plus-string.tig
$ tc -T int-plus-string.tig
int-plus-string.tig:1.5-7: type mismatch
  right operand type: string
  expected type: int
$ echo $?
5

The type checker shall ensure loop index variables are read-only.

assign-loop-var.tig
 /* error: index variable erroneously assigned to.  */
for i := 10 to 1 do
  i := i - 1
tc -T assign-loop-var.tig
$ tc -T assign-loop-var.tig
assign-loop-var.tig:3.3-12: variable is read only
$ echo $?
5

When there are several type errors, it is admitted that some remain hidden by others.

unknowns.tig
unknown_function(unknown_variable)
tc -T unknowns.tig
$ tc -T unknowns.tig
unknowns.tig:1.1-34: undeclared function: unknown_function
$ echo $?
4

Be sure to check the type of all the constructs.

bad-if.tig
if 1 then 2
tc -T bad-if.tig
$ tc -T bad-if.tig
bad-if.tig:1.1-11: type mismatch
  then clause type: int
  else clause type: void
$ echo $?
5

Be aware that type and function declarations are recursive by chunks.

mutuals.tig
let
  type one = { hd : int, tail : two }
  type two = { hd : int, tail : one }
  function one(hd : int, tail : two) : one
     = one { hd = hd, tail = tail }
  function two(hd : int, tail : one) : two
     = two { hd = hd, tail = tail }
  var one := one(11, two(22, nil))
in
  print_int(one.tail.hd); print("\n")
end
tc -T mutuals.tig
$ tc -T mutuals.tig

$ echo $?
0

In case you are interested, the result is:

tc -H mutuals.tig > mutuals.hir
$ tc -H mutuals.tig > mutuals.hir

$ echo $?
0
havm mutuals.hir
$ havm mutuals.hir
22
$ echo $?
0

The type-checker must catch erroneous inheritance relations.

bad-super-type.tig
let
  /* Mutually recursive inheritance.  */
  type A = class extends A {}

  /* Mutually recursive inheritance.  */
  type B = class extends C {}
  type C = class extends B {}

  /* Class inherits from a non-class type.  */
  type E = class extends int {}
in
end
tc --object-types-compute bad-super-type.tig
$ tc --object-types-compute bad-super-type.tig
bad-super-type.tig:3.12-29: recursive inheritance: A
bad-super-type.tig:6.12-29: recursive inheritance: C
bad-super-type.tig:10.26-28: class type expected, got: int
$ echo $?
5

Handle the type-checking of TypeChunk with care in object::TypeChecker: they are processed in three steps, while other declarations use a two-step visit. The object::TypeChecker visitor proceeds as follows when it encounters a TypeChunk:

  1. Visit the headers of all types in the block.

  2. Visit the bodies of all types in the block, but ignore members for each type being a class.

  3. For each type of the block being a class, visit its members.

This three-pass visit allows class members to make forward references to other types defined in the same block of types, for instance, instantiate a class B from a class A (defined in the same block), even if B is defined after A.

forward-reference-to-class.tig
let
  /* A block of types.  */
  class A
  {
    /* Valid forward reference to B, defined in the same block
       as the class enclosing this member.  */
    var b := new B
  }
  type t = int
  class B
  {
  }
in
end
tc --object-types-compute forward-reference-to-class.tig
$ tc --object-types-compute forward-reference-to-class.tig

$ echo $?
0

See object::TypeChecker::operator()(ast::TypeChunk&) for more details.