From db99ba17af2ed3b1fc05c463f86cdb46eb0aae04 Mon Sep 17 00:00:00 2001 From: Lorenzo Torres Date: Tue, 13 Jan 2026 20:07:32 +0100 Subject: [PATCH] Fix compilation on Zig 0.16 --- build.zig | 7 ++-- src/block.zig | 106 +++++++++++++++++++++++----------------------- src/encoding.zig | 16 +++---- src/msg_send.zig | 107 ++++++++++++++++++++++++++++++++++++----------- src/object.zig | 4 +- src/property.zig | 11 ++--- src/protocol.zig | 4 +- src/sel.zig | 4 +- 8 files changed, 163 insertions(+), 96 deletions(-) diff --git a/build.zig b/build.zig index a55bd38..2745bb2 100644 --- a/build.zig +++ b/build.zig @@ -26,9 +26,9 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }), }); - tests.linkSystemLibrary("objc"); - tests.linkFramework("Foundation"); - tests.linkFramework("AppKit"); // Required by 'tagged pointer' test. + tests.root_module.linkSystemLibrary("objc", .{}); + tests.root_module.linkFramework("Foundation", .{}); + tests.root_module.linkFramework("AppKit", .{}); // Required by 'tagged pointer' test. try addAppleSDK(b, tests.root_module); b.installArtifact(tests); @@ -68,6 +68,7 @@ pub fn addAppleSDK(b: *std.Build, m: *std.Build.Module) !void { if (!gop.found_existing) { gop.value_ptr.* = std.zig.system.darwin.getSdk( b.allocator, + b.graph.io, &m.resolved_target.?.result, ); } diff --git a/src/block.zig b/src/block.zig index 4df6dca..0ed77b0 100644 --- a/src/block.zig +++ b/src/block.zig @@ -150,21 +150,21 @@ pub fn Block( /// the first arg. The first arg is a pointer so from an ABI perspective /// this is always the same and can be safely casted. fn FnType(comptime ContextArg: type) type { - var params: [Args.len + 1]std.builtin.Type.Fn.Param = undefined; - params[0] = .{ .is_generic = false, .is_noalias = false, .type = *const ContextArg }; + var params: [Args.len + 1]std.builtin.Type.Fn.Param.Attributes = undefined; + var types: [Args.len + 1]type = undefined; + params[0] = .{}; + types[0] = *const ContextArg; for (Args, 1..) |Arg, i| { - params[i] = .{ .is_generic = false, .is_noalias = false, .type = Arg }; + params[i] = .{}; + types[i] = Arg; } - return @Type(.{ - .@"fn" = .{ - .calling_convention = .c, - .is_generic = false, - .is_var_args = false, - .return_type = Return, - .params = ¶ms, - }, - }); + return @Fn( + &types, + ¶ms, + Return, + .{.@"callconv" = .c}, + ); } }; } @@ -173,41 +173,43 @@ pub fn Block( /// argument to any block invocation. See Block. fn BlockContext(comptime Captures: type, comptime InvokeFn: type) type { const captures_info = @typeInfo(Captures).@"struct"; - var fields: [captures_info.fields.len + 5]std.builtin.Type.StructField = undefined; - fields[0] = .{ - .name = "isa", - .type = ?*anyopaque, + var field_names: [captures_info.fields.len + 5][]const u8 = undefined; + var field_types: [captures_info.fields.len + 5]type = undefined ; + var field_attrs: [captures_info.fields.len + 5]std.builtin.Type.StructField.Attributes = undefined; + field_names[0] = "isa"; + field_types[0] = ?*anyopaque; + field_attrs[0] = .{ .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(*anyopaque), + .@"comptime" = false, + .@"align" = @alignOf(*anyopaque), }; - fields[1] = .{ - .name = "flags", - .type = BlockFlags, + field_names[1] = "flags"; + field_types[1] = BlockFlags; + field_attrs[1] = .{ .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(c_int), + .@"comptime" = false, + .@"align" = @alignOf(c_int), }; - fields[2] = .{ - .name = "reserved", - .type = c_int, + field_names[2] = "reserved"; + field_types[2] = c_int; + field_attrs[2] = .{ .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(c_int), + .@"comptime" = false, + .@"align" = @alignOf(c_int), }; - fields[3] = .{ - .name = "invoke", - .type = *const InvokeFn, + field_names[3] = "invoke"; + field_types[3] = *const InvokeFn; + field_attrs[3] = .{ .default_value_ptr = null, - .is_comptime = false, - .alignment = @typeInfo(*const InvokeFn).pointer.alignment, + .@"comptime" = false, + .@"align" = @typeInfo(*const InvokeFn).pointer.alignment, }; - fields[4] = .{ - .name = "descriptor", - .type = *const Descriptor, + field_names[4] = "descriptor"; + field_types[4] = *const Descriptor; + field_attrs[4] = .{ .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(*Descriptor), + .@"comptime" = false, + .@"align" = @alignOf(*Descriptor), }; for (captures_info.fields, 5..) |capture, i| { @@ -216,24 +218,24 @@ fn BlockContext(comptime Captures: type, comptime InvokeFn: type) type { comptime_float => @compileError("capture should not be a comptime_float, try using @as"), else => {}, } - - fields[i] = .{ - .name = capture.name, - .type = capture.type, + + field_names[i] = capture.name; + field_types[i] = capture.type; + field_attrs[i] = .{ .default_value_ptr = null, - .is_comptime = false, - .alignment = capture.alignment, + .@"comptime" = false, + .@"align" = capture.alignment, }; + } - return @Type(.{ - .@"struct" = .{ - .layout = .@"extern", - .fields = &fields, - .decls = &.{}, - .is_tuple = false, - }, - }); + return @Struct( + .@"extern", + null, + &field_names, + &field_types, + &field_attrs + ); } // Pointer to opaque instead of anyopaque: https://github.com/ziglang/zig/issues/18461 diff --git a/src/encoding.zig b/src/encoding.zig index 7cfdf1b..240b57f 100644 --- a/src/encoding.zig +++ b/src/encoding.zig @@ -10,9 +10,10 @@ fn comptimeN(comptime T: type) usize { const encoding = objc.Encoding.init(T); // Figure out how much space we need - var stream: std.io.Writer.Discarding = .init(&.{}); - stream.writer.print("{f}", .{encoding}) catch unreachable; - return stream.count; + var buffer: [512]u8 = undefined; + var discarding: std.Io.Writer.Discarding = .init(&buffer); + discarding.writer.print("{f}", .{encoding}) catch unreachable; + return discarding.fullCount(); } } @@ -23,11 +24,10 @@ pub fn comptimeEncode(comptime T: type) [comptimeN(T):0]u8 { // Build our final signature var buf: [comptimeN(T) + 1]u8 = undefined; - var fbs: std.io.Writer = .fixed(buf[0 .. buf.len - 1]); - fbs.print("{f}", .{encoding}) catch unreachable; - buf[buf.len - 1] = 0; + const result = std.fmt.bufPrint(buf[0 .. buf.len - 1], "{f}", .{encoding}) catch unreachable; + buf[result.len] = 0; - return buf[0 .. buf.len - 1 :0].*; + return buf[0..result.len :0].*; } } @@ -107,7 +107,7 @@ pub const Encoding = union(enum) { pub fn format( comptime self: Encoding, - writer: *std.io.Writer, + writer: anytype, ) !void { switch (self) { .char => try writer.writeAll("c"), diff --git a/src/msg_send.zig b/src/msg_send.zig index 7b5dcb2..8789227 100644 --- a/src/msg_send.zig +++ b/src/msg_send.zig @@ -37,7 +37,31 @@ pub fn MsgSend(comptime T: type) type { const Fn = MsgSendFn(RealReturn, @TypeOf(target.value), @TypeOf(args)); const msg_send_fn = comptime msgSendPtr(RealReturn, false); const msg_send_ptr: *const Fn = @ptrCast(msg_send_fn); - const result = @call(.auto, msg_send_ptr, .{ target.value, sel.value } ++ args); + const target_id: c.id = @ptrCast(@alignCast(target.value)); + + // Convert Object arguments to c.id by extracting their .value field + const converted_args = comptime blk: { + const argsInfo = @typeInfo(@TypeOf(args)).@"struct"; + var result_types: [argsInfo.fields.len]type = undefined; + for (argsInfo.fields, 0..) |field, i| { + result_types[i] = if (field.type == objc.Object) c.id else field.type; + } + break :blk result_types; + }; + + const final_args: std.meta.Tuple(&converted_args) = blk: { + const argsInfo = @typeInfo(@TypeOf(args)).@"struct"; + var result: std.meta.Tuple(&converted_args) = undefined; + inline for (argsInfo.fields, 0..) |field, i| { + result[i] = if (field.type == objc.Object) + @field(args, field.name).value + else + @field(args, field.name); + } + break :blk result; + }; + + const result = @call(.auto, msg_send_ptr, .{ target_id, sel.value } ++ final_args); if (!is_object) return result; return .{ .value = result }; @@ -63,11 +87,36 @@ pub fn MsgSend(comptime T: type) type { const Fn = MsgSendFn(RealReturn, *c.objc_super, @TypeOf(args)); const msg_send_fn = comptime msgSendPtr(RealReturn, true); const msg_send_ptr: *const Fn = @ptrCast(msg_send_fn); + const target_id: c.id = @ptrCast(@alignCast(target.value)); + const superclass_id: c.Class = @ptrCast(@alignCast(superclass.value)); var super: c.objc_super = .{ - .receiver = target.value, - .class = superclass.value, + .receiver = target_id, + .super_class = superclass_id, }; - const result = @call(.auto, msg_send_ptr, .{ &super, sel.value } ++ args); + + // Convert Object arguments to c.id by extracting their .value field + const converted_args = comptime blk: { + const argsInfo = @typeInfo(@TypeOf(args)).@"struct"; + var result_types: [argsInfo.fields.len]type = undefined; + for (argsInfo.fields, 0..) |field, i| { + result_types[i] = if (field.type == objc.Object) c.id else field.type; + } + break :blk result_types; + }; + + const final_args: std.meta.Tuple(&converted_args) = blk: { + const argsInfo = @typeInfo(@TypeOf(args)).@"struct"; + var result: std.meta.Tuple(&converted_args) = undefined; + inline for (argsInfo.fields, 0..) |field, i| { + result[i] = if (field.type == objc.Object) + @field(args, field.name).value + else + @field(args, field.name); + } + break :blk result; + }; + + const result = @call(.auto, msg_send_ptr, .{ &super, sel.value } ++ final_args); if (!is_object) return result; return .{ .value = result }; @@ -183,34 +232,42 @@ fn MsgSendFn( // Build up our argument types. const Fn = std.builtin.Type.Fn; - const params: []Fn.Param = params: { - var acc: [argsInfo.fields.len + 2]Fn.Param = undefined; + const params = params: { + var acc: [argsInfo.fields.len + 2]Fn.Param.Attributes = undefined; - // First argument is always the target and selector. - acc[0] = .{ .type = Target, .is_generic = false, .is_noalias = false }; - acc[1] = .{ .type = c.SEL, .is_generic = false, .is_noalias = false }; + acc[0] = .{ .@"noalias" = false }; + acc[1] = .{ .@"noalias" = false }; - // Remaining arguments depend on the args given, in the order given - for (argsInfo.fields, 0..) |field, i| { - acc[i + 2] = .{ - .type = field.type, - .is_generic = false, - .is_noalias = false, - }; + for (argsInfo.fields, 0..) |_, i| { + acc[i + 2] = .{ .@"noalias" = false }; } break :params &acc; }; - return @Type(.{ - .@"fn" = .{ - .calling_convention = .c, - .is_generic = false, - .is_var_args = false, - .return_type = Return, - .params = params, - }, - }); + const types = types: { + var acc: [argsInfo.fields.len + 2]type = undefined; + + // For msgSendSuper, Target is *c.objc_super (a pointer), use it as-is. + // For regular msgSend, Target might be a non-pointer type, so use c.id. + acc[0] = if (@typeInfo(Target) == .pointer) Target else c.id; + acc[1] = c.SEL; + + for (argsInfo.fields, 0..) |field, i| { + // If an argument is objc.Object, use c.id instead since Object + // cannot be passed with C calling convention. + acc[i + 2] = if (field.type == objc.Object) c.id else field.type; + } + + break :types &acc; + }; + + return @Fn( + types, + params, + Return, + .{ .@"callconv" = .c, .varargs = false }, + ); } test { diff --git a/src/object.zig b/src/object.zig index aaa762d..09e3198 100644 --- a/src/object.zig +++ b/src/object.zig @@ -44,7 +44,9 @@ pub const Object = struct { /// Returns the class name of a given object. pub fn getClassName(self: Object) [:0]const u8 { - return std.mem.sliceTo(c.object_getClassName(self.value), 0); + const ptr = c.object_getClassName(self.value); + const len = std.mem.indexOfSentinel(u8, 0, ptr); + return ptr[0..len :0]; } /// Set a property. This is a helper around getProperty and is diff --git a/src/property.zig b/src/property.zig index e042306..563f1dd 100644 --- a/src/property.zig +++ b/src/property.zig @@ -7,15 +7,16 @@ pub const Property = extern struct { /// Returns the name of a property. pub fn getName(self: Property) [:0]const u8 { - return std.mem.sliceTo(c.property_getName(self.value), 0); + const ptr = c.property_getName(self.value); + const len = std.mem.indexOfSentinel(u8, 0, ptr); + return ptr[0..len :0]; } /// Returns the value of a property attribute given the attribute name. pub fn copyAttributeValue(self: Property, attr: [:0]const u8) ?[:0]u8 { - return std.mem.sliceTo( - c.property_copyAttributeValue(self.value, attr.ptr) orelse return null, - 0, - ); + const ptr = c.property_copyAttributeValue(self.value, attr.ptr) orelse return null; + const len = std.mem.indexOfSentinel(u8, 0, ptr); + return ptr[0..len :0]; } comptime { diff --git a/src/protocol.zig b/src/protocol.zig index 24b64b0..d218bc3 100644 --- a/src/protocol.zig +++ b/src/protocol.zig @@ -17,7 +17,9 @@ pub const Protocol = extern struct { } pub fn getName(self: Protocol) [:0]const u8 { - return std.mem.sliceTo(c.protocol_getName(self.value), 0); + const ptr = c.protocol_getName(self.value); + const len = std.mem.indexOfSentinel(u8, 0, ptr); + return ptr[0..len :0]; } pub fn getProperty( diff --git a/src/sel.zig b/src/sel.zig index 8036961..e7bd025 100644 --- a/src/sel.zig +++ b/src/sel.zig @@ -19,7 +19,9 @@ pub const Sel = struct { /// Returns the name of the method specified by a given selector. pub fn getName(self: Sel) [:0]const u8 { - return std.mem.sliceTo(c.sel_getName(self.value), 0); + const ptr = c.sel_getName(self.value); + const len = std.mem.indexOfSentinel(u8, 0, ptr); + return ptr[0..len :0]; } };