# 011 — Diagnostics - **Status:** Draft - **Normative language:** `MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` are 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](010-driver-cli.md); the backends that consume source maps are specified in [008 — C Backend](008-c-backend.md) and [009 — LLVM Backend](009-llvm-backend.md). ## 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](001-architecture.md). ```d 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](005-lowering.md)). **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](008-c-backend.md)), and the driver MUST re-map compiler errors through it (see [010 — Driver & CLI](010-driver-cli.md)). ### 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: ```json { "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](005-lowering.md)); - a VIR invariant violation detected in a debug build (see [006 — VIR](006-vir.md)); - a `Strong` request denied for reason `Illegality` or `ContradictsTrait` (see [007 — VIR Optimizer](007-vir-optimizer.md)). **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=` 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: ```d 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](000-overview.md) for conformance and determinism. - [001 — Architecture](001-architecture.md) for the cold-path GC rule. - [002 — Traits](002-traits.md) for the trait vocabulary and mismatch handling. - [005 — Lowering](005-lowering.md) for the lowering map and ICE conditions. - [006 — VIR](006-vir.md) for the invariants checked in debug. - [007 — VIR Optimizer](007-vir-optimizer.md) for request denials and `DenyReason`. - [008 — C Backend](008-c-backend.md) for `#line` emission. - [010 — Driver & CLI](010-driver-cli.md) for `--remarks`, `--diagnostics`, and exit codes.