fix(ffi): replace silent .void arg-type fallback with loud .unresolved (issue 0074)
Four FFI call-arg lowering sites resolved an argument's IR type via `getRefIRType(arg_ref) orelse .void` — a silent fallback to the load-bearing real type `.void`. A failed lookup there is a codegen invariant violation, but `.void` is treated by downstream `toLLVMType` → `abiCoerceParamType` → `coerceArg` as a legitimate void-typed foreign argument, corrupting the call ABI with no diagnostic. Add one shared resolver `LLVMEmitter.argIRTypeOrFail` that returns the dedicated `.unresolved` sentinel on a failed lookup — never `.void`/`.s64` — so the failure cannot masquerade as a real type and trips `toLLVMType`'s existing hard `@panic` tripwire at the call site. Route all four sites through it: - src/ir/emit_llvm.zig JNI constructor (NewObject) arg loop - src/backend/llvm/ops.zig objc_msgSend arg loop - src/backend/llvm/ops.zig JNI non-virtual call arg loop - src/backend/llvm/ops.zig JNI Call<Type>Method arg loop Happy path is byte-identical (every real arg already has a resolved type); FFI examples stay green with zero snapshot churn. Regression test (fail-before/pass-after) in src/ir/emit_llvm.test.zig asserts an unresolvable FFI arg ref now yields `.unresolved`, not the old silent `.void`.
This commit is contained in:
@@ -2239,6 +2239,16 @@ pub const LLVMEmitter = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Resolve the IR type of a foreign-call argument ref. Every FFI arg ref is
|
||||
/// a real function param or block instruction result, so a `null` here is a
|
||||
/// codegen invariant violation, not a recoverable case: return the dedicated
|
||||
/// `.unresolved` sentinel — never `.void`/`.s64` — so the failure cannot be
|
||||
/// mistaken for a real type and trips `toLLVMType`'s hard tripwire at the call
|
||||
/// site instead of silently emitting a void-typed foreign argument.
|
||||
pub fn argIRTypeOrFail(self: *LLVMEmitter, arg_ref: Ref) TypeId {
|
||||
return self.getRefIRType(arg_ref) orelse .unresolved;
|
||||
}
|
||||
|
||||
|
||||
/// Coerce both binary operands to match the instruction's result type.
|
||||
/// E.g. if result is i64 but one operand is i32, sext it.
|
||||
@@ -2460,7 +2470,7 @@ pub const LLVMEmitter = struct {
|
||||
call_args[1] = cls;
|
||||
call_args[2] = mid;
|
||||
for (msg.args, 0..) |arg_ref, i| {
|
||||
const raw_ty = self.getRefIRType(arg_ref) orelse .void;
|
||||
const raw_ty = self.argIRTypeOrFail(arg_ref);
|
||||
const raw_llvm = self.toLLVMType(raw_ty);
|
||||
const coerced_ty = self.abiCoerceParamType(raw_ty, raw_llvm);
|
||||
call_param_types[i + 3] = coerced_ty;
|
||||
|
||||
Reference in New Issue
Block a user