fix(reflection): replace silent .s64 arg-type fallback with loud .unresolved (issue 0075)
The `type_name` / `type_eq` reflection builtins resolved their Type arg's IR
type via `getRefIRType(...) orelse TypeId.s64`, then gated `== .any`. A failed
must-succeed lookup silently became `.s64` (`!= .any`), classifying a boxed
`Any` arg as bare i64 and reading the wrong value with no diagnostic.
Add the sibling classifier `LLVMEmitter.reflectArgRepr`, which routes the
lookup through `argIRTypeOrFail` (the issue-0074 `.unresolved` resolver) and
returns `{ boxed, bare, unresolved }`. The three emit sites in ops.zig
(`type_name` + `type_eq` x2) now switch on it: `.boxed` extracts the Any value
field, `.bare` uses the value directly, `.unresolved` hits a hard `@panic`
tripwire — never silently treated as bare. Real args always resolve, so the
happy path is byte-identical (suite stays 361/0, zero snapshot churn).
Secondary `lower.zig` `null_literal`/`undef_literal => target_type orelse .void`
confirmed intentional (typeless-literal default deliberately handled by
emitConstNull/emitConstUndef as null-ptr / undef-i64) — left with an invariant
comment, not the `.unresolved` tripwire.
Regression test in emit_llvm.test.zig asserts the loud path: fail-before with
`orelse .s64` yields `.bare`; pass-after yields `.unresolved`.
This commit is contained in:
@@ -1020,14 +1020,12 @@ pub const Ops = struct {
|
||||
// index directly.
|
||||
const arg_ref = bi.args[0];
|
||||
const arg_val = self.e.resolveRef(arg_ref);
|
||||
const arg_ir_ty = self.e.getRefIRType(arg_ref) orelse TypeId.s64;
|
||||
const tid_idx = blk: {
|
||||
if (arg_ir_ty == .any) {
|
||||
// Boxed: extract value field.
|
||||
break :blk c.LLVMBuildExtractValue(self.e.builder, arg_val, 1, "tn.tid");
|
||||
}
|
||||
const tid_idx = switch (self.e.reflectArgRepr(arg_ref)) {
|
||||
.unresolved => @panic("type_name: reflection arg IR-type unresolved — a type-resolution failure reached LLVM emission without a diagnostic"),
|
||||
// Boxed: extract value field from the Any aggregate.
|
||||
.boxed => c.LLVMBuildExtractValue(self.e.builder, arg_val, 1, "tn.tid"),
|
||||
// Bare i64 (TypeId index).
|
||||
break :blk arg_val;
|
||||
.bare => arg_val,
|
||||
};
|
||||
const arr_global = self.e.reflection().getOrBuildTypeNameArray();
|
||||
const arr_len = self.e.type_name_array_len;
|
||||
@@ -1046,15 +1044,19 @@ pub const Ops = struct {
|
||||
// icmp eq.
|
||||
const a = blk: {
|
||||
const v = self.e.resolveRef(bi.args[0]);
|
||||
const ty = self.e.getRefIRType(bi.args[0]) orelse TypeId.s64;
|
||||
if (ty == .any) break :blk c.LLVMBuildExtractValue(self.e.builder, v, 1, "te.a");
|
||||
break :blk v;
|
||||
break :blk switch (self.e.reflectArgRepr(bi.args[0])) {
|
||||
.unresolved => @panic("type_eq: first reflection arg IR-type unresolved — a type-resolution failure reached LLVM emission without a diagnostic"),
|
||||
.boxed => c.LLVMBuildExtractValue(self.e.builder, v, 1, "te.a"),
|
||||
.bare => v,
|
||||
};
|
||||
};
|
||||
const b = blk: {
|
||||
const v = self.e.resolveRef(bi.args[1]);
|
||||
const ty = self.e.getRefIRType(bi.args[1]) orelse TypeId.s64;
|
||||
if (ty == .any) break :blk c.LLVMBuildExtractValue(self.e.builder, v, 1, "te.b");
|
||||
break :blk v;
|
||||
break :blk switch (self.e.reflectArgRepr(bi.args[1])) {
|
||||
.unresolved => @panic("type_eq: second reflection arg IR-type unresolved — a type-resolution failure reached LLVM emission without a diagnostic"),
|
||||
.boxed => c.LLVMBuildExtractValue(self.e.builder, v, 1, "te.b"),
|
||||
.bare => v,
|
||||
};
|
||||
};
|
||||
const eq_res = c.LLVMBuildICmp(self.e.builder, c.LLVMIntEQ, a, b, "te.eq");
|
||||
self.e.mapRef(eq_res);
|
||||
|
||||
Reference in New Issue
Block a user