Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 try catches 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

CodeMeaning
E0001A character that cannot begin any token (includes a lone !).
E0002A string literal with no closing " before the end of the line.
E0003A \ escape that Korrin does not define.
E0004A malformed number literal (1.2.3, 10abc).
E0005A number literal outside the range of its type.
E0006A tab character in a line's leading indentation.
E0007A dedent to a column that matches no enclosing block.
E0008An opening (, [, or { with no matching close.

Syntax — E01xx

CodeMeaning
E0101A token where the grammar does not allow it (includes a chained comparison).
E0102A required token (:, ), in, a name, end of line, …) was missing.
E0103An expression was required and none was found.
E0104The left side of = is not a name, attribute, or index.
E0105(reserved) An indented block with no statements.
E0106A for header not of the form for NAME in EXPR:.
E0107A try with neither except nor finally, or a dangling except / finally.
E0108An import with no path, a non-literal path, an unusable derived name, or a stray as.

Static — E02xx

CodeMeaning
E0201return outside any function.
E0202break outside any loop.
E0203continue outside any loop.
E0204(reserved — self is an ordinary name)
E0205super outside any method.
E0206super in a class with no parent.
E0207Two parameters of one function share a name.
E0208A class names itself as its own parent.

Runtime — E03xx

CodeMeaning
E0301Read of a name with no binding in scope.
E0302Call of a value that is not a function or class.
E0303A call with the wrong number of arguments.
E0304An operator, builtin, or method got a value of the wrong type.
E0305Integer or float division/modulo by zero.
E0306An integer operation overflowed the 64-bit range.
E0307A list or string index outside its bounds (includes list.pop / list.remove_at / list.insert out of range).
E0308Indexing a value that does not support it.
E0309Access of an attribute a value does not have (an unknown instance field/method, or an unknown method on str / list / map).
E0310A map[key] lookup for an absent key.
E0311A value that is not hashable used as a map key.
E0312An exception (raised value or built-in fault) reached the top level uncaught.
E0313Function-call nesting exceeded the interpreter's recursion limit.
E0314An import could not be resolved, read, or compiled (§11).
E0315Modules import each other in a cycle (§11).
E0316A filesystem or stream operation failed (io module — §11.5).

Compilation — E04xx

Raised by the bytecode compiler, which runs only when a program is executed by the virtual machine. These are limits of the bytecode's operand widths rather than of the language: each is a ceiling on how much one function or one chunk can hold, and reaching any of them takes a machine-generated program.

CodeMeaning
E0401One function needs more than 65 535 local variables.
E0402One chunk needs more than 65 535 constants.
E0403One call passes, or one function declares, more than 255 arguments.
E0404One function captures more than 65 535 variables from enclosing scopes.

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 an Error value with .message (the diagnostic's text) and .code (its "E03xx" string).
  • A raised value is bound as-is — raise 42 binds 42, raise "x" binds "x", raise Error("x") binds that Error.

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