Diagnostics are the compiler’s public failure protocol. Every expected problem in source or configuration should become a structured diagnostic rather than a panic, silent fallback, or untyped string.
Diagnostic record
A diagnostic carries:
| Field | Purpose |
|---|---|
| Stable code | Machine-readable identity such as ATOLL2002 |
| Message | Short user-facing description |
| Severity | Error, warning, info, or hint |
| Primary range | The main source span |
| File | Source file that owns the primary range |
| Labels | Related spans, including cross-file spans |
| Help | Explanation or next action |
| Suggestion | One or more concrete text edits |
| Causes | Trace event IDs that contributed to the conclusion |
The stable code is not the complete message. Tests and tools can branch on the code while the message, labels, and help improve without breaking protocol consumers.
Code ranges
The current catalog uses broad numeric families:
| Range | Main category |
|---|---|
ATOLL1xxx |
Parse and source-form errors |
ATOLL2xxx |
Resolution, checking, decorators, and related semantic errors |
ATOLL3xxx |
Type, trait, and semantic-shape errors |
ATOLL4xxx |
Effects, capabilities, unsafe operations, and some IR safety checks |
ATOLL5xxx |
Backend and code generation |
ATOLL6xxx |
Monomorphization |
ATOLL7xxx |
Performance and optimization hints |
Removed codes leave gaps. Reassigning an old code to a different meaning would break editor filters, CI rules, documentation links, and snapshots.
Numeric families are a default classifier, not perfect provenance. MIR can
produce a pointer-safety ATOLL4xxx or backend-boundary ATOLL5xxx
diagnostic. When a caller knows the producing stage, it must tag that stage
explicitly.
Layer ownership
DiagnosticTable aggregates entries with one of these producer layers:
- Parse
- Sema
- Mono
- IR
- Codegen
- Driver
Ownership answers “which stage established this failure?” A semantic type-mismatch code emitted defensively during specialization still belongs to Mono if specialization discovered it. Conversely, a blended backend result may require code classification when the original stage tag was not retained.
New code should preserve explicit producer layers at aggregation boundaries rather than relying on code ranges after vectors from several stages have been mixed.
Source attribution
Internal ranges use byte offsets. A diagnostic also identifies its source file, allowing the language server to partition project diagnostics by document. Secondary labels can point into another file, which is necessary for cross-module declarations and prelude signatures.
Compiler-bundled core source creates a presentation problem: an error may be
detected inside a stub even though the actionable code is a user call. The
default user path redirects an internal primary frame to a suitable
user-facing label. --show-internal disables that redirection for compiler
development.
Redirection changes presentation, not ownership or the underlying source facts. Tests for an internal failure should cover both views where relevant.
Severity and gates
Severity controls the pipeline:
| Severity | Build effect |
|---|---|
| Error | Blocks the next artifact-producing stage |
| Warning | Build may succeed; indicates a likely defect |
| Info | Non-blocking explanatory information |
| Hint | Non-blocking style, migration, or optimization guidance |
atoll check returns parse and semantic diagnostics without emitting code.
atoll build stops before specialization when analysis has errors, before
codegen when Mono has errors, and before artifact writing when IR or codegen
has errors.
The artifact writer rejects a unit result that carries diagnostics requiring failure. A successful build must not leave a partially valid collection of unit files that looks complete.
Recovery and suppression
Parser recovery allows multiple diagnostics from one file, but later stages should avoid cascades that add no information. An explicit syntax-error node or sentinel type can contain damage while independent declarations continue.
Good suppression is local:
- report the unknown symbol once, then avoid derivative method failures on its error type;
- report the malformed pattern, then keep checking other match arms;
- report a missing backend layout at its source origin, then stop lowering that unit.
Suppression must not hide an independent problem or allow emission through a known error.
Suggestions
A suggestion is an ordered set of source replacements with a user-facing title. It should be auto-applicable only when:
- every edit range belongs to the intended source snapshot;
- the edit has one unambiguous interpretation;
- applying all edits yields syntactically meaningful text;
- no hidden project knowledge is required.
Help text is more appropriate when several repairs are possible.
Provenance
Diagnostics can carry compile-story event IDs. A type mismatch can therefore refer to the inference, coercion, or dispatch events that established its conflicting facts. This supports “why” tooling without expanding every ordinary CLI message into a trace dump.
Provenance is optional side data. A diagnostic remains valid and renderable when tracing is disabled.
Adding a diagnostic
- choose the stage that owns the violated invariant;
- add a new stable code in the appropriate catalog range;
- select the narrowest useful primary range;
- attach labels for the expected declaration or competing source fact;
- add help or a suggestion when the repair is known;
- tag the producer layer explicitly at aggregation;
- add positive, negative, range, message, and recovery tests;
- verify CLI and LSP rendering for multi-file cases.
A panic remains appropriate for an impossible internal state already guarded by a verifier. A user-reachable unsupported feature is not such a state.