Type Checking
Types flow up from the leaves, a coercion gets inserted, and a grammatically perfect program is rejected.
Skip to the animationType checking walks the annotated syntax tree bottom-up, computing each node's type from its children, inserting the conversions the language implies, and rejecting programs the grammar was perfectly happy to accept.
What the grammar cannot say
pos = initial + rate * "hello" is grammatically flawless. A context-free grammar cannot express "the operands of * must be numeric", because that rule depends on what the identifiers were declared as — context the grammar by definition ignores.
So a separate phase checks it, using the symbol table for declared types and the tree for structure. This is the phase that catches most of the errors a working programmer actually sees.
Types flow upward
- 1Leaves get their types from the symbol table (variables) or their form (literals).
- 2Each interior node computes its type from its children's, checking the operator permits them.
- 3If the operands mismatch but a safe conversion exists, insert a coercion node.
- 4If no conversion exists, report a type error.
A value computed from a node's children and passed upward is a synthesised attribute. An inherited attribute flows the other way, down from parent to child — needed for things like the expected type of a function argument.
Checking and rewriting
The phase does not only verify — it modifies the tree. Wrapping 60 in inttofloat makes an implicit language rule explicit, so that intermediate code generation can treat it as an ordinary operation and never think about types again.
This is a recurring pattern in compilers: each phase's real job is to remove a category of implicitness, so that later phases can be simpler.
Static, dynamic, and the rest of the phase
Static checking happens at compile time, so errors surface once and the generated code carries no type tags. Dynamic checking happens at run time, so values carry their types and errors appear only when that line executes. Most languages do both — static where they can, dynamic for array bounds and downcasts.
Type checking is the largest part of semantic analysis, not all of it. The same walk verifies that names are declared before use, that calls have the right number of arguments, that break is inside a loop, and that every path through a non-void function returns. All of them are things the grammar cannot express, and all of them need the symbol table.
Advantages and disadvantages
Advantages
- Catches a large class of real errors before the program is ever run.
- Makes implicit conversions explicit, simplifying every later phase.
- Enables optimisation — knowing a value is an int permits code a dynamic language could not emit.
Disadvantages
- Rejects some programs that would in fact have worked; that is the price of deciding in advance.
- Implicit coercions can hide genuine bugs behind a silent conversion.
- Rich type systems make checking slow, and their error messages notoriously hard to read.
Watch it work
Check yourself
question 1 / 4
One question at a time. Pick an answer to see why it is right or wrong, then move on — there is no score to keep and nothing is saved.