Skip to content

Error model: attach raw OS code to std.fs KIND_* errors + a stable panic-category vocabulary (idea from the 'el' language) #1378

Description

@paul-hammant

Context

Two small refinements to Aether's error/failure model, prompted by reading the el language (/home/paul/scm/el), which has a clean portable-first I/O error contract (opaque error + closed ErrorKind union + optional raw OS i64 code — el docs/DESIGN.md:1099-1126) and a two-tier failure model whose unrecoverable failures print a stable, greppable category name (index_out_of_bounds, bitstring_size_mismatch, …) + location, then exit — no stack unwinding (el EXAMPLES.md:1143-1159).

Checked both against Aether first so this only asks for what's actually missing. Notably, EINTR retry is already handled across std.os (aether_os.c — ~10 sites loop on EINTR), so that part of el's model is a no-op for us; not asking for it.

Part A — attach the raw OS code to std.fs's portable error kinds

Aether's std.fs has portable structured error kinds (KIND_OK, KIND_NOT_FOUND, KIND_PERMISSION_DENIED, …, std/fs/module.ae:60-63) returned via the (value, kind, message) convention. std.io also exposes the global errno as a message string (io.errno_message() / perror, std/io/module.ae:57,195). What's missing is the raw numeric OS code carried alongside the portable kind on a structured fs error.

el's contract is the useful shape: a portable ErrorKind for cross-platform switching plus an optional target-specific i64 code for the cases where the caller genuinely needs the exact errno (e.g. distinguishing EAGAIN vs EWOULDBLOCK, or logging the precise OS error). The portable kind stays the primary surface; the raw code is an escape hatch, not the default.

Ask: add a raw-OS-code accessor to the fs split-error surface — e.g. a fs_get_error_code() companion to the existing kind/message getters (thread-local, same pattern as fs_get_stat_*), populated from errno at the point of failure. Portable KIND_* unchanged; this is purely additive. Windows fills it from GetLastError() (or leaves 0 where not meaningful).

Part B — a stable, greppable panic-category vocabulary

Aether's unrecoverable failures already go through aether_panic(...) (no unwinding beyond the deferred-free journal — good, matches el's no-landing-pads approach). But the category strings are ad-hoc free-form:

  • "precondition violation: <expr> in <fn>" (codegen_func.c:1232, codegen_stmt.c:3457)
  • "forced unwrap of none" (codegen_expr.c:2345)
  • the expr! unwrap-or-trap forwards the error slot's own string (codegen_expr.c:2415)
  • runtime bounds/overflow traps elsewhere

There's no stable vocabulary — each site invents its wording, so a caller/CI can't reliably grep or match on "was this an out-of-bounds vs a forced-none vs a precondition." el pins a fixed set of category names (index_out_of_bounds, division_by_zero, overflow, invalid_shift, bitstring_size_mismatch, …) emitted verbatim + source location.

Ask: standardize the panic categories — a documented, stable set of leading tokens (precondition_violation:, forced_unwrap_none:, index_out_of_bounds:, integer_overflow:, division_by_zero:, …) that every aether_panic site uses as a prefix, with the human detail after. Benefits: CI can assert on a category, downstreams can triage by grepping a stable token, and the docs get a canonical failure-category list. Purely a message-format + docs contract — no unwinding/semantic change.

Why filed together

Both are the same theme (make failure legible and portable) and both are low-risk, additive contracts rather than semantic changes. Split into two PRs if preferred — Part A is stdlib/C, Part B is codegen message-format + a docs table. Aligns with the existing docs/error-unification.md direction.

Not urgent — filed for consideration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions