Type checking of statements We change the start rule from P D ; E to P D ; S and add the following rules for statements: S id := E if E then S while E do S S ; S
Type checking of statements The purpose is to recurse the program to find all the expressions in the statements. The statements themselves have no special type, i.e. they are void, unless they contain some type error in which case they are error. S id := E {S.type =if lookup(id.entry) = E.type then void else error } if E then { S 1 } {S.type =if E.type = bool then S 1.type else error } while E do{s 1 } {S.type =if E.type = bool then S 1.type else error } S 1 ; S 2 {S.type =if S 1.type = void and S 2.type = void then S 1.type else error }
Exercises Extend the grammar so that it is allowed to access array elements, like a[5] := a[7] mod 42 Add type checking semantics (SDD) for the new grammar rules. Extend the grammar to handle function calls in statements, like foo(a, b+c) Add type checking semantics (SDD) for the new grammar rules.
P D ; E {P.type := E.type } D D ; D id: T {addtype(id.lexval, T.type) } T char {T.type := char } integer {T.type := integer } array [num] of T 1 {T.type := array(t 1.type) } ^T 1 {T.type := pointer(t 1.type) } T 1 -> T 2 {T.type := T 1.type -> T 2.type } E literal {E.type := char } num {E.type := integer } id {E.type := lookup(id.lexval)} E 1 mod E 2 {E.type := if E 1.type = integer and E 2.type=integer then E 1.type else error} E 1 [E 2 ] {E.type := if E 1.type = array(t) and E 2.type=integer then t else error } E 1^ {E.type := if E 1.type = pointer(t) then t else error } E 1 (E 2 ) {E.type := if E 1.type = s -> t and E 2.type = s then t else error S id := E {S.type =if lookup(id.entry) = E.type then void else error } if E then { S 1 } {S.type =if E.type = bool then S 1.type else error } while E do{s 1 } {S.type =if E.type = bool then S 1.type else error } S 1 ; S 2 {S.type =if S 1.type = void and S 2.type = void then S 1.type else error }
Equivalence between type expressions Suppose we have two language constructs for which we need to compare the types, e.g. in void foo(struct A x, int y, float z);... foo(a, b, c); we need to compare the type of function call with the type of the declaration of foo. In particular this is an issue when comparing composed types. In general two type expressions are equivalent if they are The same basic types or Are constructed from the same type constructors applied to equivalent sub type expressions If the language supports naming of constructed types, the names may have the same role as the basic types (simplifying the type checking).
Equivalence between type expressions More specific: bool equiv(s, t) if s and t are the same basic type then return true else if s=array(s') and t=array(t') then return equiv(s', t') else if s=s' x s'' and t=t' x t'' then return equiv(s', t') and equiv(s'', t'') else if s=pointer(s') and t=pointer(t') then return equiv(s', t') else if s=s' -> s'' and t=t' -> t'' then return equiv(s', t') and equiv(s'', t'') else return false
Recursively defined data structures Assume this C-code: struct RECORD {void *data; struct RECORD *next;}; struct RECORD *record; What is the type of record? With name equivalence: pointer With structural equivalence: pointer struct RECORD x x x data pointer next pointer void
Type and name equivalence C uses name equivalens, counting struct <name> as a name: struct A {int x;}; struct B {int x;}; struct X {int x;}; typedef struct X C; typedef struct X D; void foo(void) { struct A a; struct B b; C c; D d; a = b; /* Error: struct A and struct B are different type names */ c = d; /* C and D are equal */ }
Type conversions Languages may allow for type conversion. This means to make a copy of a data object, where the copy has another type than the original. unsigned int x=42; (unsigned long)x Extend with a couple 0 bits. (float)x Convert to a completely different bit pattern, preserving the meaning of the content (eventually loosing some precision) (char*)x completely different. (struct A)x Keep the bit pattern but the meaning will be There is no possible meaning with this - error
Type conversions Checking of type for a + operator allowing for real and int, and allowing for implicit conversion of int to real in the language. E E 1 + E 2 {E.type= if E 1.type=int and E 2.type=int then int else if E 1.type=real and E 2.type=real then real else if E 1.type=int and E 2.type=real then real else if E 1.type=real and E 2.type=int then real else error }...
Type conversions Explicit type conversions will appear as operators to the type checker F id num (type) F! F Syntax tree for x + ( int ) y + id Compare to x +! y + toint id id! id