Lecture 009

Semantic Analysis:


Static Scope: scope depends only on the program text, not run-time behavior.

Dynamic Scope: depends on execution of the program. (Lisp)


Not all identifiers follow the most-closely nested rule. Class name should be able to accessed from anywhere. (Globally visible, or can be used before define). This means that we need multiple passes (generally more passes is better for code maintenance).

Symbol Table

For simple symbol table, we can use a stack.

careful that we don't want redefinition in function input

We need to implement: enter_scope(), find_symbol(x), add_symbol(x), check_scope(x), exit_scope()


user declares types for identifiers. compiler infer types for every expression (type inference).


It is not obvious why we need type though. The assembly language can do any operation between any types without error.

Type is an invention to help human reasoning.

Different Languages on Typing:

In practice, you see statically typed or dynamically typed language converge. - static people use "escape" to do unsafe type cast - dynamic people write optimization and add static typing for debugging

Type Checking


We will use logical rule of inference in type checking

\frac{\vdash \text{Hypothesis} ... \vdash \text{Hypothesis}}{\vdash \text{Conclusion}}

The above reads if it is provable that Hypothesis and Hypthesis is true, then it is provable the Conclusion is true.

Sound: a type system is sound e : T actually hold if \vdash e : T

Types are computed in a bottom-up pass over abstract syntax tree (AST)

Free Variable

Free Variable: a variable that is not defined within the expression

Example: x is free in let y in x + y but y is not.

Type Environment: a function O : \text{ObjectIdentifier} \to \text{Types} It can give type for free variables. We can write O \vdash e : T

Overriding Types: O[T / x] means we modifies O such that x map to type T and all other entries stay the same.

Type environment is passed down to leaf from root

Let Initialization

Let Initialization


Subtyping: we define relation \leq on classes. So X \leq Y if X (child) inherits from Y (parent)

Let Initialization with Subtyping

Let Initialization with Subtyping

Assignment with Subtyping

Assignment with Subtyping

Consider if e0 then e1 else e2 fi, the result type can be either e1 or e2. The only thing we can guarantee is the common parent for e1 and e2 (smallest supertype larger than both e1 and e2)

lib(X, Y): least upper bound (common parent)

If-Else with Subtyping

If-Else with Subtyping


Function (method) and object identifiers typically live in different namespace (no conflict if you declear a variable that has the same name as function)

M(C, f) = (T_1, ..., T_n, T_{n + 1})

above means in class C we have a method f with input x_1, ..., x_n of type T_1, ..., T_n return a type of T_{n +1}.

Function call (dispatch) example

Function call (dispatch) example

Static Dispatch example

Static Dispatch example

Full Type Environment:

Only dispatch rules need function signature M, and all other need only O.

Example: full environment

Example: full environment

To write code for TypeCheck for Let-Init, we can do:

TypeCheck(OMC, let x : T <- e0 in e1) = {
  T0 = TypeCheck(OMC, e0);
  T1 = TypeCheck(OMC.add(x : T), e1);
  assert subtype(T0, T1);
  return T1;

Table of Content