Skip to main content

PhaseError

Trait PhaseError 

Source
pub trait PhaseError {
    // Required method
    fn message(&self) -> String;

    // Provided methods
    fn notes(&self) -> Vec<String> { ... }
    fn labels(&self) -> Vec<SpanLabel> { ... }
    fn message_and_notes(&self) -> Vec<String> { ... }
}
Expand description

What a compiler phase owes the diagnostic reporter.

Each phase keeps its own error type — one enum for the whole compiler would make every phase depend on the vocabulary of every other. What they have to share is the ability to describe themselves in the user’s terms, because CompilationError::as_diagnostic is the only place a Diagnostic is ever built and it has no phase-specific knowledge to fall back on. Dumping format!("{:?}", e) into a note is exactly what this trait replaces: a Debug dump names Rust types, not source constructs.

§How much a span can say, and where that stops

Every production in grammar.lalrpop that builds a node captures @L/@R, so declarations, expressions, patterns and types all know where they were written, and canonicalization copies that onto the node it builds. An error raised from one of those construction sites returns a SpanLabel from labels and gets a caret under the text it is about — an unresolvable name is underlined at the identifier, not across the declaration containing it.

Not every error can. labels defaults to empty and that is a real answer, not a stub: an error raised while walking a node the grammar does not span — an import‘s exposing list, say — has nowhere to point, and renders as message-plus-notes with no caret, exactly as every phase after parsing used to. An error that groups others (canonical::Error::Many, EnvironmentErrors) has no position of its own and flattens its members’ labels instead, the way it already flattens their messages.

The typer is the phase that has to work for this, and it does: it does not check the canonical AST but a term language of its own, so it carries each canonical node’s span into that language, each constraint records the term and the reason it came from, and unify reports the origin of the constraint it failed on. A type error therefore underlines the sub-expression that disagrees and adds a secondary label under the annotation that says what was expected (ERR-4).

parser::Error is still the one phase error that does not go through this trait at all — it builds its own labelled Diagnostic through parser::Error::diagnostic.

§The two ways a label learns its SourceFileId

A phase still never knows the id of the file the module it is checking came from — that half is unchanged, and stays attached by compile_package, the only place that knows it, the way CompilationError::Source already works. A SpanLabel with file: None — everything built before ERR-5 — means exactly that: “in the module under check”, resolved at render time.

That is right for an error entirely about one module and cannot cover a diagnostic that also names something written elsewhere — “the annotation you are contradicting is over there, in Basics”. For that, file carries its own SourceFileId instead, taken from a SourceSpan cloned out of an Interface via Interface::source_span. An Interface can offer that because it is not a phase: it is built by driver code (dependencies::ModuleWalker::check_in_order) that already has the file the module it just checked came from, and stamps it onto the Interface before handing it to whoever imports next. canonical::Type still carries no span of its own — seeing why is worth a look at that type’s own documentation.

Required Methods§

Source

fn message(&self) -> String

One line naming what went wrong, in the vocabulary of the user’s source.

This is rendered as the diagnostic’s headline, so it has to read on its own: no {:?}, no Rust type names.

Provided Methods§

Source

fn notes(&self) -> Vec<String>

Supporting detail, one string per rendered note. Empty by default.

Source

fn labels(&self) -> Vec<SpanLabel>

The regions of the user’s source this error is about, if it knows any.

Empty — the default — means this error has no position to point at, and it renders as message-plus-notes with no caret. See the trait’s documentation.

Source

fn message_and_notes(&self) -> Vec<String>

This error’s message followed by its notes.

A Diagnostic has one headline, so an error that ends up inside a group — several errors from one phase, or a variant like canonical::Error::Many that wraps others — has to give up its headline and become notes. This is that demotion, in one place, so a group cannot silently drop the message of a member it swallowed.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl PhaseError for zelkova_compiler::canonical::Error

Canonicalization errors name source constructs — a value, a type, an operator — so their messages can be written in the same words the user wrote.

The variants whose construction site had a declaration in hand also point at it; see the enum’s own documentation for why the others do not.

Source§

impl PhaseError for zelkova_compiler::dependencies::Error

A dependency cycle belongs to the package, not to any one module, so its message names the modules involved rather than being prefixed with one of them, and its labels — one per import that forms an edge — each carry their own file rather than relying on a fallback the way a single-module phase error can (CompilationError::DependenciesError renders this without one, see there).

Source§

impl PhaseError for zelkova_compiler::exhaustiveness::Error

Source§

impl PhaseError for zelkova_compiler::ir::SpecialiseError

Source§

impl PhaseError for zelkova_compiler::program::Error

Source§

impl PhaseError for zelkova_compiler::resolve::Error

Source§

impl PhaseError for zelkova_compiler::typer::Error

Type errors are about types, and Type’s Display writes them the way the source does — so the message can name both sides of a mismatch instead of dumping the typer’s internal representation.

Source§

impl PhaseError for ManifestError

Source§

impl PhaseError for SourceFileError

Loading is the one phase that has no module and no source text to point into — it failed before either existed — so its errors name the path instead. It goes through PhaseError anyway, so that as_diagnostic has exactly one way to turn a phase error into text.