0% found this document useful (0 votes)
2 views

lecture10

The lecture discusses type systems, focusing on the expressiveness of static type systems and the introduction of SELF_TYPE in the COOL programming language to enhance type checking. It highlights the limitations of traditional type systems and how SELF_TYPE allows methods to return types that correspond to the class of the object invoking them. Additionally, the lecture addresses error recovery strategies in semantic analysis, proposing the use of a No_type for ill-typed expressions to prevent cascading errors.

Uploaded by

menber988
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

lecture10

The lecture discusses type systems, focusing on the expressiveness of static type systems and the introduction of SELF_TYPE in the COOL programming language to enhance type checking. It highlights the limitations of traditional type systems and how SELF_TYPE allows methods to return types that correspond to the class of the object invoking them. Additionally, the lecture addresses error recovery strategies in semantic analysis, proposing the use of a No_type for ill-typed expressions to prevent cascading errors.

Uploaded by

menber988
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

Type Checking II

CS143
Lecture 10

Instructor: Fredrik Kjolstad


Slide design by Prof. Alex Aiken, with modifications
1
Lecture Outline

• Type systems and their expressiveness

• Type checking with SELF_TYPE in COOL

• Error recovery in semantic analysis

2
Expressiveness of Static Type Systems

• Static type systems detect common errors

• But some correct programs are disallowed


– Some argue for dynamic type checking instead
– Others argue for more expressive static type checking

• But more expressive type systems are more


complex

3
Dynamic and Static Types

• The dynamic type of an object is the class C that


is used in the “new C” expression that created it
– A run-time notion
– Even languages that are not statically typed have the
notion of dynamic type

• The static type of an expression captures all


dynamic types the expression could have
– A compile-time notion

4
Dynamic and Static Types. (Cont.)

• In early type systems the set of static types


correspond directly with the dynamic types

• Soundness theorem: for all expressions E


dynamic_type(E) = static_type(E)
(in all executions, E evaluates to values of the type
inferred by the compiler)

• This gets more complicated in advanced type


systems

5
Dynamic and Static Types in COOL

class A { … }
class B inherits A {…}
class Main { Here, x’s value has
x has static x:A ← new A; dynamic type A
type A … Here, x’s value has
x ← new B; dynamic type B

}

• A variable of static type A can hold values of static


type B, if B ≤ A
6
Dynamic and Static Types

Soundness theorem for the Cool type system:


∀ E. dynamic_type(E) ≤ static_type(E)

Why is this Ok?


– All operations that can be used on an object of type A
can also be used on an object of type B ≤ A
• Such as fetching the value of an attribute
• Or invoking a method on the object
– Subclasses only add attributes or methods
– Methods can be redefined but with same type!

7
An Example

class Count { • Class Count incorporates


i : int ← 0; a counter
inc () : Count {
{ • The inc method works for
i ← i + 1; any subclass
self;
}
}; • But there is problem
}; lurking in the type system

8
An Example (Cont.)

• Consider a subclass Stock of Count

class Stock inherits Count {


name : String; -- name of item
};
• And the following use of Stock:

class Main {
Stock a ← (new Stock).inc (); Type checking error !
… a.name …
};
9
What Went Wrong?

• (new Stock).inc() has dynamic type Stock

• So it is legitimate to write
Stock a ← (new Stock).inc ()

• But this is not well-typed


– (new Stock).inc() has static type Count
– inc () : Count {…}

• The type checker loses type information


– This makes inheriting inc useless
– So, we must redefine inc for each of the subclasses, with a
specialized return type

10
SELF_TYPE to the Rescue

• We will extend the type system

• Insight:
– inc returns “self”
– Therefore the return value has same type as “self”
– Which could be Count or any subtype of Count!

• Introduce the keyword SELF_TYPE to use for the return


value of such functions
– We will also need to modify the typing rules to handle
SELF_TYPE

11
SELF_TYPE to the Rescue (Cont.)

• SELF_TYPE allows the return type of inc to change when


inc is inherited

• Modify the declaration of inc to read


inc() : SELF_TYPE { … }

• The type checker can now prove:


C,M ⊢ (new Count).inc() : Count
C,M ⊢ (new Stock).inc() : Stock

• The program from before is now well typed

12
Notes About SELF_TYPE

• SELF_TYPE is not a dynamic type


– It is a static type
– It helps the type checker to keep better track of types
– It enables the type checker to accept more correct
programs

• In short, having SELF_TYPE increases the


expressive power of the type system

13
SELF_TYPE and Dynamic Types (Example)

• What can be the dynamic type of the object


returned by inc?
– Answer: whatever could be the type of “self”

class A inherits Count { } ;


class B inherits Count { } ;
class C inherits Count { } ;
(inc could be invoked through any of these classes)

– Answer: Count or any subtype of Count

14
SELF_TYPE and Dynamic Types (Example)

• In general, if SELF_TYPE appears textually in the class C


as the declared type of E then
dynamic_type(E) ≤ C

• Note: The meaning of SELF_TYPE depends on where it


appears
– We write SELF_TYPEC to refer to an occurrence of SELF_TYPE
in the body of C

• This suggests a typing rule:


SELF_TYPEC ≤ C (*)

15
Type Checking

• Rule (*) has an important consequence:


– In type checking it is always safe to replace
SELF_TYPEC by C

• This suggests one way to handle SELF_TYPE :


– Replace all occurrences of SELF_TYPEC by C

• This would be correct but it is like not having


SELF_TYPE at all

16
Operations on SELF_TYPE

• Recall the operations on types


– T1 ≤ T2 T1 is a subtype of T2
– lub(T1,T2) the least-upper bound of T1 and T2

• We must extend these operations to handle


SELF_TYPE

17
Extending ≤

Let T1 and T2 be any types but SELF_TYPE


There are four cases in the definition of ≤

1. SELF_TYPEC ≤ SELF_TYPEC
• In Cool we never need to compare SELF_TYPEs coming from
different classes

2. SELF_TYPEC ≤ T1 if C ≤ T1
• SELF_TYPEC can be any subtype of C
• This includes C itself
• Thus this is the most flexible rule we can allow

18
Extending ≤ (Cont.)

3. T1 ≤ SELF_TYPEC always false


Note: SELF_TYPEC can denote any subtype of C.

4. T1 ≤ T2 (according to the rules from before)

Based on these rules we can extend lub …

19
Extending lub(T,T’)

Let T1 and T2 be any types but SELF_TYPE


Again there are four cases:
1. lub(SELF_TYPEC, SELF_TYPEC) = SELF_TYPEC

2. lub(SELF_TYPEC, T1) = lub(C, T1)


This is the best we can do because SELF_TYPEC ≤ C

3. lub(T1, SELF_TYPEC) = lub(C, T1)

4. lub(T1, T2) defined as before

20
Where Can SELF_TYPE Appear in COOL?

• The parser checks that SELF_TYPE appears only


where a type is expected

• But SELF_TYPE is not allowed everywhere a type can


appear:

1. class T inherits T’ {…}


• T, T’ cannot be SELF_TYPE
2. x : T
• T can be SELF_TYPE
• An attribute whose type is ≤ SELF_TYPEC

21
Where Can SELF_TYPE Appear in COOL?

3. let x : T in E
• T can be SELF_TYPE
• x has a type ≤ SELF_TYPEC

4. new T
• T can be SELF_TYPE
• Creates an object of the same type as self

5. m@T(E1,…,En)
• T cannot be SELF_TYPE

22
Where Can SELF_TYPE Not Appear in COOL?

6. m(x : T) : T’ { … }
• Only T’ can be SELF_TYPE !

What could go wrong if T were SELF_TYPE?


class A { foo(x : SELF_TYPE) : Bool {…}; };
class B inherits A {
b : int;
foo(x : SELF_TYPE) : Bool { … x.b …}; };

let x : A ← new B in … x.foo(new A); …
… 23
Typing Rules for SELF_TYPE

• Since occurrences of SELF_TYPE depend on the


enclosing class we need to include that context
during type checking

• Recall the form of a typing judgment:


O,M,C ⊢ e : T
(An expression e occurring in the body of C has
static type T given a variable type environment O
and method signatures M)

24
Type Checking Rules

• The next step is to design type rules using SELF_TYPE for each
language construct

• Most of the rules remain the same except that ≤ and lub are the new
ones

• Example:

O(Id) = T0
O,M,C ⊢ e1 : T0
T1 ≤ T0
O,M,C ⊢ Id ← e1 : T1
25
What is Different?

• Recall the old rule for dispatch

O,M,C ⊢ e0 : T0

O,M,C ⊢ en : Tn
M(T0, f) = (T'1,…,T'n,T'n+1)
T'n+1 ≠ SELF_TYPE
Ti ≤ T'i 1≤i≤n
O,M,C ⊢ e0.f(e1,…,en) : T'n+1

26
What is Different?

• If the return type of the method is SELF_TYPE


then the type of the dispatch is the type of the
dispatch expression:

O,M,C ⊢ e0 : T0

O,M,C ⊢ en : Tn
M(T0, f) = (T'1,…,T'n, SELF_TYPE)
Ti ≤ T'i 1≤i≤n
O,M,C ⊢ e0.f(e1,…,en) : T0
27
What is Different?

• Note this rule handles the Stock example

• Formal parameters cannot be SELF_TYPE

• Actual arguments can be SELF_TYPE


– The extended ≤ relation handles this case

• The type T0 of the dispatch expression could be


SELF_TYPE
– Which class is used to find the declaration of f?
– Answer: it is safe to use the class where the dispatch appears

28
Static Dispatch

• Recall the original rule for static dispatch

O,M,C ⊢ e0 : T0

O,M,C ⊢ en : Tn
T0 ≤ T
M(T, f) = (T1’,…,Tn’,Tn+1’)
Tn+1’ ≠ SELF_TYPE
Ti ≤ Ti ’ 1≤i≤n
O,M,C ⊢ [email protected](e1,…,en) : Tn+1’
29
Static Dispatch

• If the return type of the method is SELF_TYPE


we have:

O,M,C ⊢ e0 : T0

O,M,C ⊢ en : Tn
T0 ≤ T
M(T, f) = (T1’,…,Tn’,SELF_TYPE)
Ti ≤ Ti ’ 1 ≤ i ≤ n
O,M,C ⊢ [email protected](e1,…,en) : T0
30
Static Dispatch

• Why is this rule correct?

• If we dispatch a method returning SELF_TYPE in


class T, don’t we get back a T?

• No. SELF_TYPE is the type of the self parameter,


which may be a subtype of the class in which the
method appears

31
New Rules

• There are two new rules using SELF_TYPE

O,M,C ⊢ self : SELF_TYPEC

O,M,C ⊢ new SELF_TYPE : SELF_TYPEC


• There are a number of other places where
SELF_TYPE is used

32
Summary of SELF_TYPE

• The extended ≤ and lub operations can do a lot of the


work.

• SELF_TYPE can be used only in a few places. Be sure it


isn’t used anywhere else.

• A use of SELF_TYPE always refers to any subtype of the


current class
– The exception is the type checking of dispatch. The method return
type of SELF_TYPE might have nothing to do with the current
class

33
Why Cover SELF_TYPE ?

• SELF_TYPE is a research idea


– It adds more expressiveness to the type system

• SELF_TYPE is itself not so important


– except for the project

• Rather, SELF_TYPE is meant to illustrate that type


checking can be quite subtle

• In practice, there should be a balance between the


complexity of the type system and its expressiveness

34
Error Recovery

• As with parsing, it is important to recover from type errors

• Detecting where errors occur is easier than in parsing


– There is no reason to skip over portions of code

• The Problem:
– What type is assigned to an expression with no legitimate type?
– This type will influence the typing of the enclosing expression

35
Error Recovery Attempt

• Assign type Object to ill-typed expressions

let y : Int ← x + 2 in y + 3
• Assume x is undeclared, then its type is Object
• But now we have Object + Int
• This will generate another typing error
• We then say that that Object + Int = Object
• Then the initializer’s type will not be Int
⇒ a workable solution but with cascading errors
36
Better Error Recovery

• We can introduce a new type called No_type for use with ill-typed
expressions

• Define No_type ≤ C for all types C

• Every operation is defined for No_type


– With a No_type result

• Only one typing error for:

let y : Int ← x + 2 in y + 3

37
Notes

• A “real” compiler would use something like


No_type

• However, there are some implementation issues


– The class hierarchy is not a tree anymore

• The Object solution is fine in the class project

38

You might also like