0% found this document useful (0 votes)
46 views55 pages

PLDI Week 10 More Typing

This document discusses type safety for programming languages. It covers type checking rules for features like functions, integers, tuples, arrays, and references. It also discusses type soundness and how types can be viewed as sets of values.

Uploaded by

Victor Zhao
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)
46 views55 pages

PLDI Week 10 More Typing

This document discusses type safety for programming languages. It covers type checking rules for features like functions, integers, tuples, arrays, and references. It also discusses type soundness and how types can be viewed as sets of values.

Uploaded by

Victor Zhao
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/ 55

CS4212: Compiler Design

Week 10: Type Safety


Typing for Advanced Features

Ilya Sergey
[email protected]

ilyasergey.net/CS4212/
Simply-typed Lambda Calculus with Integers
• For the language in “stlc.ml” we have five inference rules:

INT VAR ADD


x :T ∈ G G ⊢ e1 : int G ⊢ e2 : int

G ⊢ i : int G ⊢ x :T G ⊢ e1 + e2 : int

FUN APP
G, x : T ⊢ e : S G ⊢ e1 : T -> S G ⊢ e2 : T

G ⊢ fun (x:T) -> e : T -> S G ⊢ e1 e2 : S

• Note how these rules correspond to the OCaml code.


Implementing a Type Checker
for Lambda Calculus

See stlc.ml
Exercise

• Implement the missing parts of the type-checker


Notes about this Type Checker

• In the interpreter, we only evaluate the body of a function when it's applied.

• In the type checker, we always check the body of the function (even if it's never applied.)
– We assume the input has some type (say t1) and reflect this in the type of the function (t1 -> t2).

• Dually, at a call site (e1 e2), we don't know what closure we're going to get as e1.
– But we can calculate e1's type, check that e2 is an argument of the right type, and also determine
what type will (e1 e2) have.

• Question: Why is this a valid approximation of the dynamic program behaviour?


Contexts and Inference Rules

• Need to keep track of contextual information.


– What variables are in scope?
– What are their types?
– What information doe we have about each syntactic construct?

• What relationships are there among the syntactic objects?


– e.g. is one type a subtype of another?

• How do we describe this information?


– In the compiler there's a mapping from variables to information we know about them – the "context".
– The compiler has a collection of (mutually recursive) functions that follow the structure of the syntax.
Why Inference Rules?
• They are a compact, precise way of specifying language properties.
– E.g. ~20 pages for full Java vs. 100’s of pages of prose Java Language Spec.
– Check out oat-v1-typing.pdf

• Inference rules correspond closely to the recursive AST traversal that implements them

• Type checking (and type inference) is nothing more than attempting


to prove a judgment (G ⊢ e : t) by searching backwards through the rules.

• Strong mathematical foundations


– The “Curry-Howard correspondence”:
– Programming Language ~ Logic,
– Program ~ Proof,
– Type ~ Proposition

• Talk to me you’re interested in type systems!


Types and Type Safety
Type Safety

Theorem: (type soundness of simply typed lambda calculus with integers)

If ⊢ e : t then there exists a value v such that e ⇓ v .

"Well typed programs do not go wrong."


– Robin Milner, 1978

• Note: this is a very strong property.


– Well-typed programs cannot "go wrong" by trying to execute undefined code
(such as 3 + (fun x -> 2))
– Simply-typed lambda calculus is guaranteed to terminate!
(i.e. it isn't Turing complete)
Tuples
• ML-style tuples with statically known number of products:
• First: add a new type constructor: T1 * … * Tn

TUPLE
G ⊢ e1 : T1 … G ⊢ en : Tn

G ⊢ (e1, …, en) : T1 * … * Tn

PROJ
G ⊢ e : T1 * … * Tn 1≤i≤n

G ⊢ #i e : Ti
A note on Curry-Howard Correspondence

11
Arrays
• Array constructs are not hard
• First: add a new type constructor: T[]

G ⊢ e1 : int G ⊢ e2 : T e1 is the size of the newly


NEW
allocated array. e2
initialises the elements of
G ⊢ new T[e1](e2) : T[] the array.

INDEX
G ⊢ e1 : T[] G ⊢ e2 : int

G ⊢ e1[e2] : T Note: These rules don’t


ensure that the array index
UPDATE
is in bounds – that should
G ⊢ e1 : T[] G ⊢ e2 : int G ⊢ e3 : T be checked dynamically.

G ⊢ e1[e2] = e3 ok
References
• OCaml-style references (note that in OCaml references are expressions)
• First, add a new type constructor: T ref

REF G ⊢ e :T

G ⊢ ref e : T ref

DEREF
G ⊢ e : T ref

G ⊢ !e : T
ASSIGN Note the similarity with the
rules for arrays…
G ⊢ e1 : T ref E ⊢ e2 : T

G ⊢ e1 := e2 : unit
Well-Formed Types
• In languages with type definitions, need additional rules to define well-formed types
• Judgements take the form H ⊢ t
– H is set of type names

– t is atype ⊢
– H⊢t
means

“Assuming
⊢ H lists well-formed types, t is a well-formed type”

⊢ 1 ⊢ 2 ∈
⊢ 1 ⊢ 2 ⊢ ∈
⊢ ⊢ ⊢ ⊢ ⊢⊢ 1 → ⊢
→2 1 2

• Note: also need to modify the typing rules and judgements. E.g.,

⊢ ⊢1 1 , Γ{ $→$→ 1 }
, Γ{ 1 }⊢⊢ : : 22
⊢⊢
, Γ, Γ ( ( : :1 )1 ) : : 11→→ 22
Type-Checking Statements
• In languages with statements, need additional rules to define well-formed statements

• E.g.,judgements may take the form H;G;rt ⊢ s


– H maps type names to their definitions
– G is a type environment (variables -> types)
– rt is a type
– H;G;rt ⊢ s ; Γ; ⊢
means
Γ →
“with type definitions H, assuming type environment G, s is avalid statement within
the context
; Γ; ⊢ of a function that returns a value of type rt” Γ

Γ⊢ : Γ( ) Γ⊢ : Γ⊢ : ; Γ{ #→ }; ⊢ 2
; Γ; ⊢ := ; Γ; ⊢ ; Γ; ⊢ = ; 2
Type Safety For General Languages
Theorem: (Type Safety)

If ⊢ P : t is a well-typed program, then either:


(a) the program terminates in a well-defined way, or
(b) the program continues computing forever

• Well-defined termination could include:


– halting with a return value
– raising an exception
• Type safety rules out undefined behaviours:
– abusing "unsafe" casts: converting pointers to integers, etc.
– treating non-code values as code (and vice-versa)
– breaking the type abstractions of the language (e.g., via Java/Ruby reflection)
• What is "defined" depends on the language semantics…
A good place for a break

17
Types as Sets
What are types, anyway?
• A type is just a predicate on the set of values in a system.
– For example, the type “int” can be thought of as a boolean function that returns “true”
on integers and “false” otherwise.
– Equivalently, we can think of a type as just a subset of all values.

• For efficiency and tractability, the predicates are usually taken to be very simple.
– Types are an abstraction mechanism

• We can easily add new types that distinguish different subsets of values:

type tp =
| IntT (* type of integers *)
| PosT | NegT | ZeroT (* refinements of ints *)
| BoolT (* type of booleans *)
| TrueT | FalseT (* subsets of booleans *)
| AnyT (* any value *)
Modifying the typing rules
• We need to refine the typing rules too…
• Some easy cases:
– Just split up the integers into their more refined cases:

P-INT N-INT ZERO


i>0 i<0

G ⊢ i : Pos G ⊢ i : Neg G ⊢ 0 : Zero

• Same for booleans:

TRUE FALSE

G ⊢ true : True G ⊢ false : False


What about “if”?
• Two cases are easy:

IF-T IF-F G ⊢ e1 : False E ⊢ e3 : T


G ⊢ e1 : True G ⊢ e2 : T

G ⊢ if (e1) e2 else e3 : T G ⊢ if (e1) e2 else e3 : T

• What happens when we don’t know statically which branch will be taken?
• Consider the type checking problem:

x:bool ⊢ if (x) 3 else -1 : ?

• The true branch has type Pos and the false branch has type Neg.
– What should be the result type of the whole if?
Downcasting
• What happens if we have an Int but need something of type Pos?
– At compile time, we don’t know whether the Int is greater than zero.
– At run time, we do.

• Add a “checked downcast”


G ⊢ e1 : Int G, x : Pos ⊢ e2 : T2 G ⊢ e3 : T3

G ⊢ ifPos (x = e1) e2 else e3 : LUB(T2, T3)

• At runtime, ifPos checks whether e1 is > 0.


If so, branches to e2 and otherwise branches to e3.
• Inside the expression e2, x is the name for e1’s value, which is known to be
strictly positive because of the dynamic check.
• Note that such rules force the programmer to add the appropriate checks
– We could give integer division the type: Int → NonZero → Int
Subtyping and Upper Bounds
• If we think of types as sets of values, we have a natural inclusion relation: Pos ⊆ Int
• This subset relation gives rise to a subtype relation: Pos <: Int (sometimes also typeset as ≤)
• Such inclusions give rise to a subtyping hierarchy:

Any :>
<:

Int Bool
<: :> : :>
<

:>
Neg Zero Pos True False

• Given any two types T1 and T2, we can calculate their least upper bound (LUB)
according to the hierarchy.
– Example: LUB(True, False) = Bool, LUB(Int, Bool) = Any
– Note: might want to add types for “NonZero”, “NonNegative”, and “NonPositive” so that set
union on values corresponds to taking LUBs on types.
“If” Typing Rule Revisited
• For statically unknown conditionals, we want the return value to be the LUB of
the types of the branches:

IF-BOOL
G ⊢ e1 : bool E ⊢ e2 : T1 G ⊢ e3 : T2

G ⊢ if (e1) e2 else e3 : LUB(T1,T2)

• Note that LUB(T1, T2) is the most precise type (according to the hierarchy)
that is able to describe any value that has either type T1 or type T2.

• In math notation, LUB(T1, T2) is sometimes written T1 ⋁ T2

• LUB is also called the join operation.


Subtyping Hierarchy

• A subtyping hierarchy:

Any :>
<:

Int Bool
<: :> : :>
<

:>
Neg Zero Pos True False

• The subtyping relation is a partial order:


– Reflexive: T <: T for any type T
– Transitive: T1 <: T2 and T2 <: T3 then T1 <: T3
– Antisymmetric: It T1 <: T2 and T2 <: T1 then T1 = T2
Soundness of Subtyping Relations
• We don’t have to treat every subset of the integers as a type.
– e.g., we left out the type NonNeg

• A subtyping relation T1 <: T2 is sound if it approximates the underlying semantic subset relation.

• Formally: write ⟦T⟧ for the subset of (closed) values of type T


– i.e. ⟦T⟧ = {v | ⊢ v : T}
– e.g. ⟦Zero⟧ = {0}, ⟦Pos⟧ = {1, 2, 3, …}

• If T1 <: T2 implies ⟦T1⟧ ⊆ ⟦T2⟧, then T1 <: T2 is sound.


– e.g. Pos <: Int is sound, since {1,2,3,…} ⊆ {…,-3,-2,-1,0,1,2,3,...}
– e.g. Int <: Pos is not sound, since it is not the case that
{…,-3,-2,-1,0,1,2,3,...}⊆ {1,2,3,…}
Subsumption Rule
• When we add subtyping judgments of the form T <: S we can uniformly
integrate it into the type system generically:

SUBSUMPTION
G ⊢ e : T T <: S

G⊢e:S

• Subsumption allows any value of type T to be treated as an S whenever T <: S.

• Adding this rule makes the search for typing derivations more difficult – this rule
can be applied anywhere, since T <: T.
– But careful engineering of the typing system can incorporate the subsumption rule into
a deterministic algorithm. (See, e.g., the Oat type system)
Subtyping in the Wild
Extending Subtyping to Other Types

• What about subtyping for tuples?


– Intuition: whenever a program expects T1 <: S1 T2 <: S2
something of type S1 * S2, it is sound
to give it a T1 * T2. (T1 * T2) <: (S1 * S2)
– Example: (Pos * Neg) <: (Int * Int)

• What about functions?

• When is T1 → T2 <: S1 → S2 ?
Subtyping for Function Types
• One way to see it:

Expected function

S1 T1 Actual function T2 S2

• Need to convert an S1 to a T1 and T2 to S2, so the argument type is


contravariant and the output type is covariant.

S1 <: T1 T2 <: S2

(T1 → T2) <: (S1 → S2)


Immutable Records
• Record type: {lab1:T1; lab2:T2; … ; labn:Tn}
– Each labi is a label drawn from a set of identifiers.

RECORD
G ⊢ e1 : T1 G ⊢ e2 : T2 … G ⊢ en : Tn

G ⊢ {lab1 = e1; lab2 = e2; … ; labn = en} : {lab1:T1; lab2:T2; … ; labn:Tn}

PROJECTION
G ⊢ e : {lab1:T1; lab2:T2; … ; labn:Tn}

G ⊢ e.labi : Ti
Immutable Record Subtyping
• Depth subtyping:
– Corresponding fields may be subtypes

DEPTH
T1 <: U1 T2 <: U2 … Tn <: Un

{lab1:T1; lab2:T2; … ; labn:Tn} <: {lab1:U1; lab2:U2; … ; labn:Un}

• Width subtyping:
– Subtype record may have more fields:

WIDTH
m≤n

{lab1:T1; lab2:T2; … ; labn:Tn} <: {lab1:T1; lab2:T2; … ; labm:Tm}


Mutability and Subtyping
NULL
• What is the type of null?

• Consider:
int[] a = null; // OK?
int x = null; // OK? (nope)
string s = null; // OK?
NULL
G ⊢ null : r
• Null has any reference type
– Null is generic

• What about type safety?


– Requires defined behavior when dereferencing null
e.g. Java's NullPointerException
– Requires a safety check for every dereference operation
Subtyping and References
• What is the proper subtyping relationship for references and arrays?

• Suppose we have NonZero as a type and the division operation has type:
Int → NonZero → Int
– Recall that NonZero <: Int

• Should (NonZero ref) <: (Int ref) ?

• Consider this program:

Int bad(NonZero ref r) {


Int ref a = r; ( * OK because (NonZero ref <: Int ref*)
a := 0; (* OK because 0 : Zero <: Int *)
return (42 / !r) (* OK because !r has type NonZero *)
}
Mutable Structures are Invariant
• Covariant reference types are unsound (well-typed programs do go wrong)
– As demonstrated in the previous example
• Contravariant reference types are also unsound
– i.e. If T1 <: T2 then ref T2 <: ref T1 is also unsound
– Exercise: construct a program that breaks contravariant references.
• Moral: Mutable structures are invariant:
T1 ref <: T2 ref implies T1 = T2
⊢ 1 <: 1 ... ⊢ <: ⊢ <: ⊢ <:
• Same holds for arrays,
⊢ 1 ∗ ... ∗ <: 1 ∗ ... ∗ OCaml-style
⊢ mutable
<: records, object fields,
⊢ etc.
<:
– Note: Java and C# get this wrong. They allows covariant array subtyping, but then
compensate by adding a dynamic check on every array update!
Γ = [ #→ ]

<:
Γ⊢ : <:
Γ⊢ : Γ⊢0: Γ ⊢ −1 :
Γ ⊢ [0] := −1
Reminder: Subtyping for Function Types
• One way to see it:

Expected function

S1 T1 Actual function T2 S2

• Need to convert an S1 to a T1 and T2 to S2, so the argument type is


contravariant and the output type is covariant.

S1 <: T1 T2 <: S2

(T1 → T2) <: (S1 → S2)


Another Way to See It
• We can think of a reference cell as an immutable record (object) with two
functions (methods) and some hidden state:
T ref ≃ {get: unit → T; set: T → unit}
– get returns the value hidden in the state.
– set updates the value hidden in the state.

• When is T ref <: S ref?


• Records are like tuples: subtyping extends pointwise over each component.
• {get: unit → T; set: T → unit} <: {get: unit → S; set: S → unit}
– get components are subtypes: unit → T <: unit → S
set components are subtypes: T → unit <: S → unit
• From get, we must have T <: S (covariant return)
• From set, we must have S <: T (contravariant arg.)
• From T <: S and S <: T we conclude T = S.
Demo: Arrays in Java

• Check out https://github.com/cs4212/week-10-java-arrays

• The code shows the run-time issue with covariant array subtyping

39
Structural vs. Nominal Subtyping
Structural vs. Nominal Typing
• Is type equality / subsumption defined by the structure of the data or the name of the data?
• Example: type abbreviations (OCaml) vs. “newtypes” (a la Haskell)

(* OCaml: *)
type cents = int (* cents = int in this scope *)
type age = int

let foo (x:cents) (y:age) = x + y

(* Haskell: *)
newtype Cents = Cents Integer (* Integer and Cents are isomorphic, not identical. *)
newtype Age = Age Integer

foo :: Cents -> Age -> Int


foo x y = x + y (* Ill typed! *)

• Type abbreviations are treated “structurally” Newtypes are treated “by name”.
Nominal Subtyping in Java
• In Java, Classes and Interfaces must be named and their relationships explicitly declared:

(* Java: *)
interface Foo {
int foo();
}

class C { /* Does not implement the Foo interface */


int foo() {return 2;}
}

class D implements Foo {


int foo() {return 4230;}
}

• Similarly for inheritance: the subclass relation must be declared via the “extends” keyword.
– Typechecker still checks that the classes are structurally compatible
Oat’s Type System
Oat’s Treatment of Types
• Primitive (non-reference) types:
– int, bool
• Definitely non-null reference types: R
– (named) mutable structs with (right-oriented) width subtyping
– string
– arrays (including length information, per HW4)
• Possibly-null reference types: R?
– Subtyping: R <: R?
int sum(int[]? arr) {
– Checked downcast syntax if?:
var z = 0;
if? (int[] a = arr) {
for(var i = 0; i<length(a); i = i + 1;) {
z = z + a[i];
}
}
return z;
}
Full Oat Features
• Named structure types with mutable fields
– but using structural, width subtyping

• Typed function pointers

• Polymorphic operations: length and == / !=


– need special case handling in the typechecker

• Type-annotated null values: t null always has type t?

• Definitely-not-null values means we need an "atomic" array initialization syntax


– for example, null is not allowed as a value of type int[], so to construct a record
containing a field of type int[], we need to initialize it
– subtlety: int[][] cannot be initialized by default, but int[] can be
H;G;L;rt ` block1 ;r1
H;G;L;rt ` block2 ;r2 H;G;L0 ` vdecl1 ) L1 . . . H;G;Ln 1 ` vdecli ) Ln
Oat "Returns" Analysis
lhs not a global function id typ_if typ_vdecls
H; G; L; rt ` if(exp)
H; G; L `block lhs 1: elset block2 ) L; r1 ^ r2 H;G;L0 ` vdecl1 , .. ,vdecln ) Ln
0 H;G;L H;G;L 1 ` vdecl
1 ` vdecl
) L2 ) L2
H; G; L 0 ` exp : t H; G; L1 ; rt ` stmt ) L2 ; returns
H; G; L ` exp : ref 0 ?
0 • HType-safe,
` t  t statement-oriented imperative languages H;likeG; L Oat ` exp (or: Java)t x 62 L
H ` ref  ref typ_assn H;function
G; L ` : t x 62 L
H; G; L; rt ` lhs = exp; ) L; ? lhs not a global id exptyp_decl
H;G;L, x : ref ;rt must
` block ensure
1 ;r1 H;G;L;rtthat a function ` block2 ;r (always)
2 returns a value H;G;L H; of
G; ` Lthe
var
` appropriate
x
lhs = : exp
t ) L, x type.
: t ty
H;G;L ` vdecl )1 Lelse typ_ifq H;G;L 0 ` var x = exp ) L, x : t
L; rt ` if?(ref x = 1exp) block
– Does the returned expression's 2 block 2 )
typ_stmtdecl
L; r ^
type
1 r 2 match the one declaredH; G; L
by ` exp
the : t
function?
H; G; L1 ; rt ` vdecl; ) L2 ; ? H;G;L 0 ` vdecls ) L i H ` t0  t
H;
– G; DoL all ` exppaths : bool
through the H;G;L code return`appropriately? typ_assn
exp : (t1 , ..`, tblock;r
H; G; L ` H;G;L;rt n ) -> void 0 vdecls0 )
H;G;L Li H;1 )
` vdecl G; L;L1rt `. .lhs . =H;G;L exp; ) L; ?
n 1 ` vdecli ) Ln
H; G; L ` • Oat’s
exp 1 : t 0
statement
.. H; G; L checking
` exp n : t judgment
0 typ_while typ_vdecls
H; G;0 L; rt ` while(exp) 1
0 block ) L; ? n H;G;L H;G;L
0 ` vdecl1 ` 1vdecl ) L2 n ) Ln
, .. ,vdecl
H;G;L vdecl L . . typ_stmtdecl
. void H;G;L n 1 ` vdecl
H ` t1  t1 .. H ` tn  tn ` )
– takes the expected return type typ_scall as input: what type should H; G; 0 L1the
; rt `statement 1
vdecl; ) return
1L2 ; ? (or if none)
H; G; L; H;G;L 1 ` vdecls
rt ` exp(exp ) Ln2); ) L;H;
1 , .. ,exp ? G; L1 ; rt ` stmt ) L2 ; returns
H;–G; produces
L2 ` exp :a boolean bool flag as output: does the statement exp :H;G;L
H; G; L ` definitely (t1 , return?
.. , tn )0 -> ` vdecl1 , .. ,vdecln ) Ln
void
H; G; L ` exp : bool H; G; L ` exp : t 0 .. H; G; L ` exp : t 0
H; G; L 2 ; rt ` stmt ) L 3 ; ? lhs not
1 a global 1 function id n n
H;G;L;rt ` block1 ;r1 H t 0  t .. H t 0  t
H;G;L 2 ;rt
H;G;L;rt ` block;r
` block2 ;r2 H; G; L ; rt `
1 typ_for stmt `
) 1 LH;2 ; returns
1G; L ` `
lhs n: t n
typ_scall
typ_if H; G; L; rt
H; `G; exp(exp
L ` exp1 , ..
: ,exp
t 0 n ); ) L; ?
L1 ;G;
G; H; rt L;
` rtfor(vdecls;
` if(exp) block expopt 1
; stmt
else block
opt ) 2
block
) L;) r 1 ^L r
12
; ?
H ` H; t0 G;  Lt` exp : bool
H; G; L H; ` G;expL :`refexp 0 0
? : t H`t  t
0 lhs not a global function
typ_assn id
typ_retT H; G; L; H;G;L;rt
rt ` lhs = ` block
exp; )
1 ;r 1L; ?
H;` G; blockL2 ;r`2 lhs : t
0
H ` refH;  reft ` return exp; ) L; >
G; L; H;G;L;rt
H;G;L ` vdecl ) L 0 typ_if
H;G;L, x : ref ;rt ` block1 ;r1 H;G;L;rt ` block2 ;r2
typ_ifq
1
H; G; L; rt ` if(exp) block H; G;
1 else L
2 `
block 2 exp
) L;:r1 t^ r2
typ_stmtdecl
G; L; rtH; typ_retVoid H; G; L1 ; rt ` 0 vdecl; ) 0L2 ; ?
` G; L; void ` return;1 ) L; > 2 ) L; r1 ^ r2
if?(ref x = exp) block else block
H; G; L ` exp : ref ? H`t  t
H; G; L ` exp : bool H;
H G; L 0` exp
` ref ref : (t1 , .. , tn ) -> void t
H;G;L;rt ` block;r H; G; L exp : t 0H;..G;H; L; G;rt L ` lhs
exp = : exp;
t 0 ) L; ?
H;G;L, x : ref ;rt
` 1 ` block1 1 ;r1 H;G;L;rt `n blockn2 ;r2
`
typ_while 0 0 typ_ifq
H; G; L; rt ` while(exp) block ) L; ? H; G;HL;`rtt1`  t1 ..x = H
if?(ref exp) ` tblock
n 1 else tn block2 ) L; r1 ^ r2
H;G;L1 ` vdecl ) Ltyp_scall 2
H;G;L1 ` vdecls ) L2 H; G; L; rt `H;exp(exp G; L ` 1exp , .. ,exp
: bool n ); ) L; ? typ_
Example: Typing in Oat
Checking Derivations
• A derivation or proof tree has (instances of) judgments as its nodes and edges that connect
premises to a conclusion according to an inference rule.
• Leaves of the tree are axioms (i.e. rules with no premises)
– Example: the INT rule is an axiom
• Goal of the type checker: verify that such a tree exists.
• Example 1: Find a tree for the following program using the inference rules in Oat specification
var x1 = 0;
var x2 = x1 + x1;
x1 = x1 – x2;
return(x1);

Example 2: There is no tree for this ill-typed program:

int f() {
var x = int[] null;
x = new int[] {3,4};
return x[0];
}
CIS341 – Steve Zdancewic
Example Derivation
October 7, 2020
H;G;L;rt ` block;returns

H;G;L0 ;rt `ss stmt1 .. stmtn ) Ln ;r


This is an example derivation for the following OAT statements. Note that it is not a complete OAT
typ_block
H;G;L0 ;rt ` {stmt1 .. stmtn } ;r
program (it’s technically not even a block because it isn’t inside of braces).
Let S be the following OAT program: H;G;L0 ;rt `ss stmt1 .. stmtn ) Ln ;returns
H; G; L0 ; rt ` stmt1 ) L1 ; ?
var x1 = 0; D1 ...
var x1 = 0;
var x2 = x1 + x1; D2 H; G; Ln 2 ; rt ` stmtn 1 ) Ln 1 ; ?
var x2 = x1 + x1; H; G; Ln 1 ; rt ` stmtn ) Ln ; r
x1 = x1 - x2; D2 typ_stmts
x1 = x1 – x2; H;G;L0 ;rt `ss stmt1 .. stmtn 1 stmtn ) Ln ;r
return x1;
return(x1); D4
H;G `s tdecl
The following
H ` t1 .. H ` ti x1 .. xi distinct
typ_tdeclok
H;G `s struct S{ t1 x1 ; .. ;ti xi }
D1 D2 H;GD`3 f fdeclD4
typ_stmts
H;G; · ;int `ss var x1 = 0; var x2 = x1 + x2 ; x1 = x1 - x2 ; return x 1 ; ) x 1 : int, x 2 : int, · ; >
H;G;x : t , .. , x : t ;rt ` block;> x .. x distinct
1 1 i i 1 i
typ_fdeclok
H;G ` f rt f (t1 x1 , .. , ti xi ) block

D1 = H;G ` prog
typ_int x1 6 2 ·
H; G; · ` 0 : int typ_dempty
typ_decl H;G ` e
H;G;· ` var x1 = 0 ) ·, x1 : int
typ_stmtdecl
H;G ` prog
H; G; ·; int ` var x1 = 0; ) ·, x1 : int; ? typ_dgdecl
lhs not a global function id
program
H; G; L (it’s technically
` lhs : t not even a block because it isn’t inside of braces).
; G; L ` expLet
: tS be the following
H; G; L ` exp : t0 Example Derivation
OAT program:
H`t  t 0
var x1 = 0; H ` ref
typ_assn
H; G; L; rt ` lhs = exp; ) L; ?H;G;L1 ` vdecl ) L2 typ_null
var x2 = x1 + x1; H; G; L ` ref null : ref ?
x1H;G;L
= x1 1-`x2; vdecl ) L2 H; G; L ` exp : t x 62 L
typ_stmtdecl typ_bool_true typ_decl
H;return ` vdecl; ) H;
G; L1 ; rt x1; ; ?L ` true : bool
L2G; H;G;L ` var x = exp ) L, x : t

H; G; L ` exp : (t ,
The following
1 .. , t n ) -> void H;G;L 0 ` vdecls ) L typ_bool_false
i
var x1 = 0; 0 H; G; L ` false 0 : bool
H; G; L ` exp1 : t1 .. H; G; L ` expn : tn
var x2 = x1 + x1; H;G;L0 ` vdecl1 ) L1 . . . H;G;Ln 1 ` vdecli ) Ln
0
H ` t1  t1 .. H ` tn  tn 0 typ_int typ_vdec
x1 = x1 – x2; H; G; L D` integer D : int
typ_scall D H;G;L
D 0 ` vdecl1 , .. ,vdecl n ) Ln
G; L; rt ` exp(exp1 , .. ,expn ); ) L; ?
H; return(x1); 1 2 3 4
typ_stmt
H;G; · ;int `ss var x1 = 0; G; Lx
H;var 1 ;2rt=`x1stmt
+ x2 ;) 1 - x2 ; return x1 ; ) x1 : int, x2 : int, ·;>
x1 L=2 ;typ_string
xreturns
H; G; L ` exp : H; boolG; L ` string : string
H;G;L;rt ` block1 ;r1 lhs not a global function id
id : t 2 L
D1H;G;L;rt
= ` block2 ;r2 typ_local H; G; L ` lhs : t
H; G; L ` id : t typ_if H; G; L ` exp : t 0
; G; L; rt ` if(exp) block1 else block2 ) L; r1 ^ typ_int r2 x1 6 2 ·
H; G; · ` 0 : int 0  t
H ` ttyp_decl
0 id 62 L id : t 2 G typ_assn
; G; L ` exp : ref ? H;G;· ` var x1 = 0 ) ·typ_global , x1 : intH; G; L; rt ` lhs = exp; ) L; ?
0 H; G; L ` id : t typ_stmtdecl
` ref  ref H; G; ·; int ` var x = 0; ) ·, x : int; ?
1 1
H;G;L 1 ` vdecl ) L2
;G;L, x : ref ;rt ` blockH 1 t H;G;L;rt ` block2 ;r2
1 ;r` typ_stmtdecl
H; G; L1 ; rt ` vdecl; ) L2 ; ?
typ_ifq
L; rt ` if?(refD2 x== exp)H;block
G; L1 `else 1 : t21 ).. L;H;
expblock ^ rL2 ` expn : tn
r1 G;
H ` t1  t .. H ` tn  t H; G; L ` exp : (t1 , .. , tn ) -> void
` exp : bool
H; G; Ltyp_intOps H; G; L ` exp1 typ_carr
0
: t1 .. H; G; L ` expn : tn 0
H; G; L ` new t[]{exp , .. , exp } : t[] 0 0
lhs not a global function id typ_null
var x1 = 0;
H; G;varL `x2lhs : t H; G; L ` ref null : ref ?
; G; L ` exp : t
H; G;x1 L`
= x1 + x1;
: t0
exp- x2;
= x1 Example Derivation typ_bool_true
0
H ` t  tx1;
return H; G; L ` true : bool
Htyp_assn
` ref
H; G; L; rt `Thelhs = exp; )
following L; ?H;G;L1 ` vdecl ) L2 typ_null typ_bool_false
H; G; L ` ref null : ref ? H; G; L ` false : bool
H;G;L1 ` vdecl ) L2 H; G; L ` exp : t x 62 L
typ_stmtdecl
D1 D2 D3 typ_bool_true
D4 typ_int
typ_decl
H; G; L1 ; rt H;G;
` vdecl; ) H;
L G;
; ? L ` true : bool H;G;L ` H;
var G;
x L= `expinteger
) L, x:: tint
typ_stmts
· ;int ` ss var2 x 1 = 0; var x2 = x1 + x2 ; x1 = x1 - x2 ; return x1 ; ) x1 : int, x2 : int, ·;>

H; G; L ` exp : (t1 , .. , tn ) -> void H;G;L ` vdecls ) L typ_bool_false typ_string


H; G; L ` false 0
: bool i H; G; L ` string : string
H; G;varL ` x1 exp=1 :0;tD10 1 =.. H; G; L ` expn : tn0
var
0 x2 = x1 + x1;0 typ_int H;G;L · vdecl1 ) L1 . . . id
x1062`typ_int H;G;L
: t 2 Ln 1 ` vdecli ) Ln
H ` t1  t1 .. H ` tn  tnH; G; · ` 0 : int typ_local typ_vdec
x1 = x1 – x2; H; G; L ` integer
H;G;· ` var x1 = 0 ) ·, x1 : int
:
typ_scallint typ_decl
H;G;L 0 ` vdecl H;1 ,G; .. ,vdecl
L ` idn ) : tLn
G; L; rt ` exp(exp1 , .. ,expn ); ) L; ?
H; return(x1); typ_stmtdecl
H; G; ·; int ` var x1 = 0; ) ·, x1 : int; ? id 62 L id : t 2 G
H; G; L1 ; rt ` stmt ) L2 ;typ_string returns
H; G; L ` exp : H; bool G; L ` string : string typ_global
H; G; L ` id : t
H;G;L;rtD2 = ` block ;r
1 1 lhs not a global function id
id : t 2 L H ` t
H;G;L;rttyp_intOps ` block2 ;r2 typ_local H; G; L ` lhs : t
` +:(int, int) -> int
H; G; L ` id : t typ_if H; G;H; L G;
` Lexp` 1 exp: t1: t0.. H; G; L ` expn : tn
; G; L; rt `x1 : int
if(exp) block1 else
2 ·, x1 : int block
typ_local 2 ) x1 :L;
intr 2 ^
1
·, xr1 : int
2 H `H
typ_local t1 `t0 t ..t H ` tn  t
H; G; ·, x1 : int ` x1 : int H; G; ·, x1 : int ` x1 : int typ_car
id 6 2 L id : t 2 G typ_bop x 6 2 · , x : int
, .. ,L; typ_assn
:
H;H;
G;G; L; L lhs =t[]exp; exp t[]
0 2 1
; G; L ` exp : ref ? H; G; ·, x1 : int ` x1 + x1 : int typ_global rt `` new {exp1) ?n }
typ_decl
0 H;G;· H; ` varG; x2L= ` x1 ): ·,tx1 : int, x2 : int
x1 +id
` ref  ref H; G; ·, x1 : int; int ` var x2 = x1 + x1 ; ) ·, x1 : int,H;G;L H ` t typ_stmtdecl
x2 : int; ?1 ` vdecl ) L2
;G;L, x : ref ;rt ` blockH 1 ;r` 1 t H;G;L;rt ` block2 ;r2 H; G; L ` exp1 : int typ_stmtdecl
typ_ifqH; G; L1 ; rt ` vdecl; ) L2 ; ?
L; rt ` if?(ref x = exp)H;block G; L1 `else 1 : t21 ).. L;H;
expblock r1 G; ^ rL2 ` expn : tn t 2 {int, bool, r?} typ_newarray
H ` t1  t .. H ` tn  t H; G; L ` exp : (t ,
H; G; L01` new.. , t ) -> void
n t[exp1 ] : t[]
H; G; L ` exp : bool H; G; L ` exp1 typ_carr: t1 .. H; G; L ` expn : tn 0
H; G; L ` new t[]{exp , .. , exp } : t[]
0 0
lhs not a global function id typ_null
H; G; L ` lhs : t H; G; L ` ref null : ref ?
; G; L ` exp : t
H; G; L ` exp : t0 Example Derivation typ_bool_true
H`t  t 0 H; G; L ` true : bool
Htyp_assn
` ref
H; G; L; rt ` lhs = exp; ) L; ?H;G;L1 ` vdecl ) L2 typ_null typ_bool_false
H; G; L ` ref null : ref ? H; G; L ` false : bool
H;G;L1 ` vdecl ) L2 H; G; L ` exp : t x 62 L
typ_stmtdecl typ_bool_true typ_int
typ_decl
H; G;
H; G; L1 ; rt ` vdecl; ) L2 ; ? L ` true : bool H;G;L ` H;
varG;
x L= ` integer
exp ) L, x:: tint

H; G; L ` exp : (t1 , .. , tn ) -> void H;G;L ` vdecls ) L typ_bool_false typ_string


H; G; L ` false 0
: bool i H; G; L ` string : string
H; G;varL ` x1 exp=1 :0;t10 .. H; G; L ` expn : tn0
var x2 = x1 + x1; H;G;L0 ` vdecl1 ) L1 . . . idH;G;L : t 2 L n 1 ` vdecli ) Ln
0
H ` t1  t1 .. H ` tn  tn 0 typ_int typ_local typ_vdec
x1 = x1 – x2; H; G; L ` integer : int
typ_scall H;G;L0 ` vdeclH; 1 ,G;.. ,vdecl
L ` idn ) : tLn
G; L; rt ` exp(exp1 , .. ,expn ); ) L; ?
H; return(x1);
H; G; L1 ; rt ` stmt ) L2 ;typ_string returns id 62 L id : t 2 G
H; G; L ` exp : H; bool G; L ` string : string typ_global
D5 = H; G; L ` id : t
H;G;L;rt ` block1 ;r1 lhs not a global function id
id : t 2 L
typ_intOps H ` t
H;G;L;rt ` -:(int, block
`int) ->2int;r2 typ_local H; G; L ` lhs : t
x1 : int 2 ·, x1 : int, xH; : G;
int L ` id : t typ_if x : H;
int G;
2 H;·L, x`
G;: L exp
int, ` x1 :
exp
: int t 1: t 0.. H; G; L ` expn : tn
; G; L; rt ` if(exp) block1 else block22 ) L; r1typ_local ^ r2 2
H ` t
1
 0 t
2
.. H ` t typ_local
 t
H; G; ·, x1 : int, x2 : int ` x1 : int H; G; ·, x1 : int, H `2 t  t2
1 x : int ` x : int n
typ_car
id 6 2 L id : t 2 G , .. ,L;
typ_bop
typ_assn
?n } : t[]
H; G; ·, x1 : int, x2 : int ` x1 - x1 :H;
H; G;G; L; L lhs =t[]exp; exp
0 int
; G; L ` exp : ref ? typ_global rt `` new {exp1)
0
` ref  ref H; G; L ` id : t
H;G;L H `` tvdecl ) L
D3 = 1 2
;G;L, x : ref ;rt ` blockH 1 ;r` 1 t H;G;L;rt ` block2 ;r2 H; G; L ` exp1 : int typ_stmtdecl
x1 : int 2 ·, x1 : int, x2 : int typ_ifq H; G; L1 ; rt ` vdecl; ) L2 ; ?
L; rt ` if?(ref
G ` x1 not axglobal
= exp) H;block
function G;
id L1 ` else expblock
1 : t21 ).. L;H; r 1 G;
^ rL `
typ_local
2 exp n : t n t 2 { int,
sub_sub_int bool, r?
D }
H; G; ·, x1 : int, x2 : int ` x1 : int
H; G; L H ` int  int
` exp : (t , .. , t ) -> void
5
typ_newarray
HH;`G;t1·, x : intt, x ..
: int ; H
int `
` t
x n = 
x - t
x ; ) · , x : int , x : int H;
; ? G; L 1 ` new n t[exp1 ] :typ_assn t[]
H; G; L ` exp : bool 1 2 1 1 2
H; G; L ` exp1 typ_carr
1 2 0
: t1 .. H; G; L ` expn : tn 0
H; G; L ` new t[]{exp , .. , exp } : t[]
0 0
H; G; L2 ` exp : bool typ_null
H; G; L ` ref null : ref ?
H; G; L2 ; rt ` stmt ) L3 ; ?
H;G;L
D5 =2 ;rt ` block;r
2 Subtyping Example Derivation
Rules typ_bool_true
typ_for H; G; L ` true : bool
1 ; rt ` for(vdecls; expopt ; stmt opt )
H`t  t
block ) L1 ; ?
typ_intOps
1 2 typ_bool_false
` -:(int, int) 0-> int 0 H; G; L ` false : bool
H; G; L `x1 :exp int 2 : ·t, x1 : H `xt2 : int
int,  t x2 : int 2 ·, x1 : int, x2 : int
typ_retT
typ_local typ_local
sub_sub_int typ_int
H; G; · , x
H; G; L; t ` return1 : int, x 2 : int ` x
exp; )1 L; > : int H; G; · , x1 : H
int,
` xint
2 : int ` x
int2 : int
H; G; L ` integer : int typ_bop
H; G; ·, x1 : int, x2 : int ` x1 - x1 : int
typ_retVoid sub_sub_bool
typ_string
H; G; L; void ` return; ) L; > H ` bool  bool
H; G; L ` string : string
var x1 = 0;
D3 =
var x2 = x1 + x1; H ` r ref 1  ref
id : t2 2 L sub_sub_nref
H ` ref 1 ? H;  G;refL2`? id : t typ_local
x1 = x1 – x2; x1 : int 2 ·, x1 : int, x2 : int
1 not areturn(x1);
global function id typ_local sub_sub_int D5
H; G; ·, x1 : int, x2 : int ` x1 : int H ` int  int
H `r refid 1 L refid2 : t 2 G
62 typ_assn
H; G; ·, x1 : int, x2 : int; int ` x1 = x1 - x2 ; ) ·, x1 : int, x2 : int; ? typ_global
sub_sub_ref
H ` ref 1H;G; ref L `2 id : t
H `Ht ` ref  ref
D4 = r 1 2
9 H; G; L ` exp1 : t1 .. H; sub_sub_nrref
G; L ` expn : tn
H ` ref  ref
H ` t1  t .. H ` tn  t
1 2 ?
x1 : int 2 ·, x1 : int, x2 : int typ_car
typ_local H; G;L `int sub_sub_int
new t[]{exp1 , .. , expn } : t[]
H; G; ·, x1 : int, x2 : intH``rx1ref: 1 int
 ref 2 H ` int
typ_retT
H; G; ·, x1 : int, x2 : int; int ` return x1 ; ) ·, x1 : int,Hx2`: int;
t >
H; G; L ` exp1 : int sub_subrstring
H `r string  string
t 2 {int, bool, r?}
typ_newarray
H; G; L ` new t[exp sub_subrarray
1 ] : t[]
H `r t[]  t[]
Example: Ill-Typed Oat Program

int f() {
var x = int[] null;
x = new int[] {3,4};
return x[0];
}
Next in this Module

• Making our programs faster

You might also like