ffi M1.2 A.2a: objcDefinedStateStructType helper
Builds (and interns) the hidden sx-state struct type for an
sx-defined '#objc_class'. Layout:
__<ClassName>State {
user_field_0,
user_field_1,
...
}
This struct is what the runtime's '__sx_state' ivar points at —
separate from the Obj-C object itself, which stays opaque. The
sx method bodies will operate on '*__SxFooState' (after '*Self'
substitution in A.2b) so 'self.field' resolves to a plain struct
field access — A.3's 'free if types align' premise.
M1.2 A.5 will prepend '__sx_allocator: Allocator' so dealloc can
free through the per-instance allocator. Field-by-name access
stays correct across the future repositioning.
Methods / '#extends' / '#implements' members are ignored — only
'.field' contributes. Three unit tests pin: typical-field case,
empty-class case, mixed-member case.
Dead code at this commit — helper isn't called yet. A.2b (body
lowering with '*Self' substitution) wires it in. 170 example
tests + zig build test green.
This commit is contained in:
@@ -4672,6 +4672,49 @@ pub const Lowering = struct {
|
||||
return self.module.types.intern(.{ .@"struct" = .{ .name = name_id, .fields = &.{} } });
|
||||
}
|
||||
|
||||
/// Build (and cache) the hidden sx-state struct type for an sx-defined
|
||||
/// `#objc_class`. The state struct is what the runtime's `__sx_state`
|
||||
/// ivar points at — separate from the Obj-C object itself, which stays
|
||||
/// opaque. Layout (M1.2 A.2):
|
||||
///
|
||||
/// __<ClassName>State {
|
||||
/// user_field_0,
|
||||
/// user_field_1,
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// M1.2 A.5 will prepend `__sx_allocator: Allocator` so `-dealloc`
|
||||
/// can free through the per-instance allocator and method bodies can
|
||||
/// access `self.allocator`. For A.2 the struct holds only the
|
||||
/// user-declared fields — sufficient for the body lowering +
|
||||
/// `self.field` access work in A.2/A.3. Field-by-name resolution
|
||||
/// stays correct across the future repositioning.
|
||||
///
|
||||
/// Foreign-class members other than `.field` are ignored here —
|
||||
/// methods / `#extends` / `#implements` don't contribute to the
|
||||
/// state layout.
|
||||
fn objcDefinedStateStructType(self: *Lowering, fcd: *const ast.ForeignClassDecl) TypeId {
|
||||
const state_name = std.fmt.allocPrint(self.alloc, "__{s}State", .{fcd.name}) catch unreachable;
|
||||
const name_id = self.module.types.internString(state_name);
|
||||
if (self.module.types.findByName(name_id)) |existing| return existing;
|
||||
|
||||
var fields = std.ArrayList(types.TypeInfo.StructInfo.Field).empty;
|
||||
for (fcd.members) |m| {
|
||||
switch (m) {
|
||||
.field => |f| {
|
||||
const f_name_id = self.module.types.internString(f.name);
|
||||
const f_ty = self.resolveType(f.field_type);
|
||||
fields.append(self.alloc, .{ .name = f_name_id, .ty = f_ty }) catch unreachable;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
return self.module.types.intern(.{ .@"struct" = .{
|
||||
.name = name_id,
|
||||
.fields = fields.toOwnedSlice(self.alloc) catch unreachable,
|
||||
} });
|
||||
}
|
||||
|
||||
/// Lower `inst.method(args)` on an `#objc_class` / `#objc_protocol`
|
||||
/// receiver. The selector is derived by `deriveObjcSelector`; arity
|
||||
/// is validated against the keyword count produced by the mangling
|
||||
|
||||
Reference in New Issue
Block a user