Type checking II
Lecture 8
Table of Contents
Review
Questions about the last class?
Quiz
Is printf part of the C language?
Quiz Discussion
Is printf part of C?
- Library methods vs. keywords
- Keywords in syntax vs. reserved identifiers
- Growing a Language
- Language as methods of abstraction, adding new symbols
-nostdinc
flag
Performing type checking
- Type checker proves that input an program follows the typing rules
SimpleC's types
- int, bool, string, function
int
- Set of integers
- What operations are there for integers?
Definition of int type vs. machine representation.
C long vs. Java BigInt
How do we check an operation?
- Examples: addition
+
- Takes two integers
- How do we know 1 + 2 is legal?
+
takes two integers- How do we know
1
and2
's types?
Remember: the map is not the territory! The ASCII symbols for 1
, 2
, +
, are just numeric encoding conventions that our systems use to draw glyphs on screen. The type-checker needs to interpret the meaning of these symbols.
While we can easily observe the type ourselves, we need to create an program on a computer that can mechanically determine the meaning without a human to tell it our interpretation the meaning.
Types of symbols in the language
- Integer literals (defined in syntax)
- Always have type "int" (as an axiom)
- What about variables?
Declarations give axioms/assumptions to the type-checker so it can prove
Type-checking 1 + 2
- We know
+
takes two integer - We know
1
and2
are integers - Therefore we know
1 + 2
is type safe. QED!
What about +
in 1 + 2 * 3
?
- We know
+
takes two integers? 1
is an integer?- How do we know the type of
2 * 3
?
The type of functions
+
is a function- We define it to take two integer
- And we define it to produce an integer
- Using formal notation:
+ : (int, int) -> int
The symbol +
is a function ->
which takes two integer (int, int)
and returns an integer -> int
The typing rules for functions and type operations are the same
+
is a built-in function with special syntax (infix instead of prefix)
Back to 1 + 2 * 3
- To type-checking
+
we need to type-check2 * 3
- To type-check
*
, we check its type* : (int, int) -> int)
- Type-checking a function both
- ensures its parameter types match, and
- deduces the return value's type
Let's sketch an algorithm for type-checking arithmetic expressions
- How can we handle these nested constructs?
The typing rules implemented by the type checker are designed to match the actual runtime behavior of the program. Programming language researchers develop proofs that the type system reflects the runtime behavior and also ensures safety.
Recursively type-check expression
- Basically an evaluator, like Calc.java
- But instead of integer values, we use a type as the (abstract) value
- Recall that a type is a set of legal values (and operators)
- Type-checking gives symbols the type name itself instead of one of the values
Algorithm pseudo-code
typecheck(expr): if expr is NUM: return INT elif expr equals either "true" or "false": return BOOL elif expr.op is one of + - * / assert typecheck(expr.left) == INT assert typecheck(expr.right) == INT return INT elif expr.op is one of < <= > >= == != assert typecheck(expr.left) == INT assert typecheck(expr.right) == INT return BOOL elif expr.op is one of && || ! assert typecheck(expr.left) == BOOL assert typecheck(expr.right) == BOOL return BOOL
Compare to an evaluator
- Same structure
- Different operations
eval(expr): if expr is NUM: return toInteger(expr) elif expr equals either "true" or "false": return expr == "false" ? 0 : 1 elif expr.op is one of + - * / left = eval(expr.left) right = eval(expr.right) return do_op(left, expr.op, right) elif expr.op is one of < <= > >= == != left = eval(expr.left) right = eval(expr.right) return do_op(left, expr.op, right) elif expr.op is one of && || ! left = eval(expr.left) right = eval(expr.right) return do_op(left, expr.op, right)
Type checkers as theorem provers
- Type-checker is equivalent to logical proofs
- Curry-howard correspondence
- Coq is a proof writing assistant based on
Function's return type is the theorem
- Type-checking proves that it returns the type
- Leaves of the tree are axioms
- Defined by language designer or by developer
Typing judgments
- Based on "proof rules"
- Systematic notation for deriving proofs from logical rules
- Popular notation in academia
- Sort of an esoteric description of an evaluator
- Described in Type Systems
- Example
Handling declarations
- User may add new symbols to the language
- Type-checking needs to proof type safety of these as well
- Many language require declarations
Some languages can infer types from operators used.
Type-checker records type information about each symbol
- Maps symbol name to type
- Declarations populate the symbol table
- Relevant to scoping rules
- Same name, different memory location
- Disambiguate by scope
Symbol tables
(Source)
Conditional statements
x = setx() if (x > 10) y = 1 else y = "hello"
What if the type-unsafe branch is not reachable?
x = setx() if (x > 10) y = 1 else y = "hello"
Design decisions
- No type system: any symbol, any type
- Type-safety: values in all uses fit symbol's type
- Inflexible in C, e.g., no function overloading
Polymorphism
- Designs that permit multiple types for the same symbol, e.g.,
- Subclasses
- Operator/function overloading
- Generics
Type checking loops
What if we don't know how long the loop runs?
int y int x
while x > 0 if (x > 10) y = 1 else y = "hello" x = updatex(x)