Type checking I
Lecture 7
Table of Contents
Review
Questions about the last class?
Quiz
What is an example of a programming language that lacks a type system, but nevertheless are still safe from bad behavior (according to Cardelli's definition) at runtime?
Quiz Discussion
Why do type checking?
Prevent "unsafe" behavior
- Add one to a struct
- Array out of bounds access
- Null pointer dereference
- Arithmetic overflow
prevent programming errors
static (compile-time) vs. dynamic (run-time, cardelli calls this checking, reserving types for static definitions)
strict vs. weak
design considerations (halting problem)
What is a "type" in a typed language?
Typed languages restrict what values a symbol will hold
- A set of values
- and operations on those values
- Examples
- int: the set of integers (1, 53, -2, etc.), arithmetic operations
- bool: { true, false } and logical operations (and, or, not)
- string: { "hello", "world", … } and concatenation
Type checking ensures restrictions aren't violated
Untyped languages
- No restriction on range of values
- Every symbol can have every value
- What about "bad behavior" in these languages?
Safe vs. unsafe behavior
Cardelli distinguishes between trapped vs. untrapped
- Trapped
- Terminated by the machine, e.g., null-pointer dereference, divide-by-zero
- Untrapped
- Silent errors (program continues running), e.g., writing past array bounds, adding one to a struct reference
- Safe languages prevent untrapped (and some trapped) errors
Why focus on untrapped?
Trapped caught at runtime
Untrapped fail silently
Untrapped (and some trapped too) may be infeasible or impossible to determine without execution (related to the halting problem)
When to check safety?
Compile-time vs. run-time
- Compile-time (static) checking: C, Rust, Java, etc.
- Run-time (dynamic) checking: Python, Lisp, Java(?)
Cardelli distinguishes between types and checking, e.g., languages like lisp enforce safety albeit at runtime, but is checking is not type checking, since lisp is untyped.
Effectively, "typing" means the language (i.e., syntax and semantics) defines type rules, not only that behaviors that types prevent happen to be checked during execution.
Practical type checking
- Forbidden errors: all untrapped errors and some trapped
- Good behavior: a program has no forbidden errors
This also depends on what types have been included in the language. For example, you can implement subclassing in C, but C's type system will not enforce it.
How much checking to perform?
- Strongly-checked: all legal programs have good behavior
- Weakly-checked: some programs violate safety
What are some examples of strongly- and weakly-checked languages?
- In weakly-checked languages, what are some examples of violative programs that don't get rejected?
Type systems restrict what input programs are part of the language (similarly to how grammars restrict the space of input programs). A legal program is one that is part of the language.
If safety is so important, why have weak checking at all?
Examples of languages
Typed | Untyped | |
---|---|---|
Safe | ML, Java | LISP |
Unsafe | C | Assembly |
Note the distinction between having a type system vs. whether the language checks type safety.
Demo: Python vs. C
Dynamic checking:
print "STARTED RUNNING" x = "degrees" x = 1.7 + " degrees" y = 1.7 * 2 print y
Static checking:
#include <stdio.h> int main() { printf("STARTED RUNNING\n"); double temp = (double)2; int y = 1.7 * temp; /* int y = ((void *)1.7) * (float)((void *)2); */ float yf = 1.7 * 2; printf("%x\n", y); printf("%f\n", yf); return 0; }
Unsafe program allowed by weakly-checked language:
#include <stdio.h> int main() { printf("STARTED RUNNING\n"); float x = 1.7; float *p = &x; int *ip = (int *)p; printf("%d\n", *ip); return 0; }