ffi fix: route foreign-class UFCS arg target_types through extends chain
For UFCS dispatch on foreign-class receivers (`#foreign #objc_class` aliases), `resolveCallParamTypes` was returning an empty slice — both `resolveFuncByName(qualified)` and `fn_ast_map.get(qualified)` miss for `#foreign` methods (they live in `foreign_class_map`, not the regular fn maps). With `param_types` empty, the per-arg `target_type` assignment in `lowerCall` was skipped, leaving `self.target_type` as whatever it held on entry — usually the enclosing function's return type. Inside a `-> BOOL` method, `xx ptr` then lowered with target type `i8`: `ptrtoint ptr to i64` → `trunc i64 to i8`, sending the low byte of the pointer through. Symptom: chess on iOS-sim crashed in `-[NSNotificationCenter addObserver:selector:name:object:]` with `observer = 0xC0` (low byte of the SxAppDelegate receiver) when the AppDelegate method's first param was renamed to anything other than `self`. The original session diagnosed it as a `self`-vs-`this` hardcoding in `lower.zig`, but those hardcoded `"self"` strings are all on compiler-synthesized parameters (init scopes, JNI stubs, property IMPs, dealloc IMPs) — not the user-facing #objc_class body params. The bug was in arg-type resolution. Fix walks `foreign_class_map` + `findForeignMethodInChain` to recover the declared param types (skipping the implicit `*Self` for instance methods). Regression test `examples/issue-0044.sx` exercises the BOOL-return + foreign-class arg shape; pre-fix the receiver round-trip prints WRONG, post-fix it prints ok.
This commit is contained in:
@@ -8615,6 +8615,30 @@ pub const Lowering = struct {
|
||||
}
|
||||
}
|
||||
if (self.getStructTypeName(obj_ty)) |sname| {
|
||||
// Foreign-class receiver (`#objc_class` / `#jni_class` / etc.):
|
||||
// resolve the method from `foreign_class_map` walking `#extends`.
|
||||
// Without this path, `target_type` for each arg falls back to
|
||||
// whatever `self.target_type` was on entry — typically the
|
||||
// enclosing fn's return type — which silently truncates `xx ptr`
|
||||
// casts inside e.g. a `BOOL`-returning method body.
|
||||
if (self.foreign_class_map.get(sname)) |fcd| {
|
||||
if (self.findForeignMethodInChain(fcd, fa.field)) |found| {
|
||||
const md = found.method;
|
||||
const saved_fc = self.current_foreign_class;
|
||||
defer self.current_foreign_class = saved_fc;
|
||||
self.current_foreign_class = found.fcd;
|
||||
const user_param_start: usize = if (md.is_static) 0 else 1;
|
||||
if (md.params.len > user_param_start) {
|
||||
var types_list = std.ArrayList(TypeId).empty;
|
||||
for (md.params[user_param_start..]) |p_node| {
|
||||
types_list.append(self.alloc, self.resolveType(p_node)) catch unreachable;
|
||||
}
|
||||
return types_list.items;
|
||||
}
|
||||
return &.{};
|
||||
}
|
||||
}
|
||||
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sname, fa.field }) catch return &.{};
|
||||
// Try already-lowered functions first
|
||||
if (self.resolveFuncByName(qualified)) |fid| {
|
||||
|
||||
Reference in New Issue
Block a user