8.7 KiB
011 — Diagnostics
- Status: Draft
- Normative language:
MUST,MUST NOT,SHOULD,SHOULD NOT, andMAYare to be interpreted as described in RFC 2119.
1. Purpose
libVCT has a single central diagnostics service (vct.diag). Every module emits
diagnostics through it; no module prints directly to a stream. This gives embedders one
uniform surface for errors, warnings, notes, optimizer remarks, and internal compiler
errors, and it lets the CLI present them in several formats without every module knowing
which format is active.
2. Scope
This file specifies the diagnostics service, the Diagnostic structure and its
severity and code vocabulary, source maps, output channels, optimizer remarks, the
internal-compiler-error policy, and process exit codes. The pipeline that produces these
diagnostics is specified in 010 — Driver & CLI; the backends that
consume source maps are specified in 008 — C Backend and
009 — LLVM Backend.
3. Definitions
| Term | Meaning |
|---|---|
| Diagnostic | One emitted message with severity, code, span, and optional notes and suggestions. |
| Severity | Error, Warning, Note, Remark, or Ice. |
DiagCode |
A stable, namespaced identifier such as VCT1002. |
| Span | A source range: file, line, column, and length. |
| Source map | The association of HIR nodes with frontend source locations, resolved for VIR through the lowering map. |
| Consumer | A DiagnosticConsumer callback that receives emitted diagnostics. |
| ICE | Internal compiler error. |
4. Model
The service is emit-only from the perspective of compilation modules. A module
builds a Diagnostic and hands it to the service. The service forwards it to the
configured consumer. Fatal conditions (an error diagnostic, or an ICE) stop the
pipeline at the next safe boundary; they never let compilation continue on invalid
state.
The service is GC-allocated, consistent with the cold path rule in 001 — Architecture.
struct Diagnostic {
Severity severity; // Error | Warning | Note | Remark | Ice
DiagCode code; // stable namespaced, e.g. VCT1002
string message;
Span primary;
Span[] notes;
Suggestion[] suggestions;
}
5. Normative requirements
5.1 Central service
R1. Every module MUST emit diagnostics through the central service. No module MUST print to standard output or standard error directly.
R2. The service MUST be GC-allocated.
R3. The service MUST forward each emitted diagnostic to the configured consumer in emission order.
R4. An Error or Ice diagnostic MUST prevent further compilation on the invalid
path. The pipeline MUST stop at the next safe boundary after such a diagnostic.
5.2 Diagnostic structure
R5. Every diagnostic MUST carry a Severity and a DiagCode. A diagnostic with no
code MUST NOT be emitted.
R6. Severity MUST be one of the values in Table 1.
Table 1. Severity levels
| Severity | Meaning |
|---|---|
| Error | Compilation cannot proceed correctly. |
| Warning | Suspicious but continuable. |
| Note | Supporting context attached to another diagnostic. |
| Remark | An optimizer decision record. |
| Ice | Internal compiler error. |
R7. A DiagCode MUST be a stable, namespaced identifier in the form VCT followed
by digits. Once published, a code's meaning MUST NOT change and the code MUST NOT be
renumbered.
R8. A diagnostic MUST carry a primary Span whenever a source location is known, and it MAY
carry additional note spans and suggestions. When no location can be resolved, the diagnostic MUST
carry no location instead (see R12).
R9. A Note diagnostic MUST be attached to a parent diagnostic rather than emitted
as a standalone top-level message.
5.3 Source maps
R10. The frontend MUST be able to register a SourceLocation (file, line, column,
length) against any HIR node.
R11. For VIR entities, the service MUST resolve source locations through the lowering map that associates HIR nodes with the VIR entities they became (see 005 — Lowering).
R12. When a location cannot be resolved, the service MUST degrade gracefully to a diagnostic with no location. It MUST NOT fabricate a location.
R13. The C backend MUST emit #line directives from the registered source map (see
008 — C Backend), and the driver MUST re-map compiler errors through
it (see 010 — Driver & CLI).
5.4 Channels
R14. The service MUST expose a single canonical DiagnosticConsumer callback for
embedders.
R15. The CLI MUST provide a human-readable channel that enables color when standard error is a terminal.
R16. The CLI MUST provide a JSON channel selected by --diagnostics=json.
R17. The JSON channel MUST use a versioned schema. The schema version MUST appear in the output, and the schema MUST be stable within a major version.
R18. A single diagnostic MUST render equivalently in content across all channels; the channels differ only in presentation.
Example JSON shape:
{
"version": 1,
"diagnostics": [
{
"severity": "error",
"code": "VCT1002",
"message": "missing trait at lowering",
"file": "input.vox",
"line": 12,
"column": 5,
"length": 3,
"notes": []
}
]
}
5.5 Optimizer remarks
R19. Optimizer remarks MUST be off by default and MUST be enabled by --remarks.
R20. Optimizer remarks MUST be enabled automatically at -O3.
R21. Every remark MUST be tied to a specific trait-pipeline decision, naming the request or attribute that drove it.
Examples:
HIR: replaced 3 uses of node#412 (comptime 64)
VIR: denied Inline on @foo - CostModel (callee 2.4x size budget)
5.6 Internal compiler error policy
R22. The following conditions MUST be treated as internal compiler errors:
- a missing or contradictory trait encountered at lowering (see 005 — Lowering);
- a VIR invariant violation detected in a debug build (see 006 — VIR);
- a
Strongrequest denied for reasonIllegalityorContradictsTrait(see 007 — VIR Optimizer).
R23. In release builds, an internal compiler error MUST return a failure result and
emit an Ice diagnostic that requests a module dump.
R24. In debug builds, an internal compiler error MUST assert.
R25. An internal compiler error MUST NEVER be silently continued past.
5.7 Warning policy
R26. The trait-mismatch warning raised when a suggested trait conflicts with the
derived value MUST be promoted to an error under -Werror.
R27. A frontend MUST be able to opt out of warning promotions: -Wno-error=<category> MUST
disable the promotion of that category, and a bare -Wno-error MUST disable every promotion.
5.8 Exit codes
R28. The CLI MUST exit with the codes in Table 2.
Table 2. Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Diagnostics present. |
| 2 | Usage or configuration error. |
| 3 | Internal compiler error. |
R29. Exit code 3 MUST take precedence over exit code 1 when an ICE has been emitted.
6. Invariants
- I1. Every emitted diagnostic has a severity and a
DiagCode. - I2. No compilation module writes directly to a stream.
- I3. Diagnostics are emitted and rendered in a deterministic order for a fixed
input and
Config. - I4. An ICE never resolves to a success exit code.
- I5. A location is either resolved or omitted; it is never invented.
- I6. The JSON channel's schema version is always present.
7. Examples
A frontend registers a location and a later lowering failure reports it:
diag.registerLocation(node, SourceLocation("input.vox", 12, 5, 3));
// later, during lowering:
emit(Diagnostic(Severity.Error, DiagCode(1002),
"missing trait at lowering", span));
A MustTail request on GCC becomes a hard error rather than a silent fallback:
error[VCT1044]: MustTail requires [[clang::musttail]]; current compiler is GCC
--> input.vox:7:5
8. Cross-references
- 000 — Overview for conformance and determinism.
- 001 — Architecture for the cold-path GC rule.
- 002 — Traits for the trait vocabulary and mismatch handling.
- 005 — Lowering for the lowering map and ICE conditions.
- 006 — VIR for the invariants checked in debug.
- 007 — VIR Optimizer for request denials and
DenyReason. - 008 — C Backend for
#lineemission. - 010 — Driver & CLI for
--remarks,--diagnostics, and exit codes.