lib/choir/src/core/operation/registration.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const interfaces = @import("../interfaces/root.zig");
3
4 pub fn Methods(comptime Operation: type) type {
5 return struct {
6 pub fn getRegisteredInfo(self: *Operation) ?*const interfaces.OperationInfo {
7 return self.name.resolveRegisteredInfo(self.context);
8 }
9
10 fn lookupRegisteredInfo(self: *const Operation) ?*const interfaces.OperationInfo {
11 if (self.name.getRegisteredInfo()) |info| {
12 return info;
13 }
14 return self.context.lookupOperation(self.name.name);
15 }
16
17 pub fn getInherentAttributeNames(self: *const Operation) []const []const u8 {
18 const info = lookupRegisteredInfo(self) orelse return &.{};
19 return info.getInherentAttributeNames();
20 }
21
22 pub fn hasInherentAttributeName(self: *const Operation, name: []const u8) bool {
23 const info = lookupRegisteredInfo(self) orelse return false;
24 return info.hasInherentAttributeName(name);
25 }
26
27 pub fn isDiscardableAttrName(self: *const Operation, name: []const u8) bool {
28 return !hasInherentAttributeName(self, name);
29 }
30
31 pub fn isRegistered(self: *Operation) bool {
32 return getRegisteredInfo(self) != null;
33 }
34
35 pub fn getInterface(self: *Operation, comptime IFace: type) ?*const IFace.VTable {
36 _ = self.name.resolveRegisteredInfo(self.context);
37
38 if (self.name.getInterface(IFace)) |vtable| {
39 return vtable;
40 }
41
42 const fallbacks = self.context.dialect_registry.op_interface_fallbacks.lookup(IFace.id) orelse return null;
43
44 const ns = self.name.getDialectNamespace();
45 if (ns.len > 0) {
46 if (fallbacks.get(ns)) |fallback| {
47 if (fallback(self)) |vt_any| {
48 return @ptrCast(@alignCast(vt_any));
49 }
50 }
51 }
52
53 return null;
54 }
55
56 pub fn InterfaceHandle(comptime IFace: type) type {
57 return struct {
58 op: *Operation,
59 vtable: *const IFace.VTable,
60
61 fn returnType(comptime fn_ptr_type: type) type {
62 const ptr_info = @typeInfo(fn_ptr_type);
63 const fn_type = switch (ptr_info) {
64 .pointer => |p| p.child,
65 else => @compileError("expected interface vtable field to be a function pointer"),
66 };
67 const fn_info = switch (@typeInfo(fn_type)) {
68 .@"fn" => |f| f,
69 else => @compileError("expected interface vtable field to be a function pointer"),
70 };
71 return fn_info.return_type orelse @compileError("generic interface vtable methods are not supported");
72 }
73
74 fn methodFnPtrType(comptime method: std.meta.FieldEnum(IFace.VTable)) type {
75 const dummy: IFace.VTable = undefined;
76 return @TypeOf(@field(dummy, @tagName(method)));
77 }
78
79 pub inline fn call(
80 self: @This(),
81 comptime method: std.meta.FieldEnum(IFace.VTable),
82 args: anytype,
83 ) returnType(methodFnPtrType(method)) {
84 const fn_ptr = @field(self.vtable, @tagName(method));
85 const op_any: *const anyopaque = @ptrCast(self.op);
86 return @call(.auto, fn_ptr, .{op_any} ++ args);
87 }
88 };
89 }
90
91 pub fn interface(self: *Operation, comptime IFace: type) ?InterfaceHandle(IFace) {
92 const vtable = getInterface(self, IFace) orelse return null;
93 return .{ .op = self, .vtable = vtable };
94 }
95
96 pub fn hasInterface(self: *Operation, comptime IFace: type) bool {
97 return getInterface(self, IFace) != null;
98 }
99
100 pub fn getTraits(self: *Operation) interfaces.OperationTraits {
101 _ = self.name.resolveRegisteredInfo(self.context);
102 return self.name.getTraits();
103 }
104
105 pub fn hasTrait(self: *Operation, comptime trait_field: []const u8) bool {
106 const traits = getTraits(self);
107 return @field(traits, trait_field);
108 }
109
110 pub fn hasTraitId(self: *Operation, trait_id: interfaces.TraitId) bool {
111 _ = self.name.resolveRegisteredInfo(self.context);
112 return self.name.hasTraitId(trait_id);
113 }
114
115 pub fn hasTraitName(self: *Operation, trait_name: []const u8) bool {
116 return hasTraitId(self, interfaces.traitIdRuntime(trait_name));
117 }
118 };
119 }