ERR/E3: error-tag {} interpolation via an always-linked tag-name table

`{}` on an error-set value printed `<?>` (any_to_string had no error_set
category). Now it renders the tag name (`BadDigit`), reusing the existing
any_to_string dispatch.

Pieces:
- New `error_tag_name_get` IR op (UnaryOp): tag id -> name. Lowered from a new
  `error_tag_name(e) -> string #builtin` (std.sx). Handled across inst.zig
  (op def), print.zig, interp.zig (comptime: tags.getName), and emit_llvm.zig.
- emit_llvm getOrBuildTagNameArray: an always-linked `[N x {ptr,i64}]` global
  of tag names indexed by global tag id (the TagRegistry namespace, slot 0 =
  ""). error_tag_name_get zext's the u32 tag value and GEPs into it. Built once;
  not trace-gated, so it works in release too (per the spec's "tag-name table
  always shipped").
- resolveTypeCategoryTags gains an `error_set` category so the
  `case error_set:` arm in any_to_string matches; that arm coerces the Any to
  u32 (`xx val`) and calls error_tag_name. (cast(type) didn't recover the tag
  id for error-set values; the u32 coercion does.)

examples/240-error-tag-interpolation.sx: bound tags + a catch-bound tag print
their names. Regenerated ffi-objc-call-06-sret-return.ir — pure block-renumber
drift from adding one if-arm to the shared any_to_string (verified
semantically identical after collapsing block numbers).

Gates: zig build, zig build test, bash tests/run_examples.sh (277 passed; lone
failure is the user's uncommitted 213-canonical-map pack WIP).
This commit is contained in:
agra
2026-06-01 07:47:32 +03:00
parent 6e32e6c63c
commit a3ff503f47
10 changed files with 684 additions and 569 deletions

View File

@@ -0,0 +1,29 @@
// Error-tag `{}` interpolation (ERR step E3 — tag-name table). Formatting an
// error-set value with `{}` renders the tag NAME (`BadDigit`), not the raw id,
// reusing the `any_to_string` dispatch (new `error_set` category → the
// `error_tag_name` builtin → the always-linked tag-name table indexed by global
// tag id). Works for a bound tag, a re-raised/caught tag, and inside text.
#import "modules/std.sx";
E :: error { BadDigit, Empty, Overflow }
parse :: (n: s32) -> (s32, !E) {
if n < 0 { raise error.BadDigit; }
if n == 0 { raise error.Empty; }
return n * 2;
}
main :: () -> s32 {
a : E = error.BadDigit;
b : E = error.Overflow;
print("a={} b={}\n", a, b); // a=BadDigit b=Overflow
// A tag bound by `catch` interpolates too (diverging handler).
v := parse(0) catch e {
print("parse failed with {}\n", e); // parse failed with Empty
return 0;
};
print("v={}\n", v); // not reached (parse(0) raises Empty)
return 0;
}