ffi M1.2 A.2c + A.3: eager body lowering + self.field via state struct
Adds Pass 4b 'lowerObjcDefinedClassMethods' to lowerRoot: after
scan, walk objc_defined_class_cache and force-lower each bodied
instance method. The Obj-C runtime invokes these via the IMP
pointers wired up in A.4 — no sx-side call path drives lazy
lowering, so we trigger it here. Mirrors the JNI eager-lower
pattern in Pass 5.
Bug fix: lazyLowerFunction has its OWN inline body-lowering
path (separate from lowerFunction) that re-resolves param types
at line 1025. It was running without current_foreign_class set,
so '*Self' fell through to the type_bridge fallback and got
interned as a 0-field struct named 'Self' — body's
'self.counter' GEP'd into '{}' and LLVM verification rejected.
Fix: set current_foreign_class at the top of lazyLowerFunction
via the same lookupObjcDefinedClassForMethod path lowerFunction
uses. Save+restore via defer.
A.3 ('self.field access via the ivar') falls out for free —
'*Self' resolves to '*__SxFooState' so 'self.counter' is a
plain struct field access. IR snapshot in
142-objc-class-method-lowering.ir shows the round-trip:
define internal void @SxFoo.bump(ptr, ptr self) {
%gep = getelementptr inbounds { i32 }, ptr %self, 0, 0
%v = load i32, ptr %gep
store i32 (%v + 1), ptr %gep
ret void
}
171 examples pass (+1 from 142); zig build test green.
Still gated: Obj-C runtime dispatch (A.7) — sx-side
'f.bump()' calls bail at lower.zig:4407 with the existing
diagnostic. IMP-trampoline emission (the C-ABI shim that bridges
'objc_msgSend' → this body) lands in A.4 alongside class-pair
init.
This commit is contained in:
@@ -247,6 +247,12 @@ pub const Lowering = struct {
|
||||
self.lowerDeferredTypeFns();
|
||||
// Pass 4: target-specific entry-point sanity checks
|
||||
self.checkRequiredEntryPoints();
|
||||
// Pass 4b: eagerly lower bodied methods on sx-defined `#objc_class`
|
||||
// declarations. The Obj-C runtime calls these via IMP pointers
|
||||
// registered in M1.2 A.4 — no sx-side call path drives lazy
|
||||
// lowering, so we trigger it here. Mirrors the JNI eager-lower
|
||||
// pattern in Pass 5.
|
||||
self.lowerObjcDefinedClassMethods();
|
||||
// Pass 5: synthesize JNI-mangled exports for `#jni_main` bodied methods.
|
||||
// Android's JNI runtime resolves `private native sx_<m>(...)` declared in
|
||||
// the bundled classes.dex by looking up the symbol
|
||||
@@ -886,6 +892,17 @@ pub const Lowering = struct {
|
||||
fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
// Already lowered?
|
||||
if (self.lowered_functions.contains(name)) return;
|
||||
|
||||
// For sx-defined `#objc_class` methods, pin current_foreign_class
|
||||
// so `*Self` substitutions in resolveTypeWithBindings find the
|
||||
// state-struct type (M1.2 A.2b). The inline body-lowering path
|
||||
// below re-resolves param types, so the context must be set
|
||||
// BEFORE any resolveReturnType / resolveParamType call.
|
||||
const saved_fc_lazy = self.current_foreign_class;
|
||||
defer self.current_foreign_class = saved_fc_lazy;
|
||||
if (self.lookupObjcDefinedClassForMethod(name)) |fcd| {
|
||||
self.current_foreign_class = fcd;
|
||||
}
|
||||
// No AST? (builtins, foreign functions, or imported functions not in this file)
|
||||
const fd = self.fn_ast_map.get(name) orelse return;
|
||||
// Foreign declarations stay as extern stubs but need to be REGISTERED
|
||||
@@ -11399,6 +11416,28 @@ pub const Lowering = struct {
|
||||
/// receiver), followed by the user-declared params with pointer types
|
||||
/// type-erased to `*void` (JNI carries jobjects, not sx-typed handles —
|
||||
/// future work can keep richer typing inside the body when needed).
|
||||
/// Eagerly lower bodied instance methods on every sx-defined
|
||||
/// `#objc_class`. The Obj-C runtime invokes these via the IMP
|
||||
/// pointers wired up in M1.2 A.4 — no sx-side call path triggers
|
||||
/// lazy lowering, so we walk the cache and force-lower here.
|
||||
/// `lowerFunction` sets `current_foreign_class` automatically based
|
||||
/// on the qualified name, so `*Self` substitutions in the body
|
||||
/// resolve correctly (M1.2 A.2b).
|
||||
fn lowerObjcDefinedClassMethods(self: *Lowering) void {
|
||||
for (self.module.objc_defined_class_cache.items) |entry| {
|
||||
const fcd = entry.decl;
|
||||
for (fcd.members) |m| {
|
||||
const method = switch (m) {
|
||||
.method => |md| md,
|
||||
else => continue,
|
||||
};
|
||||
if (method.body == null) continue;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ fcd.name, method.name }) catch continue;
|
||||
self.lazyLowerFunction(qualified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn synthesizeJniMainStubs(self: *Lowering) void {
|
||||
var seen = std.StringHashMap(void).init(self.alloc);
|
||||
defer seen.deinit();
|
||||
|
||||
Reference in New Issue
Block a user