v6 Semantics: static types on a dynamic runtime
Lesson, slides, and applied problem sets.
View SlidesLesson
v6 Semantics: type checking
This stage enforces static types before code generation.
This is the first module where the analyzer computes and propagates type information instead of only name tables.
1) Type model in this pack
Base types:
numberstringboolnil
Structured types:
array<T>map<K, V>fn(P1, P2, ...) -> R
Plus one practical bucket:
unknown(used when inference lacks certainty or mixed constraints appear)
Assignability
- exact structural match for most types
nilis assignable to any destinationunknownpropagates as "compatible" to avoid false positives in early inference states
This makes analyzer behavior deterministic and still educational.
2) Declarations and inference
For let / var / const:
- explicit annotation is validated against initializer
- missing annotation:
- initializer must provide clear inferable type
- empty literals are ambiguous and should error unless contextual type exists
If inference fails:
- report
cannot infer type: <name>
const without initializer remains a structural rule:
const requires initializer: <name>
Function declarations:
- parameter and return types are required
- missing signature should be reported as a function signature error
- function body is type-checked under declared return type
3) Expression typing in practice
Arithmetic:
+supportsnumber+numberandstring+string- * /require numbers- comparisons
< <= > >=require numbers and return bool == !=require comparable types
Boolean logic:
&&/||require booleans and return booleans!requires boolean
Flow checks:
if/while/forconditions must be bool-compatible- for-loops support
condandposttyping in addition to init/body analysis
Calls:
- callee must be a function type
- arity must match
- each argument must be assignable to declared param type
If not function:
call of non-function
If arity mismatch:
arity mismatch
4) Collections and indexing
array typing:
- inferred from first element if no expected context
- all subsequent elements must be assignable to the base element type
- empty array literal needs contextual type (or explicit annotation)
map typing:
- keys must share a stable key type and be valid map keys
- values must share value type
- empty map literal requires expected type context
Indexing:
- array index requires number (or unknown)
- map index key must match declared map key type
- invalid index base returns type error (including non-collection indexing)
Map key validity check:
- primitives and
nilkeys are valid - invalid key types produce key-type errors
5) Module + type integration
v6 combines module semantics with type rules:
- module imports/exports still validated
- exported declarations carry types into member access
- member calls use the exported function signature/type
When analyzing util.add:
- resolve module alias
- resolve exported type for
add - validate arguments via that function type
unknown export and unknown module continue to be front-end errors, often before deeper type mismatch checks at call sites.
6) Return and control-flow typing
Return checking now includes:
- return outside function error remains unchanged
- return expression must be assignable to function return type
return type mismatch: expected ... got ...
Break/continue rules remain from v4/v5 and still apply.
7) Practice checkpoints (almost solved)
1) Inference edge
Program:
let a = 1;
let xs = [];
Expected:
ainfersnumberxsreports cannot-infer without annotation
2) Binary typing
Program:
let n: number = "x" + 1;
Expected:
- diagnostic for
+operands not compatible
3) Boolean condition
Program:
if ("true") {}
Expected:
type mismatch: expected bool got string
4) Call + signature typing
Program:
fn add(a: number, b: number) -> number { return a + b; }
add(true, 1);
Expected:
type mismatch ... number got boolon first arg
5) Nil flexibility
Program:
let n: number = nil;
Expected:
- valid (
nilassignable to any type in this pack)
6) Array type enforcement
Program:
let xs = [1, "a"];
Expected:
- first element infers
number, second produces element mismatch
7) Map key restrictions
Program:
let bad = { [1,2]: 3 };
Expected:
- invalid map key type if inferred key shape is non-primitive
8) Function signatures in modules
Program:
module util {
export fn add(a: number, b: number) -> number { return a + b; }
}
module main {
import util;
util.add("x", 1);
}
Expected:
- member resolves
util.addto function type - arg type mismatch before runtime
Module Items
Semantic Analysis v6