From 26d3b94a3b6f573cedc1533ecd70be0f4a3b7681 Mon Sep 17 00:00:00 2001 From: namedkitten Date: Thu, 17 Sep 2020 18:21:24 +0100 Subject: [PATCH] h --- deps/interfaces/interface.zig | 269 ++++++++++++++++++---------------- deps/time | 2 +- src/types/bar.zig | 2 +- src/types/widget.zig | 2 +- 4 files changed, 148 insertions(+), 127 deletions(-) diff --git a/deps/interfaces/interface.zig b/deps/interfaces/interface.zig index 94d722b..29327cd 100644 --- a/deps/interfaces/interface.zig +++ b/deps/interfaces/interface.zig @@ -6,7 +6,7 @@ const assert = std.debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -pub const SelfType = @OpaqueType(); +pub const SelfType = @Type(.Opaque); fn makeSelfPtr(ptr: anytype) *SelfType { if (comptime !trait.isSingleItemPtr(@TypeOf(ptr))) { @@ -43,16 +43,21 @@ pub const Storage = struct { erased_ptr: *SelfType, ImplType: type, - pub fn init(args: anytype) !Comptime { - if (args.len != 1) { - @compileError("Comptime storage expected a 1-tuple in initialization."); - } + fn makeInit(comptime TInterface: type) type { + return struct { + fn init(obj: anytype) !TInterface { + const ImplType = PtrChildOrSelf(@TypeOf(obj)); - var obj = args[0]; + comptime var obj_holder = obj; - return Comptime{ - .erased_ptr = makeSelfPtr(&obj), - .ImplType = @TypeOf(args[0]), + return TInterface{ + .vtable_ptr = &comptime makeVTable(TInterface.VTable, ImplType), + .storage = Comptime{ + .erased_ptr = makeSelfPtr(&obj_holder), + .ImplType = @TypeOf(obj), + }, + }; + } }; } @@ -66,13 +71,16 @@ pub const Storage = struct { pub const NonOwning = struct { erased_ptr: *SelfType, - pub fn init(args: anytype) !NonOwning { - if (args.len != 1) { - @compileError("NonOwning storage expected a 1-tuple in initialization."); - } - - return NonOwning{ - .erased_ptr = makeSelfPtr(args[0]), + fn makeInit(comptime TInterface: type) type { + return struct { + fn init(ptr: anytype) !TInterface { + return TInterface{ + .vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(@TypeOf(ptr))), + .storage = NonOwning{ + .erased_ptr = makeSelfPtr(ptr), + }, + }; + } }; } @@ -87,19 +95,22 @@ pub const Storage = struct { allocator: *mem.Allocator, mem: []u8, - pub fn init(args: anytype) !Owning { - if (args.len != 2) { - @compileError("Owning storage expected a 2-tuple in initialization."); - } + fn makeInit(comptime TInterface: type) type { + return struct { + fn init(obj: anytype, allocator: *std.mem.Allocator) !TInterface { + const AllocT = @TypeOf(obj); - const AllocT = @TypeOf(args[0]); + var ptr = try allocator.create(AllocT); + ptr.* = obj; - var obj = try args[1].create(AllocT); - obj.* = args[0]; - - return Owning{ - .allocator = args[1], - .mem = std.mem.asBytes(obj)[0..], + return TInterface{ + .vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(AllocT)), + .storage = Owning{ + .allocator = allocator, + .mem = std.mem.asBytes(ptr)[0..], + }, + }; + } }; } @@ -108,7 +119,7 @@ pub const Storage = struct { } pub fn deinit(self: Owning) void { - const result = self.allocator.shrinkBytes(self.mem, 0, 0); + const result = self.allocator.shrinkBytes(self.mem, 0, 0, 0, 0); assert(result == 0); } }; @@ -119,23 +130,28 @@ pub const Storage = struct { mem: [size]u8, - pub fn init(args: anytype) !Self { - if (args.len != 1) { - @compileError("Inline storage expected a 1-tuple in initialization."); - } + fn makeInit(comptime TInterface: type) type { + return struct { + fn init(value: anytype) !TInterface { + const ImplSize = @sizeOf(@TypeOf(value)); - const ImplSize = @sizeOf(@TypeOf(args[0])); + if (ImplSize > size) { + @compileError("Type does not fit in inline storage."); + } - if (ImplSize > size) { - @compileError("Type does not fit in inline storage."); - } + var self = Self{ + .mem = undefined, + }; + if (ImplSize > 0) { + std.mem.copy(u8, self.mem[0..], @ptrCast([*]const u8, &args[0])[0..ImplSize]); + } - var self: Self = undefined; - - if (ImplSize > 0) { - std.mem.copy(u8, self.mem[0..], @ptrCast([*]const u8, &args[0])[0..ImplSize]); - } - return self; + return TInterface{ + .vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(@TypeOf(value))), + .storage = self, + }; + } + }; } pub fn getSelfPtr(self: *Self) *SelfType { @@ -242,64 +258,68 @@ fn getFunctionFromImpl(comptime name: []const u8, comptime FnT: type, comptime I const args = @typeInfo(fn_decl.fn_type).Fn.args; if (args.len == 0) { - return null; + return @field(ImplT, name); } - const arg0_type = args[0].arg_type.?; - if (arg0_type != ImplT and arg0_type != *ImplT and arg0_type != *const ImplT) { - return null; + if (args.len > 0) { + const arg0_type = args[0].arg_type.?; + const is_method = arg0_type == ImplT or arg0_type == *ImplT or arg0_type == *const ImplT; + + const candidate_cc = @typeInfo(fn_decl.fn_type).Fn.calling_convention; + switch (candidate_cc) { + .Async, .Unspecified => {}, + else => return null, + } + + const Return = @typeInfo(FnT).Fn.return_type orelse noreturn; + const CurrSelfType = @typeInfo(FnT).Fn.args[0].arg_type.?; + + const call_type: GenCallType = switch (our_cc) { + .Async => if (candidate_cc == .Async) .BothAsync else .AsyncCallsBlocking, + .Unspecified => if (candidate_cc == .Unspecified) .BothBlocking else .BlockingCallsAsync, + else => unreachable, + }; + + if (!is_method) { + return @field(ImplT, name); + } + + // TODO: Make this less hacky somehow? + // We need some new feature to do so unfortunately. + return switch (args.len) { + 1 => struct { + fn impl(self_ptr: CurrSelfType) callconv(our_cc) Return { + return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{} }); + } + }.impl, + 2 => struct { + fn impl(self_ptr: CurrSelfType, arg: args[1].arg_type.?) callconv(our_cc) Return { + return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{arg} }); + } + }.impl, + 3 => struct { + fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?) callconv(our_cc) Return { + return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2 } }); + } + }.impl, + 4 => struct { + fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?) callconv(our_cc) Return { + return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3 } }); + } + }.impl, + 5 => struct { + fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?) callconv(our_cc) Return { + return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4 } }); + } + }.impl, + 6 => struct { + fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?, arg5: args[5].arg_type.?) callconv(our_cc) Return { + return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4, arg5 } }); + } + }.impl, + else => @compileError("Unsupported number of arguments, please provide a manually written vtable."), + }; } - - const candidate_cc = @typeInfo(fn_decl.fn_type).Fn.calling_convention; - switch (candidate_cc) { - .Async, .Unspecified => {}, - else => return null, - } - - const Return = @typeInfo(FnT).Fn.return_type orelse noreturn; - const CurrSelfType = @typeInfo(FnT).Fn.args[0].arg_type.?; - - const call_type: GenCallType = switch (our_cc) { - .Async => if (candidate_cc == .Async) .BothAsync else .AsyncCallsBlocking, - .Unspecified => if (candidate_cc == .Unspecified) .BothBlocking else .BlockingCallsAsync, - else => unreachable, - }; - - // TODO: Make this less hacky somehow? - // We need some new feature to do so unfortunately. - return switch (args.len) { - 1 => struct { - fn impl(self_ptr: CurrSelfType) callconv(our_cc) Return { - return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{} }); - } - }.impl, - 2 => struct { - fn impl(self_ptr: CurrSelfType, arg: args[1].arg_type.?) callconv(our_cc) Return { - return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{arg} }); - } - }.impl, - 3 => struct { - fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?) callconv(our_cc) Return { - return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2 } }); - } - }.impl, - 4 => struct { - fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?) callconv(our_cc) Return { - return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3 } }); - } - }.impl, - 5 => struct { - fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?) callconv(our_cc) Return { - return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4 } }); - } - }.impl, - 6 => struct { - fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?, arg5: args[5].arg_type.?) callconv(our_cc) Return { - return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4, arg5 } }); - } - }.impl, - else => @compileError("Unsupported number of arguments, please provide a manually written vtable."), - }; }, else => return null, } @@ -368,23 +388,19 @@ fn checkVtableType(comptime VTableT: type) void { .Unspecified, .Async => {}, else => @compileError("Virtual function's '" ++ field.name ++ "' calling convention is not default or async."), } - - if (type_info.Fn.args.len == 0) { - @compileError("Virtual function '" ++ field.name ++ "' must have at least one argument."); - } - - const arg_type = type_info.Fn.args[0].arg_type.?; - if (arg_type != *SelfType and arg_type != *const SelfType) { - @compileError("Virtual function's '" ++ field.name ++ "' first argument must be *SelfType or *const SelfType"); - } } } -fn vtableHasMethod(comptime VTableT: type, comptime name: []const u8, is_optional: *bool, is_async: *bool) bool { +fn vtableHasMethod(comptime VTableT: type, comptime name: []const u8, is_optional: *bool, is_async: *bool, is_method: *bool) bool { for (std.meta.fields(VTableT)) |field| { if (std.mem.eql(u8, name, field.name)) { is_optional.* = trait.is(.Optional)(field.field_type); - is_async.* = @typeInfo(if (is_optional.*) std.meta.Child(field.field_type) else field.field_type).Fn.calling_convention == .Async; + const fn_typeinfo = @typeInfo(if (is_optional.*) std.meta.Child(field.field_type) else field.field_type).Fn; + is_async.* = fn_typeinfo.calling_convention == .Async; + is_method.* = fn_typeinfo.args.len > 0 and blk: { + const first_arg_type = fn_typeinfo.args[0].arg_type.?; + break :blk first_arg_type == *SelfType or first_arg_type == *const SelfType; + }; return true; } } @@ -426,27 +442,23 @@ pub fn Interface(comptime VTableT: type, comptime StorageT: type) type { storage: StorageT, const Self = @This(); + const VTable = VTableT; + const Storage = StorageT; - pub fn init(args: anytype) !Self { - const ImplType = PtrChildOrSelf(@TypeOf(args.@"0")); - - return Self{ - .vtable_ptr = &comptime makeVTable(VTableT, ImplType), - .storage = try StorageT.init(args), - }; - } + pub const init = StorageT.makeInit(Self).init; pub fn initWithVTable(vtable_ptr: *const VTableT, args: anytype) !Self { return .{ .vtable_ptr = vtable_ptr, - .storage = try StorageT.init(args), + .storage = try init(args), }; } pub fn call(self: anytype, comptime name: []const u8, args: anytype) VTableReturnType(VTableT, name) { comptime var is_optional = true; comptime var is_async = true; - comptime assert(vtableHasMethod(VTableT, name, &is_optional, &is_async)); + comptime var is_method = true; + comptime assert(vtableHasMethod(VTableT, name, &is_optional, &is_async, &is_method)); const fn_ptr = if (is_optional) blk: { const val = @field(self.vtable_ptr, name); @@ -454,14 +466,23 @@ pub fn Interface(comptime VTableT: type, comptime StorageT: type) type { return null; } else @field(self.vtable_ptr, name); - const self_ptr = self.storage.getSelfPtr(); - const new_args = .{self_ptr}; + if (is_method) { + const self_ptr = self.storage.getSelfPtr(); + const new_args = .{self_ptr}; - if (!is_async) { - return @call(.{}, fn_ptr, new_args ++ args); + if (!is_async) { + return @call(.{}, fn_ptr, new_args ++ args); + } else { + var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; + return await @asyncCall(&stack_frame, {}, fn_ptr, new_args ++ args); + } } else { - var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; - return await @asyncCall(&stack_frame, {}, fn_ptr, new_args ++ args); + if (!is_async) { + return @call(.{}, fn_ptr, args); + } else { + var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; + return await @asyncCall(&stack_frame, {}, fn_ptr, args); + } } } diff --git a/deps/time b/deps/time index 04d7cb5..6b5387f 160000 --- a/deps/time +++ b/deps/time @@ -1 +1 @@ -Subproject commit 04d7cb5a54fbbc0399fd9299b71f461b5bf4ae2c +Subproject commit 6b5387fcd3007cf01c08b4f5136d24a6562be413 diff --git a/src/types/bar.zig b/src/types/bar.zig index 7ebfafb..4f6d8fd 100644 --- a/src/types/bar.zig +++ b/src/types/bar.zig @@ -11,7 +11,7 @@ pub const Bar = struct { }, interface.Storage.NonOwning); iface: IFace, pub fn init(impl_ptr: anytype) Bar { - return .{ .iface = try IFace.init(.{impl_ptr}) }; + return .{ .iface = try IFace.init(impl_ptr) }; } pub fn keep_running(self: *Bar) bool { return self.iface.call("keep_running", .{}); diff --git a/src/types/widget.zig b/src/types/widget.zig index 9e977e0..88b7152 100644 --- a/src/types/widget.zig +++ b/src/types/widget.zig @@ -14,7 +14,7 @@ pub const Widget = struct { }, interface.Storage.NonOwning); iface: IFace, pub fn init(impl_ptr: anytype) Widget { - return .{ .iface = try IFace.init(.{impl_ptr}) }; + return .{ .iface = try IFace.init(impl_ptr) }; } pub fn name(self: *Widget) []const u8 { return self.iface.call("name", .{});