ffi M1.2 A.1: objcTypeEncodingFromSignature helper + encoding table
Derives Apple's runtime type-encoding string from an IR method signature. Called by class_addMethod(cls, sel, imp, types) when M1.2 A.4+ synthesise IMPs for sx-defined classes. Layout: <ret> @ : <param0> <param1> ... — @ is the receiver, : is _cmd. Caller passes user-declared params AFTER stripping 'self: *Self'. Encoding table: v=void B=bool c=s8/BOOL s=s16 i=s32 q=s64 C=u8 S=u16 I=u32 Q=u64 f=f32 d=f64 @=foreign Obj-C class ptr #=Class :=SEL *=[*]u8 (C string) ^v=any other ptr bool (sx i1) maps to 'B' (C99 _Bool); s8 to 'c' (Apple's BOOL). Foreign-class pointers detected via foreign_class_map lookup on the pointee struct name. Other pointers fall to ^v — encoding is metadata, not ABI, so conservative is safe. Struct / slice / closure / etc. BAIL via diagnostic (ObjcEncodingUnsupported) rather than silently mis-encoding, per CLAUDE.md rejected-patterns rule. Future passes will widen the table as new shapes show up in real IMPs. Dead code at this commit — helper isn't called yet. Three unit tests in src/ir/lower.test.zig pin the primitive / pointer / Obj-C-class-pointer encodings before A.2 wires the helper in. 170 example tests + zig build test green.
This commit is contained in:
@@ -221,3 +221,108 @@ test "lower: while loop generates header/body/exit blocks" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, output, "while.exit") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, output, "cond_br") != null);
|
||||
}
|
||||
|
||||
// M1.2 A.1 — Obj-C type-encoding helper.
|
||||
test "lower: objcTypeEncodingFromSignature emits primitive shapes" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = ir_mod.Module.init(alloc);
|
||||
defer module.deinit();
|
||||
var lowering = Lowering.init(&module);
|
||||
|
||||
// Niladic void method: -(void)greet → "v@:"
|
||||
const e1 = try lowering.objcTypeEncodingFromSignature(.void, &.{}, null);
|
||||
defer alloc.free(e1);
|
||||
try std.testing.expectEqualStrings("v@:", e1);
|
||||
|
||||
// Returns s32, takes s32: -(int)add:(int)x → "i@:i"
|
||||
const e2 = try lowering.objcTypeEncodingFromSignature(.s32, &.{.s32}, null);
|
||||
defer alloc.free(e2);
|
||||
try std.testing.expectEqualStrings("i@:i", e2);
|
||||
|
||||
// s64 return, two s64 args: "q@:qq"
|
||||
const e3 = try lowering.objcTypeEncodingFromSignature(.s64, &.{ .s64, .s64 }, null);
|
||||
defer alloc.free(e3);
|
||||
try std.testing.expectEqualStrings("q@:qq", e3);
|
||||
|
||||
// BOOL return (s8): "c@:"
|
||||
const e4 = try lowering.objcTypeEncodingFromSignature(.s8, &.{}, null);
|
||||
defer alloc.free(e4);
|
||||
try std.testing.expectEqualStrings("c@:", e4);
|
||||
|
||||
// Float/double: "f@:d"
|
||||
const e5 = try lowering.objcTypeEncodingFromSignature(.f32, &.{.f64}, null);
|
||||
defer alloc.free(e5);
|
||||
try std.testing.expectEqualStrings("f@:d", e5);
|
||||
|
||||
// bool (i1) is `B` — distinct from BOOL (`c`).
|
||||
const e6 = try lowering.objcTypeEncodingFromSignature(.bool, &.{.bool}, null);
|
||||
defer alloc.free(e6);
|
||||
try std.testing.expectEqualStrings("B@:B", e6);
|
||||
|
||||
// usize / isize on the 64-bit target.
|
||||
const e7 = try lowering.objcTypeEncodingFromSignature(.usize, &.{.isize}, null);
|
||||
defer alloc.free(e7);
|
||||
try std.testing.expectEqualStrings("Q@:q", e7);
|
||||
|
||||
// Unsigned variants u8/u16/u32/u64.
|
||||
const e8 = try lowering.objcTypeEncodingFromSignature(.u32, &.{ .u8, .u16, .u64 }, null);
|
||||
defer alloc.free(e8);
|
||||
try std.testing.expectEqualStrings("I@:CSQ", e8);
|
||||
}
|
||||
|
||||
test "lower: objcTypeEncodingFromSignature emits pointer shapes" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = ir_mod.Module.init(alloc);
|
||||
defer module.deinit();
|
||||
var lowering = Lowering.init(&module);
|
||||
|
||||
// Generic `*void` → `^v`.
|
||||
const void_ptr = module.types.ptrTo(.void);
|
||||
const e1 = try lowering.objcTypeEncodingFromSignature(void_ptr, &.{void_ptr}, null);
|
||||
defer alloc.free(e1);
|
||||
try std.testing.expectEqualStrings("^v@:^v", e1);
|
||||
|
||||
// `[*]u8` C-string carrier → `*`.
|
||||
const u8_many = module.types.intern(.{ .many_pointer = .{ .element = .u8 } });
|
||||
const e2 = try lowering.objcTypeEncodingFromSignature(.void, &.{u8_many}, null);
|
||||
defer alloc.free(e2);
|
||||
try std.testing.expectEqualStrings("v@:*", e2);
|
||||
|
||||
// `[*]s32` (non-u8 many-pointer) → `^v`.
|
||||
const s32_many = module.types.intern(.{ .many_pointer = .{ .element = .s32 } });
|
||||
const e3 = try lowering.objcTypeEncodingFromSignature(.void, &.{s32_many}, null);
|
||||
defer alloc.free(e3);
|
||||
try std.testing.expectEqualStrings("v@:^v", e3);
|
||||
}
|
||||
|
||||
test "lower: objcTypeEncodingFromSignature emits @ for Obj-C class pointers" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = ir_mod.Module.init(alloc);
|
||||
defer module.deinit();
|
||||
var lowering = Lowering.init(&module);
|
||||
|
||||
// Synthesize a foreign Obj-C class entry so the encoder recognises
|
||||
// `*NSString` as an object pointer.
|
||||
const ns_name = module.types.internString("NSString");
|
||||
const ns_struct = module.types.intern(.{ .@"struct" = .{ .name = ns_name, .fields = &.{} } });
|
||||
const ns_ptr = module.types.ptrTo(ns_struct);
|
||||
var ns_fcd = ast.ForeignClassDecl{
|
||||
.name = "NSString",
|
||||
.foreign_path = "NSString",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.foreign_class_map.put("NSString", &ns_fcd);
|
||||
|
||||
// Return *NSString, no args: "@@:"
|
||||
const e1 = try lowering.objcTypeEncodingFromSignature(ns_ptr, &.{}, null);
|
||||
defer alloc.free(e1);
|
||||
try std.testing.expectEqualStrings("@@:", e1);
|
||||
|
||||
// Return *NSString, take *NSString: "@@:@"
|
||||
const e2 = try lowering.objcTypeEncodingFromSignature(ns_ptr, &.{ns_ptr}, null);
|
||||
defer alloc.free(e2);
|
||||
try std.testing.expectEqualStrings("@@:@", e2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user