9. Errors and diagnostics
9.1 The diagnostic model
Every problem Korrin reports is a diagnostic: a stable error code, a one-line message, one or more labelled spans of source, and optional notes and a help line. Diagnostics are produced by four stages — lexer, parser, resolver, interpreter — and rendered the same way regardless of origin (ADR 0008).
9.2 Stability of codes
Once a code is assigned a meaning it keeps it forever. Wording may improve; the
code–meaning mapping does not change. New codes are appended. The authoritative
list is the ErrorCode enum in crates/korrin/src/diagnostics.rs; this section
is its prose companion.
9.3 When does a program stop?
- Lexer and parser errors are collected — one mistake does not hide the rest — and reported together. The program does not run.
- Resolver errors are all reported in one pass. The program does not run.
- A runtime fault raises an exception (§9.6). If a
trycatches it (§5.10), the program continues; otherwise it stops at that point, and output written before then has already been produced.
9.4 Codes
Lexical — E00xx
| Code | Meaning |
|---|---|
E0001 | A character that cannot begin any token (includes a lone !). |
E0002 | A string literal with no closing " before the end of the line. |
E0003 | A \ escape that Korrin does not define. |
E0004 | A malformed number literal (1.2.3, 10abc). |
E0005 | A number literal outside the range of its type. |
E0006 | A tab character in a line's leading indentation. |
E0007 | A dedent to a column that matches no enclosing block. |
E0008 | An opening (, [, or { with no matching close. |
Syntax — E01xx
| Code | Meaning |
|---|---|
E0101 | A token where the grammar does not allow it (includes a chained comparison). |
E0102 | A required token (:, ), in, a name, end of line, …) was missing. |
E0103 | An expression was required and none was found. |
E0104 | The left side of = is not a name, attribute, or index. |
E0105 | (reserved) An indented block with no statements. |
E0106 | A for header not of the form for NAME in EXPR:. |
E0107 | A try with neither except nor finally, or a dangling except / finally. |
Static — E02xx
| Code | Meaning |
|---|---|
E0201 | return outside any function. |
E0202 | break outside any loop. |
E0203 | continue outside any loop. |
E0204 | (reserved — self is an ordinary name) |
E0205 | super outside any method. |
E0206 | super in a class with no parent. |
E0207 | Two parameters of one function share a name. |
E0208 | A class names itself as its own parent. |
Runtime — E03xx
| Code | Meaning |
|---|---|
E0301 | Read of a name with no binding in scope. |
E0302 | Call of a value that is not a function or class. |
E0303 | A call with the wrong number of arguments. |
E0304 | An operator, builtin, or method got a value of the wrong type. |
E0305 | Integer or float division/modulo by zero. |
E0306 | An integer operation overflowed the 64-bit range. |
E0307 | A list or string index outside its bounds (includes list.pop / list.remove_at / list.insert out of range). |
E0308 | Indexing a value that does not support it. |
E0309 | Access of an attribute a value does not have (an unknown instance field/method, or an unknown method on str / list / map). |
E0310 | A map[key] lookup for an absent key. |
E0311 | A value that is not hashable used as a map key. |
E0312 | An exception (raised value or built-in fault) reached the top level uncaught. |
E0313 | Function-call nesting exceeded the interpreter's recursion limit. |
9.6 Exceptions
A runtime fault is an exception: it unwinds until a try
(§5.10) catches it, or it reaches the top level as
E0312.
Catching. except NAME: binds the exception to NAME. What NAME holds
depends on how the exception arose:
- A built-in fault (
E0301–E0313) is bound as anErrorvalue with.message(the diagnostic's text) and.code(its"E03xx"string). - A
raised value is bound as-is —raise 42binds42,raise "x"binds"x",raise Error("x")binds thatError.
The Error type. Error is a built-in name bound in every program.
Error(message?) constructs an instance with:
.message— the argument's display form, or"error"if none was given;.code—nil(a program-created error has no diagnostic code).
type(Error("x")) is "Error".
e = Error("out of range")
print(e.message)
print(e.code)
print(type(e))
# => out of range
# => nil
# => Error
Uncaught. An exception with no enclosing try prints an E0312 diagnostic
pointing at the raise (or the faulting expression) and the program exits
non-zero. An uncaught Error shows its .message; any other value shows as
uncaught: <repr>.
try:
value = {"a": 1}["b"]
except err:
print("{err.code}")
# => E0310
9.5 Examples
x = 1 + undefined_name
# error: E0301
fn f(a, a):
return a
# error: E0207
print(10 % 0)
# error: E0305