diff --git a/src/builtins/builtins.zig b/src/builtins/builtins.zig index 8ca941c27..91a5abf01 100644 --- a/src/builtins/builtins.zig +++ b/src/builtins/builtins.zig @@ -30,6 +30,7 @@ pub const CoreData = struct { const func = cy.hostFuncEntry; const funcs = [_]C.HostFuncEntry{ // Utils. + func("allTypes", zErrFunc(allTypes)), func("bitcast_", zErrFunc(bitcast)), func("copy_", zErrFunc(copy)), func("choicetag_", zErrFunc(choicetag)), @@ -50,6 +51,7 @@ const funcs = [_]C.HostFuncEntry{ func("queueTask", zErrFunc(queueTask)), func("runestr", zErrFunc(runestr)), func("sizeof_", sizeof), + func("typeInfo", zErrFunc(typeInfo)), // Compile-time funcs. @@ -796,6 +798,260 @@ pub fn sizeof(vm: *cy.VM) Value { } } +pub fn allTypes(ivm: *cy.VM) !Value { + const vm: *C.ZVM = @ptrCast(ivm); + + const list_tmpl = ivm.sema.list_tmpl.head.toC(); + const type_v = vm.newType(bt.Type); + defer vm.release(type_v); + var list_t: cy.TypeId = undefined; + _ = vm.expandTemplateType(list_tmpl, &.{type_v}, &list_t); + + const list_v = vm.newList(list_t, &.{}); + for (1..ivm.sema.types.items.len) |id| { + const type_e = ivm.sema.types.items[id]; + if (type_e.kind == .bare or type_e.kind == .ct_ref) { + continue; + } + const elem = vm.newType(@intCast(id)); + vm.listAppend(list_v, elem); + vm.release(elem); + } + return @bitCast(list_v); +} + +pub fn typeInfo(ivm: *cy.VM) !Value { + const type_id = ivm.getObject(*cy.heap.Type, 0).type; + + const vm: *C.ZVM = @ptrCast(ivm); + const info_t = vm.findType("TypeInfo"); + switch (type_id) { + bt.Void => { + return @bitCast(vm.newChoice(info_t, "void_t", C.Void)); + }, + bt.Type => { + return @bitCast(vm.newChoice(info_t, "type_t", C.Void)); + }, + bt.Error => { + return @bitCast(vm.newChoice(info_t, "error_t", C.Void)); + }, + else => { + const type_e = ivm.sema.getType(type_id); + switch (type_e.kind) { + .float => { + const bits = type_e.data.float.bits; + const float_info_t = vm.findType("FloatInfo"); + const float_info = vm.newInstance(float_info_t, &.{ + C.toFieldInit("bits", bits), + }); + return @bitCast(vm.newChoice(info_t, "float_t", float_info)); + }, + .int => { + if (type_e.sym.getVariant()) |variant| { + if (variant.data.sym.template == ivm.sema.pointer_tmpl) { + const ptr_info_t = vm.findType("PointerInfo"); + const elem_t = type_e.sym.getVariant().?.args[0]; + vm.retain(@bitCast(elem_t)); + const ptr_info = vm.newInstance(ptr_info_t, &.{ + C.toFieldInit("elem", @bitCast(elem_t)), + }); + return @bitCast(vm.newChoice(info_t, "ptr_t", ptr_info)); + } + } + const bits = type_e.data.int.bits; + const int_info_t = vm.findType("IntInfo"); + const int_info = vm.newInstance(int_info_t, &.{ + C.toFieldInit("sign", C.bool_(type_id != bt.Byte)), + C.toFieldInit("bits", bits), + }); + return @bitCast(vm.newChoice(info_t, "int_t", int_info)); + }, + .bool => { + return @bitCast(vm.newChoice(info_t, "bool_t", C.Void)); + }, + .trait => { + const name = type_e.sym.name(); + const trait_info_t = vm.findType("TraitInfo"); + const trait_info = vm.newInstance(trait_info_t, &.{ + C.toFieldInit("name", vm.newString(name)), + }); + return @bitCast(vm.newChoice(info_t, "trait_t", trait_info)); + }, + .array => { + const n = type_e.data.array.n; + const elem_t = type_e.data.array.elem_t; + const array_info_t = vm.findType("ArrayInfo"); + const array_info = vm.newInstance(array_info_t, &.{ + C.toFieldInit("len", C.int(@intCast(n))), + C.toFieldInit("elem", vm.newType(elem_t)), + }); + return @bitCast(vm.newChoice(info_t, "array_t", array_info)); + }, + .option => { + const opt_info_t = vm.findType("OptionInfo"); + const elem_t = type_e.sym.getVariant().?.args[0]; + vm.retain(@bitCast(elem_t)); + const opt_info = vm.newInstance(opt_info_t, &.{ + C.toFieldInit("elem", @bitCast(elem_t)), + }); + return @bitCast(vm.newChoice(info_t, "opt_t", opt_info)); + }, + .choice => { + const choice_info_t = vm.findType("ChoiceInfo"); + const list_tmpl = ivm.sema.list_tmpl.head.toC(); + const choice_case_t = vm.findType("ChoiceCase"); + const choice_case_tv = vm.newType(choice_case_t); + defer vm.release(choice_case_tv); + var list_t: cy.TypeId = undefined; + _ = vm.expandTemplateType(list_tmpl, &.{choice_case_tv}, &list_t); + const list_v = vm.newList(list_t, &.{}); + for (type_e.sym.cast(.enum_t).members()) |member| { + const case = vm.newInstance(choice_case_t, &.{ + C.toFieldInit("name", vm.newString(member.head.name())), + C.toFieldInit("type", vm.newType(member.payloadType)), + }); + vm.listAppend(list_v, case); + vm.release(case); + } + const name = type_e.sym.name(); + const namev = try StringSome(ivm, @bitCast(vm.newString(name))); + const choice_info = vm.newInstance(choice_info_t, &.{ + C.toFieldInit("name", @bitCast(namev)), + C.toFieldInit("cases", list_v), + }); + return @bitCast(vm.newChoice(info_t, "choice_t", choice_info)); + }, + .enum_t => { + const enum_info_t = vm.findType("EnumInfo"); + const list_tmpl = ivm.sema.list_tmpl.head.toC(); + const enum_case_t = vm.findType("EnumCase"); + const enum_case_tv = vm.newType(enum_case_t); + defer vm.release(enum_case_tv); + var list_t: cy.TypeId = undefined; + _ = vm.expandTemplateType(list_tmpl, &.{enum_case_tv}, &list_t); + const list_v = vm.newList(list_t, &.{}); + for (type_e.sym.cast(.enum_t).members()) |member| { + const case = vm.newInstance(enum_case_t, &.{ + C.toFieldInit("name", vm.newString(member.head.name())), + }); + vm.listAppend(list_v, case); + vm.release(case); + } + const name = type_e.sym.name(); + const namev = try StringSome(ivm, @bitCast(vm.newString(name))); + const enum_info = vm.newInstance(enum_info_t, &.{ + C.toFieldInit("name", @bitCast(namev)), + C.toFieldInit("cases", list_v), + }); + return @bitCast(vm.newChoice(info_t, "enum_t", enum_info)); + }, + .host_object => { + const hostobj_info_t = vm.findType("HostObjectInfo"); + const name = type_e.sym.name(); + const hostobj_info = vm.newInstance(hostobj_info_t, &.{ + C.toFieldInit("name", vm.newString(name)), + }); + return @bitCast(vm.newChoice(info_t, "hostobj_t", hostobj_info)); + }, + .object => { + const object_info_t = vm.findType("ObjectInfo"); + const list_tmpl = ivm.sema.list_tmpl.head.toC(); + const field_t = vm.findType("ObjectField"); + const field_tv = vm.newType(field_t); + defer vm.release(field_tv); + var list_t: cy.TypeId = undefined; + _ = vm.expandTemplateType(list_tmpl, &.{field_tv}, &list_t); + const list_v = vm.newList(list_t, &.{}); + for (type_e.sym.cast(.object_t).getFields()) |field| { + const f = vm.newInstance(field_t, &.{ + C.toFieldInit("name", vm.newString(field.sym.head.name())), + C.toFieldInit("type", vm.newType(field.type)), + }); + vm.listAppend(list_v, f); + vm.release(f); + } + const name = type_e.sym.name(); + const namev = try StringSome(ivm, @bitCast(vm.newString(name))); + const object_info = vm.newInstance(object_info_t, &.{ + C.toFieldInit("name", @bitCast(namev)), + C.toFieldInit("fields", list_v), + }); + return @bitCast(vm.newChoice(info_t, "object_t", object_info)); + }, + .struct_t => { + const struct_info_t = vm.findType("StructInfo"); + const list_tmpl = ivm.sema.list_tmpl.head.toC(); + const field_t = vm.findType("StructField"); + const field_tv = vm.newType(field_t); + defer vm.release(field_tv); + var list_t: cy.TypeId = undefined; + _ = vm.expandTemplateType(list_tmpl, &.{field_tv}, &list_t); + const list_v = vm.newList(list_t, &.{}); + for (type_e.sym.cast(.struct_t).getFields()) |field| { + const f = vm.newInstance(field_t, &.{ + C.toFieldInit("name", vm.newString(field.sym.head.name())), + C.toFieldInit("type", vm.newType(field.type)), + C.toFieldInit("offset", C.int(@intCast(field.offset))), + }); + vm.listAppend(list_v, f); + vm.release(f); + } + const name = type_e.sym.name(); + const namev = try StringSome(ivm, @bitCast(vm.newString(name))); + const struct_info = vm.newInstance(struct_info_t, &.{ + C.toFieldInit("name", @bitCast(namev)), + C.toFieldInit("fields", list_v), + }); + return @bitCast(vm.newChoice(info_t, "struct_t", struct_info)); + }, + .func_ptr => { + const func_info = newFuncInfo(ivm, 0, type_e); + return @bitCast(vm.newChoice(info_t, "func_t", func_info)); + }, + .func_union => { + const func_info = newFuncInfo(ivm, 1, type_e); + return @bitCast(vm.newChoice(info_t, "func_t", func_info)); + }, + .func_sym => { + const func_info = newFuncInfo(ivm, 2, type_e); + return @bitCast(vm.newChoice(info_t, "func_t", func_info)); + }, + else => { + std.debug.panic("Unsupported: {}", .{type_e.kind}); + } + } + }, + } +} + +fn newFuncInfo(ivm: *cy.VM, kind: u8, type_e: cy.types.Type) C.Value { + const vm: *C.ZVM = @ptrCast(ivm); + const func_info_t = vm.findType("FuncInfo"); + const list_tmpl = ivm.sema.list_tmpl.head.toC(); + const param_t = vm.findType("FuncParam"); + const param_tv = vm.newType(param_t); + defer vm.release(param_tv); + var list_t: cy.TypeId = undefined; + _ = vm.expandTemplateType(list_tmpl, &.{param_tv}, &list_t); + const list_v = vm.newList(list_t, &.{}); + const sig = ivm.sema.getFuncSig(type_e.data.func_ptr.sig); + for (sig.params()) |param| { + const p = vm.newInstance(param_t, &.{ + C.toFieldInit("type", vm.newType(param.type)), + }); + vm.listAppend(list_v, p); + vm.release(p); + } + + const func_kind_t = vm.findType("FuncKind"); + const kind_v = cy.Value.initEnum(@intCast(func_kind_t), kind); + return vm.newInstance(func_info_t, &.{ + C.toFieldInit("kind", @bitCast(kind_v)), + C.toFieldInit("ret", vm.newType(sig.ret)), + C.toFieldInit("params", list_v), + }); +} + pub fn type_call(vm: *cy.VM) !Value { const val = vm.getValue(0); const typeId = val.getTypeId(); diff --git a/src/builtins/builtins_vm.cy b/src/builtins/builtins_vm.cy index 6d01e6012..a23680466 100644 --- a/src/builtins/builtins_vm.cy +++ b/src/builtins/builtins_vm.cy @@ -2,6 +2,9 @@ --| Functions. --| +--| Returns all the current registered types when this function is invoked. +@host func allTypes() List[type] + func bitcast[D, S](D type, val S) D: return bitcast_(D, typeid[D], val, typeid[S]) @@ -79,6 +82,9 @@ func sizeof[T](T type) int: func typeOf(t ExprType) type: return t.getType() +--| Returns info about a type. +@host func typeInfo(T type) TypeInfo + --| --| Value templates. --| @@ -207,8 +213,100 @@ type float #float64_t: @host type any _ @host type type _: + --| Returns a unique ID for this type. @host func id(self) int +type TypeInfo enum: + case int_t IntInfo + case array_t ArrayInfo + case bool_t void + case choice_t ChoiceInfo + case enum_t EnumInfo + case error_t void + case float_t FloatInfo + case func_t FuncInfo + case object_t ObjectInfo + case hostobj_t HostObjectInfo + case opt_t OptionInfo + case ptr_t PointerInfo + case struct_t StructInfo + case trait_t TraitInfo + case type_t void + case void_t void + +type IntInfo struct: + sign bool + bits int + +type FloatInfo struct: + bits int + +type HostObjectInfo struct: + name String + -- getchildren + -- finalizer + +type ObjectInfo struct: + name ?String + fields List[ObjectField] + -- funcs List[funcsym_t] + +type ObjectField struct: + name String + type type + +type EnumInfo struct: + name ?String + cases List[EnumCase] + +type EnumCase struct: + name String + +type ChoiceInfo struct: + name ?String + cases List[ChoiceCase] + +type ChoiceCase struct: + name String + type type + +type FuncKind enum: + case ptr + case union + case sym + +type FuncInfo struct: + kind FuncKind + params List[FuncParam] + ret type + +type FuncParam struct: + type type + +type ArrayInfo struct: + len int + elem type + +type StructInfo struct: + name ?String + fields List[StructField] + -- funcs List[funcsym_t] + +type StructField struct: + name String + type type + offset int + +type TraitInfo struct: + name String + -- funcs List[funcsym_t] + +type PointerInfo struct: + elem type + +type OptionInfo struct: + elem type + --| Return the type of a value. --| See `typeof` to obtain the type of an expression at compile-time. @host func type.$call(val any) type @@ -730,6 +828,7 @@ func Memory.alloc[T](self, T type, n int) [*]T: func Memory.free[T](self, ptr *T): self.iface.free(ptrcast(byte, ptr)[0..sizeof(T)]) +-- TODO: Should be merged with Memory.free. func Memory.free_[T](self, slice [*]T): var bytes = ptrcast(byte, slice.ptr)[0..sizeof(T)*slice.len()] self.iface.free(bytes) diff --git a/src/capi.zig b/src/capi.zig index a463c0243..b1b26c4d8 100644 --- a/src/capi.zig +++ b/src/capi.zig @@ -366,6 +366,14 @@ pub const ZVM = struct { return c.clNewChoice(@ptrCast(self), choice_t, toStr(name), val); } + pub fn newList(self: *ZVM, list_t: Type, vals: []const Value) Value { + return c.clNewList(@ptrCast(self), list_t, vals.ptr, vals.len); + } + + pub fn listAppend(self: *ZVM, list: Value, val: Value) void { + c.clListAppend(@ptrCast(self), list, val); + } + pub fn findType(self: *ZVM, path: []const u8) Type { return c.clFindType(@ptrCast(self), toStr(path)); } diff --git a/src/lib.zig b/src/lib.zig index 4be0a1014..bcfe0d478 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -620,6 +620,14 @@ export fn clNewListDyn(vm: *cy.VM, ptr: [*]const Value, len: usize) Value { return cy.heap.allocListDyn(vm, elems) catch fatal(); } +export fn clNewList(vm: *cy.VM, list_t: cy.TypeId, ptr: [*]const Value, len: usize) Value { + const elems = ptr[0..len]; + for (elems) |elem| { + cy.arc.retain(vm, elem); + } + return cy.heap.allocList(vm, list_t, elems) catch fatal(); +} + export fn clNewEmptyMap(vm: *cy.VM) Value { return cy.heap.allocEmptyMap(vm) catch fatal(); } diff --git a/src/sema.zig b/src/sema.zig index 62ad179a3..7d6b62188 100644 --- a/src/sema.zig +++ b/src/sema.zig @@ -1685,15 +1685,15 @@ pub fn declareEnumMembers(c: *cy.Chunk, sym: *cy.sym.EnumType, decl: *ast.EnumDe const members = try c.alloc.alloc(*cy.sym.EnumMember, decl.members.len); for (decl.members, 0..) |member, i| { const mName = c.ast.nodeString(member.name); - var payloadType: cy.TypeId = cy.NullId; + var payloadType: cy.TypeId = bt.Void; if (sym.isChoiceType and member.typeSpec != null) { payloadType = try resolveTypeSpecNode(c, member.typeSpec); } const modSymId = try c.declareEnumMember(@ptrCast(sym), mName, sym.type, sym.isChoiceType, @intCast(i), payloadType, member); members[i] = modSymId; } - sym.members = members.ptr; - sym.numMembers = @intCast(members.len); + sym.member_ptr = members.ptr; + sym.member_len = @intCast(members.len); } /// Only allows binding a predefined host type id (BIND_TYPE_DECL). @@ -3286,6 +3286,7 @@ pub fn symbol(c: *cy.Chunk, sym: *Sym, expr: Expr, prefer_ct_sym: bool) !ExprRes }, .type, .enum_t, + .trait_t, .hostobj_t, .object_t, .struct_t => { @@ -3302,12 +3303,8 @@ pub fn symbol(c: *cy.Chunk, sym: *Sym, expr: Expr, prefer_ct_sym: bool) !ExprRes if (member.is_choice_type) { return semaInitChoiceNoPayload(c, member, node); } else { - var typeId = member.payloadType; - if (typeId == cy.NullId) { - typeId = member.type; - } - const ctype = CompactType.init(typeId); - const irIdx = try c.ir.pushExpr(.enumMemberSym, c.alloc, typeId, node, .{ + const ctype = CompactType.init(member.type); + const irIdx = try c.ir.pushExpr(.enumMemberSym, c.alloc, member.type, node, .{ .type = member.type, .val = @as(u8, @intCast(member.val)), }); diff --git a/src/std/test.zig b/src/std/test.zig index 70d9042f4..04a39cd19 100644 --- a/src/std/test.zig +++ b/src/std/test.zig @@ -198,7 +198,7 @@ fn eq2(c: cy.Context, type_id: cy.TypeId, act: rt.Any, exp: rt.Any) bool { bt.Type => { const actv = act.asHeapObject().type; const expv = exp.asHeapObject().type; - if (std.meta.eql(actv, expv)) { + if (actv.type == expv.type) { return true; } else { const act_name = c.sema.allocTypeName(actv.type) catch @panic(""); diff --git a/src/sym.zig b/src/sym.zig index 918261923..58fb2be66 100644 --- a/src/sym.zig +++ b/src/sym.zig @@ -144,7 +144,7 @@ pub const Sym = extern struct { .enum_t => { const enumType = self.cast(.enum_t); enumType.deinit(alloc); - alloc.free(enumType.members[0..enumType.numMembers]); + alloc.free(enumType.members()); alloc.destroy(enumType); }, .func_template => { @@ -1019,8 +1019,8 @@ pub const EnumType = extern struct { head: Sym, decl: *ast.EnumDecl, type: cy.TypeId, - members: [*]*EnumMember, - numMembers: u32, + member_ptr: [*]*EnumMember, + member_len: u32, mod: vmc.Module, variant: ?*Variant, @@ -1029,17 +1029,17 @@ pub const EnumType = extern struct { pub fn deinit(self: *EnumType, alloc: std.mem.Allocator) void { self.getMod().deinit(alloc); - for (self.members[0..self.numMembers]) |m| { + for (self.members()) |m| { alloc.destroy(m); } } pub fn isResolved(self: *EnumType) bool { - return self.numMembers != 0; + return self.member_len != 0; } pub fn getValueSym(self: *EnumType, val: u16) *EnumMember { - return self.members[val]; + return self.member_ptr[val]; } pub fn getMod(self: *EnumType) *cy.Module { @@ -1047,7 +1047,11 @@ pub const EnumType = extern struct { } pub fn getMemberByIdx(self: *EnumType, idx: u32) *EnumMember { - return self.members[idx]; + return self.member_ptr[idx]; + } + + pub fn members(self: *EnumType) []*EnumMember { + return self.member_ptr[0..self.member_len]; } pub fn getMember(self: *EnumType, name: []const u8) ?*EnumMember { @@ -1718,8 +1722,8 @@ pub const ChunkExt = struct { .head = Sym.init(.enum_t, parent, name), .type = typeId, .decl = decl, - .members = undefined, - .numMembers = 0, + .member_ptr = undefined, + .member_len = 0, .isChoiceType = isChoiceType, .variant = null, .mod = undefined, diff --git a/test/meta/type.cy b/test/meta/type.cy index 3c43b0573..fdc2629ca 100644 --- a/test/meta/type.cy +++ b/test/meta/type.cy @@ -18,6 +18,94 @@ t.eq((error).id(), 3) t.eq((Fiber).id(), 23) t.eq((type).id(), 11) +-- typeInfo() +t.assert(typeInfo(int).!int_t == IntInfo{ + sign = true, + bits = 64, +}) +t.assert(typeInfo(byte).!int_t == IntInfo{ + sign = false, + bits = 8, +}) +t.assert(typeInfo(float).!float_t == FloatInfo{ + bits = 64, +}) +t.eq(typeInfo(bool).!bool_t, _) +t.eq(typeInfo(void).!void_t, _) +t.eq(typeInfo(type).!type_t, _) +t.eq(typeInfo(error).!error_t, _) +type Area trait: + func area(self) float +t.assert(typeInfo(Area).!trait_t == TraitInfo{ + name = 'Area', +}) +t.assert(typeInfo([10]int).!array_t == ArrayInfo{ + len = 10, + elem = int, +}) +t.assert(typeInfo(?int).!opt_t == OptionInfo{ + elem = int +}) +t.assert(typeInfo(*int).!ptr_t == PointerInfo{ + elem = int +}) +type Shape enum: + case rectangle Rectangle + case line float + case point +type Rectangle: + width float + height float +var choice_t = typeInfo(Shape).!choice_t +t.eq(choice_t.name.?, 'Shape') +t.eq(choice_t.cases.len(), 3) +t.eq(choice_t.cases[0].name, 'rectangle') +t.eq(choice_t.cases[0].type, Rectangle) +t.eq(choice_t.cases[1].name, 'line') +t.eq(choice_t.cases[1].type, float) +t.eq(choice_t.cases[2].name, 'point') +t.eq(choice_t.cases[2].type, void) +var enum_t = typeInfo(Shape.Tag).!enum_t +t.eq(enum_t.name.?, 'Tag') +t.eq(enum_t.cases.len(), 3) +t.eq(enum_t.cases[0].name, 'rectangle') +t.eq(enum_t.cases[1].name, 'line') +t.eq(enum_t.cases[2].name, 'point') +type O: + a int + b float +var object_t = typeInfo(O).!object_t +t.eq(object_t.name.?, 'O') +t.eq(object_t.fields.len(), 2) +t.eq(object_t.fields[0].name, 'a') +t.eq(object_t.fields[0].type, int) +t.eq(object_t.fields[1].name, 'b') +t.eq(object_t.fields[1].type, float) +type S struct: + a int + b float +var struct_t = typeInfo(S).!struct_t +t.eq(struct_t.name.?, 'S') +t.eq(struct_t.fields.len(), 2) +t.eq(struct_t.fields[0].name, 'a') +t.eq(struct_t.fields[0].type, int) +t.eq(struct_t.fields[1].name, 'b') +t.eq(struct_t.fields[1].type, float) +type FnPtr -> func(int, float) String +var func_t = typeInfo(FnPtr).!func_t +t.eq(func_t.kind, .ptr) +t.eq(func_t.ret, String) +t.eq(func_t.params.len(), 2) +t.eq(func_t.params[0].type, int) +t.eq(func_t.params[1].type, float) +type FnUnion -> Func(int, float) String +func_t = typeInfo(FnUnion).!func_t +t.eq(func_t.kind, .union) +t.eq(func_t.ret, String) +t.eq(func_t.params.len(), 2) +t.eq(func_t.params[0].type, int) +t.eq(func_t.params[1].type, float) + -- Referencing type name path. use os t.eq(type(os.CArray), type) diff --git a/test/modules/core.cy b/test/modules/core.cy index fc5cb7321..4a439875e 100644 --- a/test/modules/core.cy +++ b/test/modules/core.cy @@ -138,4 +138,6 @@ type Foo: var foo = Foo{a=123} t.eq(typeOf(foo), Foo) +-- typeInfo(), see type.cy + --cytest: pass \ No newline at end of file