Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 46

Modern Compiler Design

T8 Semantic Analysis: Type Checking

Mooly Sagiv and Greta Yorsh School of Computer Science Tel-Aviv University gretay@post.tau.ac.il http://www.cs.tau.ac.il/~gretay

TA1
Submit
08/12/04 today after class or 09/12/04 tomorrow to my mail box Schreiber 271

Questions after the session

Course Requirements
Compiler Project 60% Theoretical Assignments 5% Exam 35% (must pass to get credit)

PA2
Average grades
automatic 88 manual 91 total 89

Main pitfalls
special characters in strings newline and EOF in comments too long strings

Today
txt
Lexical Analysis Syntax Analysis Parsing AST Symbol Table etc. Inter. Rep. (IR) Code Gen.

exe

COOL code

Executable code

Goals
Implementing Type Checking for Cool Cool Type Rules

Next week
MIPS Recap short (longer at the last session 15-16) Stack Frames
5

Major Semantic Tasks


Inheritance graph construction and checking Symbol table construction
construct symbol table for features using inheritance tree assign enclosing scope for each AST node

Scope checking
check program using symbol tables

Check for Main class and main method Type checking


uses inheritance graph and symbol tables assign type for each AST node
6

Inheritance Graph
What do we put in it?
all predefined classes: Int, String, Bool, Object, IO all user-defined classes

Why do we put it there ?


to be able to check that something is a type to be able to compare types (for type checking)

Implementation
Mapping class names to nodes in the graph For each node keep
parent (and children) in the inheritance graph is the class reachable/unreachable from the Object class via the "inherits from" relation?

Check Inheritance Graph


Local properties - do not require traversing inheritance graph
base class is defined base class can be inherited from

Global properties
find all classes reachable from root class Object check that it really is a tree (no cycles)

Major Semantic Tasks


Inheritance graph construction and checking Symbol table construction
construct symbol table for features using inheritance tree assign enclosing scope for each AST node

Scope checking
check program using symbol tables

Check for Main class and main method Type checking


uses inheritance graph and symbol tables assign type for each AST node
10

Semantic Checks
Scope rules
identifiers are defined no multiple definition of same identifier local variables are defined before used program conforms to scope rules

Type rules
which types can be combined with certain operator assignment of expression to variable formal and actual parameters of a method call

11

Type Checking - example


String String string ERROR
(in Java ) in Cool ?

drive + drink

Int

String

12 + Rumster
ERROR
12

Type Checking - Implementation


Single traversal over AST Types passed up the tree Type environment passed down the tree

13

Example
globals
Symbol Foo kind class

lt

Foo
Symbol test kind Int->Int Int

plus

neg test

lookup(x)

Symbol c Int

int_const val=45

object name=x

int_const val=32

: Int

: Int

O(x) = Int O,M,C x : Int O,M,C int_const : Int

(45 + x) < (~32)

14

Example

lt

O,M,C

E1 : Int O,M,C E2 : Int E1 <


E2 : Bool

O,M,C

: Bool

O,M,C O,M,C

E1 : Int ~E1 : Int

: Int

plus

neg

: Int
O,M,C O,M,C O,M,C

E1 : Int E2 : Int E1 + E2 : Int

int_const val=45

object name=x

int_const val=32

: Int

: Int

O(x) = Int O,M,C x : Int O,M,C int_const : Int

: Int

(45 + x) < (~32)

15

Cool Types
How types are represented ?
Symbol compare types by comparing Symbols

When are types are created?


during lexer/parsing predefined types

16

Cool Types
The types are
Class Names SELF_TYPE

The user declares types for identifiers The semantic analysis infers types for expressions
Every expression has a unique type

A languages type system specifies which operations are valid for which types The goal of type checking is to ensure that operations are used with the correct types
Enforces intended interpretation of values

Cool is statically typed: type checking during compilation


17

Cool Types
O, M, K e: T
type environment

O --- mapping Object Ids to types


types to free identifiers in the current scope O(x) = T O(T/y) --- O modified to return T on argument y

M --- mapping methods to method signatures


M(C, f) = (A, B, D) means there is a method f(a:A, b:B): D defined in class C (or its ancestor) there maybe a method and an object id with the same name

K --- the class in which expression e appears


when SELF_TYPE is involved
18

Type rules
O, M, K e1 : Int O, M, K e2 : Int O, M, K e1 + e2 : Int Rules give templates describing how to type expressions By filling the templates, we can produce complete typings for expressions
O(x) = T O,M,C int_const : Int O,M,C x : T
19

Subtyping
Define a relation on classes
X X X Y if X inherits from Y X Z if X Y and Y Z Object D IO E B

A C B Object E D and D E

C A

20

Least Upper Bounds


In Cool, the least upper bound of two types is their least common ancestor in the inheritance tree

Object lub(A,B) = C lub(C,D) = D lub(C,E) = Object D C A B


21

IO E

Least Upper Bounds


Z is the least upper bound of X and Y lub(X, Y)=Z
X Z and Y Z Z is upper bound X Z and Y Z Z Z Z is the least upper bound

22

Dynamic and Static Types in Cool


class A { ... } class B inherits A { ... } class Main ( x: A new A ; ... x new B ; }

static type of x is A

dynamic type of x A

dynamic type of x is B

A variable of static type A can hold the value of static type B if B A

23

Dynamic and Static Types


The dynamic type of an object is the class that is used in the new expression
a runtime notion even languages that are statically typed have dynamic types

The static type of an expression captures all the dynamic types that the expression could have
a compile-time notion

24

Soundness
A type system is sound if e. dynamic_type(e) static_type(e)
whenever the inferred type of e is T

( denoted by e : T )
Then e evaluates to a value of type T in all executions of the program

We only want sound rules But some sound rules are better than others
25

Assign
O (x) = T0 O,M,K e1 : T1 T 1 T0 O,M,K x e1 : T1 class B { foo() : B }; class A inherits B { ...
ERROR OK OK
B

[Assign]

{ }

};

let x:A in x (new A).foo(); let x:B in x (new A).foo(); let x:Object in x (new A).foo();
26

Let Rule with Initialization


O,M,K e0: T O(T/x) e1 : T1 O,M,K let x: T e0 in e1 : T1 }; O,M,K e0: T O(T0/x) e1 : T1
T T0

Weak rule class B { foo():C { }

class A inherits B { ... let

};

x:B new A in x.foo();

O,M,K let x: T0 e0 in e1 : T1

Both rules are sound but more programs typecheck with the second one
uses subtyping
27

If-Then-Else
O,M,K e0 :Bool O,M,K e1: T1 O,M,K e2 : T2 O,M,K if e0 then e1 else e2 fi : lub(T1, T2)

foo(a:A, b:B, c:C, e:E) : D { if (a < b) then e else c fi } lub(E,C) = Object Is Object D ? ERROR A

Object D C B IO E

28

Case
O,M,K e : T O[X/x],M,K e1: E O[Y/y],M,K e2: F O[Z/z],M,K e3: G O,M,K case e of x: X =>e1; y:Y =>e2 ; z:Z=>e3 esac : lub(E,F,G)

foo(d:D) : D { case d of x : IO => let a:A(new A) in x; A y : E => (new B); z : C => z; esac }; lub(A,B,C) = C and C D OK

Object D C A B IO E

29

Dispatch
O, M c : C O, M a : A O, M b : B M(C, f) = (A1, B1, D1) A A1 , B

[Dispatch]

B1

O, M, K c.f(a, b): D1

class C1 { foo(a:A1, b:B1) : D1 { new D1 ; }; }; (new C).foo( (new A) , (new B) );


30

Static Dispatch
O, M, K c : C O, M, K a : A O, M, K b : B M(C, f) = (A1, B1, D1) A A1, B O, M, K B1, C C1 c@C1.f(a, b): D1 [StaticDispatch]

class C1 { foo(a:A1, b:B1) : D1 { new D1 ; }; }; (new C)@C1.foo( (new A) , (new B) );


31

SELF_TYPE Example
class A { foo() : A { self } ; }; class B inherits A { ... }; class Main { B x (new B).foo(); }; }; class B inherits A { ... }; class Main { B x (new B).foo(); }; class A { foo() : SELF_TYPE { self } ;

ERROR

OK

What can be a dynamic type of object returned by foo() ?


any subtype of A
32

SELF_TYPE
Research idea Helps type checker to accept more correct programs C,M,K (new A).foo() : A C,M,K (new B).foo() : B SELF_TYPE is NOT a dynamic type Meaning of SELF_TYPE depends on where it appears textually
33

Where can SELF_TYPE appear ?


Parser checks that SELF_TYPE appears only where a type is expected
How ?

But SELF_TYPE is not allowed everywhere a type can appear

34

Where can SELF_TYPE appear ?


class T1 inherits T2 { }
T1, T2 cannot be SELF_TYPE attribute let case

x : SELF_TYPE

new SELF_TYPE foo@T(e)

creates an object of the same type as self T cannot be SELF_TYPE only T2 can be SELF_TYPE

foo(x:T1):T2 {}

35

new Example
class A { foo() : A { new SELF_TYPE }; class B inherits A { } (new A).foo(); creates A object (new B).foo(); creates B object };

36

Subtyping for SELF_TYPE


SELF_TYPEc SELF_TYPEc

SELF_TYPEc C It is always safe to replace SELF_TYPEc with C SELF_TYPEc T if C T

T SELF_TYPEc is always false


because SELF_TYPEc can denote any subtype of C
37

lub(T,T) for SELF_TYPE


lub(SELF_TYPEc, SELF_TYPEc) = SELF_TYPEc lub(T, SELF_TYPEc) = lub(T,C)
the best we can do

38

New Rules
O, M, K self : SELF_TYPEk

O, M, K

new SELF_TYPE : SELF_TYPEk

39

Other rules
A use of SELF_TYPE refers to any subtype of the current class Except in dispatch
because the method return type of SELF_TYPE might have nothing to do with the current class

40

Dispatch
O, M c : C O, M a : A O, M b : B M(C, foo) = (A1, B1, D1) A A1 , B

B1, D1 SELF_TYPE

O, M, K c.foo(a, b) : D1

O, M c : C O, M a : A O, M b : B M(C, foo) = (A1, B1, SELF_TYPE) A A1 , B

which class is used to find the declaration of foo() ? It is safe to use the class where the dispatch appears
B1
41

O, M, K c.foo(a, b): C

Example
class A { foo() : A { self }; }; class B inherits A { } (new A).foo(); returns A object (new B).foo(); returns B object

42

Static Dispatch
O, M, K c : C O, M, K a : A O, M, K b : B M(C, f) = (A1, B1, D1) A A1, B O, M, K B1, C C1, D1 SELF_TYPE c.f@C1(a, b): D1

O, M, K c : C O, M, K a : A

if we dispatch a method returning SELF_TYPE in class C1, do we get back C1 ? No. SELF_TYPE is the type of self, which may be a subtype of the class in which the method appears

O, M, K b : B M(C, f) = (A1, B1, SELF_TYPE) A A1, B O, M, K B1, C C1 c@C1.f@(a, b): C


43

SELF_TYPE Example
class A { deligate : B; callMe() : ??? SELF_TYPE { deligate.callMe(); } ; }; class B { callMe() : SELF_TYPE { self }; }; class Main { A a (new A).callMe(); };
44

ERROR

Error Recovery
Error detection is easy What type is assigned to an expression with no legitimate type ?
influences type of enclosing expressions cascading errors

let y : Int x + 2 in y + 3 Better solution: special type No_Type


inheritance graph can be cyclic
45

Major Semantic Tasks


Inheritance graph construction and checking Symbol table construction
construct symbol table for features using inheritance tree assign enclosing scope for each AST node

Scope checking
check program using symbol tables

Check for Main class and main method Type checking


uses inheritance graph and symbol tables assign type for each AST node
Remaining Semantic Checks
checks that require type + symbol table information combined

Disclaimer: This is a nave phase ordering. Phases could be mixed, combined, optimized, re-ordered etc.
46

You might also like