Files
libvct-spec/spec/011-diagnostics.md

247 lines
8.7 KiB
Markdown

# 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=<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:
```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.