fix(ir): comptime print of an Any-held Type no longer silently stops [F0.12]

`any_to_string` runs `type := type_of(val)`; for an `.any` operand
`type_of` lowers to `struct_get(val, 0)` to read the Any's tag. At
runtime a first-class Type value is the aggregate `{ tag=.any, value=tid }`
so the read succeeds, but the comptime interpreter stores a Type as a bare
`.type_tag(tid)` and the comptime `struct_get` arm had no case for it — it
raised `CannotEvalComptime`, which `runComptimeSideEffects` swallowed into
`void_val`, truncating the `#run` while still building with exit 0.

- interp.zig: comptime `struct_get` handles a `.type_tag(tid)` base by
  mirroring the runtime Any-Type layout (field 0 -> `.any` tag, field 1 ->
  the type id), so `type_of` of an Any-held Type evaluates as it does at
  runtime and execution continues.
- emit_llvm.zig: `runComptimeSideEffects` no longer swallows a side-effect
  bail; it prints a loud diagnostic and sets `comptime_failed`
  (-> error.ComptimeError, non-zero exit), matching the const-init path.
  A truncated `#run` can no longer ship a successful build.

Regression: examples/0613-comptime-print-any-type.sx (all five lines print,
exit 0). Resolves issue 0096.
This commit is contained in:
agra
2026-06-05 20:48:49 +03:00
parent 794f2ef94a
commit b0d85a858c
7 changed files with 147 additions and 1 deletions

View File

@@ -0,0 +1,32 @@
// Comptime `#run` formatting of an `Any` that holds a `Type`.
//
// `print("{}", at)` where `at: Any` holds a `Type` value routes through
// `format` → `any_to_string`, whose `type := type_of(val)` lowers (for an
// `.any` operand) to `struct_get(val, 0)` — reading the Any-Type's tag.
// At runtime a `Type` value is the aggregate `{ tag=.any, value=tid }`,
// so the read works and the type's name prints. The comptime interpreter
// stores a first-class `Type` as a bare `.type_tag`, so the struct_get
// must mirror that same `{ .any, tid }` layout — otherwise it bails and
// the `#run` truncates. Reflection over the same `Any` (`type_name`,
// `type_is_unsigned`) already works; the value-print must match.
//
// Regression (issue 0096): a comptime `#run` print of an `Any`-held
// `Type` silently stopped (omitted `value=` + every later line) yet still
// built with exit 0.
#import "modules/std.sx";
ct_probe :: () {
print("before\n");
x : u64 = 1;
t : Type = type_of(x);
at : Any = t;
print("name={}\n", type_name(at));
print("unsigned={}\n", type_is_unsigned(at));
print("value={}\n", at);
print("after\n");
}
#run ct_probe();
main :: () {}

View File

@@ -0,0 +1 @@
0

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,6 @@
before
name=u64
unsigned=true
value=u64
after
--- build done ---