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:
@@ -828,7 +828,23 @@ pub const LLVMEmitter = struct {
|
||||
interp_inst.build_config = &self.build_config;
|
||||
if (self.import_sources) |sm| interp_inst.setSourceMap(sm);
|
||||
sx_trace_clear();
|
||||
const result = interp_inst.call(func_id, &.{}) catch Value.void_val;
|
||||
Interpreter.last_bail_op = null;
|
||||
Interpreter.last_bail_builtin = null;
|
||||
Interpreter.last_bail_detail = null;
|
||||
const result = interp_inst.call(func_id, &.{}) catch |err| blk: {
|
||||
// A comptime `#run` side-effect that bails must NOT silently
|
||||
// truncate its output and still ship a successful build.
|
||||
// Surface the bail loudly and fail the build, mirroring the
|
||||
// const-init path in emitGlobals. Whatever output the run
|
||||
// produced before the bail is flushed below so the user sees
|
||||
// where execution stopped.
|
||||
const op = Interpreter.last_bail_op orelse "<unknown>";
|
||||
const detail = Interpreter.last_bail_detail orelse "";
|
||||
const sep: []const u8 = if (detail.len > 0) ": " else "";
|
||||
std.debug.print("error: comptime `#run` ({s}) failed: {s} (op={s}{s}{s})\n", .{ fname, @errorName(err), op, sep, detail });
|
||||
self.comptime_failed = true;
|
||||
break :blk Value.void_val;
|
||||
};
|
||||
// Route #run `print` output to fd 1 so it joins the
|
||||
// JIT-executed runtime's stream. Same call site shape as
|
||||
// `core.flushInterpOutput` — see issue-0047.
|
||||
|
||||
@@ -916,6 +916,17 @@ pub const Interpreter = struct {
|
||||
if (fa.field_index == 0) return .{ .value = .{ .int = v } };
|
||||
return error.OutOfBounds;
|
||||
},
|
||||
.type_tag => |tid| {
|
||||
// A first-class Type value is the comptime form of the
|
||||
// runtime Any-Type aggregate `{ tag=.any, value=tid }`
|
||||
// (see `const_type` lowering in buildPackSliceValue).
|
||||
// `type_of(any_holding_a_Type)` lowers to struct_get
|
||||
// field 0, expecting that runtime layout — mirror it so
|
||||
// field 0 reads the `.any` tag and field 1 the type id.
|
||||
if (fa.field_index == 0) return .{ .value = .{ .int = @intCast(TypeId.any.index()) } };
|
||||
if (fa.field_index == 1) return .{ .value = .{ .type_tag = tid } };
|
||||
return error.OutOfBounds;
|
||||
},
|
||||
else => return typeErrorDetail("comptime struct_get: base has no fields (not an aggregate/string/int)"),
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user