diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 25c1152..f90a90e 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -23,7 +23,6 @@ allocator: std.mem.Allocator, ip: InternPool = .{}, document_store: *DocumentStore, uri: DocumentStore.Uri, -decls: std.ArrayListUnmanaged(Decl) = .{}, namespaces: std.MultiArrayList(Namespace) = .{}, /// Interpreter diagnostic errors @@ -69,7 +68,6 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void { interpreter.namespaces.items(.usingnamespaces)[i].deinit(interpreter.allocator); } interpreter.namespaces.deinit(interpreter.allocator); - interpreter.decls.deinit(interpreter.allocator); } pub const Type = struct { @@ -86,29 +84,18 @@ pub const Value = struct { ty: Index, val: Index, }; - -pub const Decl = struct { - name: []const u8, - ty: Index, - val: Index, - alignment: u16, - address_space: std.builtin.AddressSpace, - is_pub: bool, - is_exported: bool, -}; - // pub const Comptimeness = enum { @"comptime", runtime }; -pub const NamespaceIndex = InternPool.NamespaceIndex; - pub const Namespace = struct { /// always points to Namespace or Index.none - parent: NamespaceIndex, + parent: Namespace.Index, node_idx: Ast.Node.Index, /// Will be a struct, enum, union, opaque or .none - ty: Index, - decls: std.StringArrayHashMapUnmanaged(Decl) = .{}, - usingnamespaces: std.ArrayListUnmanaged(NamespaceIndex) = .{}, + ty: InternPool.Index, + decls: std.StringArrayHashMapUnmanaged(InternPool.DeclIndex) = .{}, + usingnamespaces: std.ArrayListUnmanaged(InternPool.DeclIndex) = .{}, + + pub const Index = InternPool.NamespaceIndex; // TODO: Actually use this value // comptimeness: Comptimeness, @@ -161,15 +148,15 @@ pub const InterpretResult = union(enum) { pub fn huntItDown( interpreter: *ComptimeInterpreter, - namespace: NamespaceIndex, + namespace: Namespace.Index, decl_name: []const u8, options: InterpretOptions, -) ?Decl { +) ?InternPool.DeclIndex { _ = options; var current_namespace = namespace; while (current_namespace != .none) { - const decls: std.StringArrayHashMapUnmanaged(Decl) = interpreter.namespaces.items(.decls)[@enumToInt(current_namespace)]; + const decls = interpreter.namespaces.items(.decls)[@enumToInt(current_namespace)]; defer current_namespace = interpreter.namespaces.items(.parent)[@enumToInt(current_namespace)]; if (decls.get(decl_name)) |decl| { @@ -199,7 +186,7 @@ pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || s pub fn interpret( interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - namespace: NamespaceIndex, + namespace: Namespace.Index, options: InterpretOptions, ) InterpretError!InterpretResult { const tree = interpreter.getHandle().tree; @@ -229,10 +216,16 @@ pub fn interpret( .node_idx = node_idx, .ty = undefined, }); - const container_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); + const container_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1); - var fields = std.ArrayListUnmanaged(InternPool.Struct.Field){}; - defer fields.deinit(interpreter.allocator); + const struct_index = try interpreter.ip.createStruct(interpreter.allocator, .{ + .fields = .{}, + .namespace = container_namespace, + .layout = .Auto, // TODO + .backing_int_ty = .none, // TODO + .status = .none, // TODO + }); + var struct_info = interpreter.ip.getStruct(struct_index); var buffer: [2]Ast.Node.Index = undefined; @@ -259,28 +252,18 @@ pub fn interpret( ); continue; } - const field_name = try interpreter.ip.get(interpreter.allocator, .{ - .bytes = tree.tokenSlice(container_field.ast.main_token), - }); - const field: InternPool.Struct.Field = .{ - .name = field_name, + + const field_name = tree.tokenSlice(container_field.ast.main_token); + + try struct_info.fields.put(interpreter.allocator, field_name, .{ .ty = init_type_value.val, .default_value = default_value, .alignment = 0, // TODO, .is_comptime = false, // TODO - }; - - try fields.append(interpreter.allocator, field); + }); } - const struct_type = try interpreter.ip.get(interpreter.allocator, Key{ - .struct_type = .{ - .fields = fields.items, - .namespace = namespace, - .layout = .Auto, // TODO - .backing_int_ty = .none, // TODO - }, - }); + const struct_type = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }); // TODO potential double free on struct_info.field interpreter.namespaces.items(.ty)[@enumToInt(container_namespace)] = struct_type; return InterpretResult{ .value = Value{ @@ -317,7 +300,7 @@ pub fn interpret( if (v.ty != type_type) return InterpretResult{ .nothing = {} }; } - try decls.putNoClobber(interpreter.allocator, name, .{ + const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, .ty = if (type_value) |v| v.val else init_value.?.ty, .val = if (init_value) |init| init.val else .none, @@ -326,6 +309,7 @@ pub fn interpret( .is_pub = true, // TODO .is_exported = false, // TODO }); + try decls.putNoClobber(interpreter.allocator, name, decl_index); // TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...) @@ -345,7 +329,7 @@ pub fn interpret( .node_idx = node_idx, .ty = .none, }); - const block_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); + const block_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1); var buffer: [2]Ast.Node.Index = undefined; const statements = ast.blockStatements(tree, node_idx, &buffer).?; @@ -442,7 +426,8 @@ pub fn interpret( } // Logic to find identifiers in accessible scopes - if (interpreter.huntItDown(namespace, identifier, options)) |decl| { + if (interpreter.huntItDown(namespace, identifier, options)) |decl_index| { + const decl = interpreter.ip.getDecl(decl_index); return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -478,7 +463,8 @@ pub fn interpret( if (irv.val == .none) break :blk true; const ty_key = interpreter.ip.indexToKey(irv.val); - if (interpreter.huntItDown(ty_key.getNamespace(), field_name, options)) |decl| { + if (interpreter.huntItDown(ty_key.getNamespace(interpreter.ip), field_name, options)) |decl_index| { + const decl = interpreter.ip.getDecl(decl_index); return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -492,19 +478,18 @@ pub fn interpret( _ = error_set_info; }, .union_type => {}, // TODO - .enum_type => |enum_info| { // TODO - if (interpreter.ip.contains(Key{ .bytes = field_name })) |field_name_index| { - for (enum_info.fields) |field| { - if (field.name != field_name_index) continue; - return InterpretResult{ - .value = Value{ - .interpreter = interpreter, - .node_idx = data[node_idx].rhs, - .ty = irv.val, - .val = .none, // TODO resolve enum value - }, - }; - } + .enum_type => |enum_index| { // TODO + const enum_info = interpreter.ip.getEnum(enum_index); + if (enum_info.fields.get(field_name)) |field| { + _ = field; + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = irv.val, + .val = .none, // TODO resolve enum value + }, + }; } }, else => break :blk false, @@ -583,17 +568,15 @@ pub fn interpret( } }; } }, - .struct_type => |struct_info| blk: { - // if the intern pool does not contain the field name, it is impossible that there is a field with the given name - const field_name_index = interpreter.ip.contains(Key{ .bytes = field_name }) orelse break :blk true; - - for (struct_info.fields) |field, i| { - if (field.name != field_name_index) continue; + .struct_type => |struct_index| blk: { + const struct_info = interpreter.ip.getStruct(struct_index); + if (struct_info.fields.getIndex(field_name)) |field_index| { + const field = struct_info.fields.values()[field_index]; const val = found_val: { if (irv.val == .none) break :found_val .none; const val_key = interpreter.ip.indexToKey(irv.val); if (val_key != .aggregate) break :found_val .none; - break :found_val val_key.aggregate[i]; + break :found_val val_key.aggregate[field_index]; }; return InterpretResult{ .value = Value{ @@ -813,15 +796,17 @@ pub fn interpret( // TODO: Implement root support if (std.mem.eql(u8, import_str[1 .. import_str.len - 1], "root")) { + const struct_index = try interpreter.ip.createStruct(interpreter.allocator, .{ + .fields = .{}, + .namespace = .none, + .layout = .Auto, + .backing_int_ty = .none, + .status = .none, + }); return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = .{ - .fields = &.{}, - .namespace = .none, - .layout = .Auto, - .backing_int_ty = .none, - } }), + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }), .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .undefined_value }), } }; } @@ -865,7 +850,7 @@ pub fn interpret( if (value.ty != type_type) return error.InvalidBuiltin; if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8 - const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace(); + const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace(interpreter.ip); if (value_namespace == .none) return error.InvalidBuiltin; const name = interpreter.ip.indexToKey(field_name.val).bytes; // TODO add checks @@ -986,7 +971,8 @@ pub fn interpret( if (namespace != .none) { const decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; - try decls.put(interpreter.allocator, name, .{ + + const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, .ty = type_type, .val = function_type, @@ -995,6 +981,7 @@ pub fn interpret( .is_pub = false, // TODO .is_exported = false, // TODO }); + try decls.putNoClobber(interpreter.allocator, name, decl_index); } return InterpretResult{ .nothing = {} }; @@ -1107,7 +1094,7 @@ pub fn interpret( } pub const CallResult = struct { - namespace: NamespaceIndex, + namespace: Namespace.Index, result: union(enum) { value: Value, nothing, @@ -1116,7 +1103,7 @@ pub const CallResult = struct { pub fn call( interpreter: *ComptimeInterpreter, - namespace: NamespaceIndex, + namespace: Namespace.Index, func_node_idx: Ast.Node.Index, arguments: []const Value, options: InterpretOptions, @@ -1136,7 +1123,7 @@ pub fn call( .node_idx = func_node_idx, .ty = .none, }); - const fn_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); + const fn_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1); const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); @@ -1157,7 +1144,8 @@ pub fn call( if (param.name_token) |name_token| { const name = offsets.tokenToSlice(tree, name_token); - try interpreter.namespaces.items(.decls)[@enumToInt(fn_namespace)].put(interpreter.allocator, name, .{ + const decls = &interpreter.namespaces.items(.decls)[@enumToInt(fn_namespace)]; + const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, .ty = tex.val, .val = arguments[arg_index].val, @@ -1166,6 +1154,7 @@ pub fn call( .is_pub = true, // TODO .is_exported = false, // TODO }); + try decls.putNoClobber(interpreter.allocator, name, decl_index); arg_index += 1; } } diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 07fb94e..3f6304f 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -4,6 +4,11 @@ map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, items: std.MultiArrayList(Item) = .{}, extra: std.ArrayListUnmanaged(u8) = .{}, +decls: std.ArrayListUnmanaged(InternPool.Decl) = .{}, +structs: std.ArrayListUnmanaged(InternPool.Struct) = .{}, +enums: std.ArrayListUnmanaged(InternPool.Enum) = .{}, +unions: std.ArrayListUnmanaged(InternPool.Union) = .{}, + const InternPool = @This(); const std = @import("std"); const builtin = @import("builtin"); @@ -36,15 +41,26 @@ pub const Array = packed struct { sentinel: Index = .none, }; +pub const FieldStatus = enum { + none, + field_types_wip, + have_field_types, + layout_wip, + have_layout, + fully_resolved_wip, + fully_resolved, +}; + +pub const StructIndex = enum(u32) { _ }; + pub const Struct = struct { - fields: []const Field, + fields: std.StringArrayHashMapUnmanaged(Field), namespace: NamespaceIndex, layout: std.builtin.Type.ContainerLayout = .Auto, backing_int_ty: Index, + status: FieldStatus, pub const Field = packed struct { - /// guaranteed to be .bytes - name: Index, ty: Index, default_value: Index = .none, alignment: u16 = 0, @@ -66,17 +82,14 @@ pub const ErrorSet = struct { names: []const Index, }; +pub const EnumIndex = enum(u32) { _ }; + pub const Enum = struct { tag_type: Index, - fields: []const Field, + fields: std.StringArrayHashMapUnmanaged(void), + values: std.AutoArrayHashMapUnmanaged(Index, void), namespace: NamespaceIndex, tag_type_infered: bool, - - pub const Field = packed struct { - /// guaranteed to be .bytes - name: Index, - val: Index, - }; }; pub const Function = struct { @@ -94,15 +107,16 @@ pub const Function = struct { is_var_args: bool = false, }; +pub const UnionIndex = enum(u32) { _ }; + pub const Union = struct { tag_type: Index, - fields: []const Field, + fields: std.StringArrayHashMapUnmanaged(Field), namespace: NamespaceIndex, layout: std.builtin.Type.ContainerLayout = .Auto, + status: FieldStatus, pub const Field = packed struct { - /// guaranteed to be .bytes - name: Index, ty: Index, alignment: u16, }; @@ -134,19 +148,34 @@ pub const UnionValue = packed struct { val: Index, }; +pub const DeclIndex = enum(u32) { _ }; + +pub const Decl = struct { + name: []const u8, + ty: Index, + val: Index, + alignment: u16, + address_space: std.builtin.AddressSpace, + is_pub: bool, + is_exported: bool, +}; + pub const Key = union(enum) { simple: Simple, int_type: Int, pointer_type: Pointer, array_type: Array, - struct_type: Struct, + /// TODO consider *Struct instead of StructIndex + struct_type: StructIndex, optional_type: Optional, error_union_type: ErrorUnion, error_set_type: ErrorSet, - enum_type: Enum, + /// TODO consider *Enum instead of EnumIndex + enum_type: EnumIndex, function_type: Function, - union_type: Union, + /// TODO consider *Union instead of UnionIndex + union_type: UnionIndex, tuple_type: Tuple, vector_type: Vector, anyframe_type: AnyFrame, @@ -396,7 +425,8 @@ pub const Key = union(enum) { }, .int_type => |int_info| return int_info, .enum_type => return panicOrElse("TODO", .{ .signedness = .unsigned, .bits = 0 }), - .struct_type => |struct_info| { + .struct_type => |struct_index| { + const struct_info = ip.getStruct(struct_index); assert(struct_info.layout == .Packed); key = ip.indexToKey(struct_info.backing_int_ty); }, @@ -485,11 +515,11 @@ pub const Key = union(enum) { }; } - pub fn getNamespace(ty: Key) NamespaceIndex { + pub fn getNamespace(ty: Key, ip: InternPool) NamespaceIndex { return switch (ty) { - .struct_type => |struct_info| struct_info.namespace, - .enum_type => |enum_info| enum_info.namespace, - .union_type => |union_info| union_info.namespace, + .struct_type => |struct_index| ip.getStruct(struct_index).namespace, + .enum_type => |enum_index| ip.getEnum(enum_index).namespace, + .union_type => |union_index| ip.getUnion(union_index).namespace, else => .none, }; } @@ -555,10 +585,12 @@ pub const Key = union(enum) { } return null; }, - .struct_type => |struct_info| { - for (struct_info.fields) |field| { - if (field.is_comptime) continue; - if (ip.indexToKey(field.ty).onePossibleValue(ip) != null) continue; + .struct_type => |struct_index| { + const struct_info = ip.getStruct(struct_index); + var field_it = struct_info.fields.iterator(); + while (field_it.next()) |entry| { + if (entry.value_ptr.is_comptime) continue; + if (ip.indexToKey(entry.value_ptr.ty).onePossibleValue(ip) != null) continue; return null; } return panicOrElse("TODO return empty struct value", null); @@ -572,10 +604,11 @@ pub const Key = union(enum) { }, .error_union_type => return null, .error_set_type => return null, - .enum_type => |enum_info| { - switch (enum_info.fields.len) { + .enum_type => |enum_index| { + const enum_info = ip.getEnum(enum_index); + switch (enum_info.fields.count()) { 0 => return Key{ .simple = .unreachable_value }, - 1 => return ip.indexToKey(enum_info.fields[0].val), + 1 => return ip.indexToKey(enum_info.values.keys()[0]), else => return null, } }, @@ -623,11 +656,7 @@ pub const Key = union(enum) { ip: InternPool, }; - // TODO implement options - pub const FormatOptions = struct { - include_fields: bool = true, - include_declarations: bool = true, - }; + pub const FormatOptions = struct {}; fn formatType( ctx: TypeFormatContext, @@ -925,27 +954,27 @@ pub const Key = union(enum) { .bytes => |bytes| try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}), .aggregate => |aggregate| { - const struct_info = ip.indexToKey(ty).struct_type; - assert(aggregate.len == struct_info.fields.len); + const struct_info = ip.getStruct(ip.indexToKey(ty).struct_type); + assert(aggregate.len == struct_info.fields.count()); try writer.writeAll(".{"); var i: u32 = 0; while (i < aggregate.len) : (i += 1) { if (i != 0) try writer.writeAll(", "); - const field_name = ip.indexToKey(struct_info.fields[i].name).bytes; + const field_name = struct_info.fields.keys()[i]; try writer.print(".{s} = ", .{field_name}); - try printValue(ip.indexToKey(aggregate[i]), struct_info.fields[i].ty, ip, writer); + try printValue(ip.indexToKey(aggregate[i]), struct_info.fields.values()[i].ty, ip, writer); } try writer.writeByte('}'); }, .union_value => |union_value| { - const union_info = ip.indexToKey(ty).union_type; + const union_info = ip.getUnion(ip.indexToKey(ty).union_type); - const name = ip.indexToKey(union_info.fields[union_value.field_index].name).bytes; + const name = union_info.fields.keys()[union_value.field_index]; try writer.print(".{{ .{} = {} }}", .{ std.zig.fmtId(name), - union_value.val.fmtValue(union_info.fields[union_value.field_index].ty, ip), + union_value.val.fmtValue(union_info.fields.values()[union_value.field_index].ty, ip), }); }, } @@ -978,6 +1007,7 @@ pub const Item = struct { /// Two values which have the same type can be equality compared simply /// by checking if their indexes are equal, provided they are both in /// the same `InternPool`. +/// TODO split this into an Optional and non-Optional Index pub const Index = enum(u32) { none = std.math.maxInt(u32), _, @@ -1181,6 +1211,21 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.map.deinit(gpa); ip.items.deinit(gpa); ip.extra.deinit(gpa); + + for (ip.structs.items) |*item| { + item.fields.deinit(gpa); + } + for (ip.enums.items) |*item| { + item.fields.deinit(gpa); + item.values.deinit(gpa); + } + for (ip.unions.items) |*item| { + item.fields.deinit(gpa); + } + ip.decls.deinit(gpa); + ip.structs.deinit(gpa); + ip.enums.deinit(gpa); + ip.unions.deinit(gpa); } pub fn indexToKey(ip: InternPool, index: Index) Key { @@ -1199,17 +1244,18 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { } }, .type_pointer => .{ .pointer_type = ip.extraData(Pointer, data) }, .type_array => .{ .array_type = ip.extraData(Array, data) }, - .type_struct => .{ .struct_type = ip.extraData(Struct, data) }, .type_optional => .{ .optional_type = .{ .payload_type = @intToEnum(Index, data) } }, .type_anyframe => .{ .anyframe_type = .{ .child = @intToEnum(Index, data) } }, .type_error_union => .{ .error_union_type = ip.extraData(ErrorUnion, data) }, .type_error_set => .{ .error_set_type = ip.extraData(ErrorSet, data) }, - .type_enum => .{ .enum_type = ip.extraData(Enum, data) }, .type_function => .{ .function_type = ip.extraData(Function, data) }, - .type_union => .{ .union_type = ip.extraData(Union, data) }, .type_tuple => .{ .tuple_type = ip.extraData(Tuple, data) }, .type_vector => .{ .vector_type = ip.extraData(Vector, data) }, + .type_struct => .{ .struct_type = @intToEnum(StructIndex, data) }, + .type_enum => .{ .enum_type = @intToEnum(EnumIndex, data) }, + .type_union => .{ .union_type = @intToEnum(UnionIndex, data) }, + .int_u32 => .{ .int_u64_value = @intCast(u32, data) }, .int_i32 => .{ .int_i64_value = @bitCast(i32, data) }, .int_u64 => .{ .int_u64_value = ip.extraData(u64, data) }, @@ -1248,6 +1294,10 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .optional_type => |optional_ty| @enumToInt(optional_ty.payload_type), .anyframe_type => |anyframe_ty| @enumToInt(anyframe_ty.child), + .struct_type => |struct_index| @enumToInt(struct_index), + .enum_type => |enum_index| @enumToInt(enum_index), + .union_type => |union_index| @enumToInt(union_index), + .int_u64_value => |int_val| if (tag == .int_u32) @intCast(u32, int_val) else try ip.addExtra(gpa, int_val), .int_i64_value => |int_val| if (tag == .int_i32) @bitCast(u32, @intCast(u32, int_val)) else try ip.addExtra(gpa, int_val), .int_big_value => |big_int_val| try ip.addExtra(gpa, big_int_val.limbs), @@ -1270,6 +1320,36 @@ pub fn contains(ip: InternPool, key: Key) ?Index { return @intToEnum(Index, index); } +pub fn getDecl(ip: *InternPool, index: InternPool.DeclIndex) *InternPool.Decl { + return &ip.decls.items[@enumToInt(index)]; +} +pub fn getStruct(ip: InternPool, index: InternPool.StructIndex) *InternPool.Struct { + return &ip.structs.items[@enumToInt(index)]; +} +pub fn getEnum(ip: InternPool, index: InternPool.EnumIndex) *InternPool.Enum { + return &ip.enums.items[@enumToInt(index)]; +} +pub fn getUnion(ip: InternPool, index: InternPool.UnionIndex) *InternPool.Union { + return &ip.unions.items[@enumToInt(index)]; +} + +pub fn createDecl(ip: *InternPool, gpa: Allocator, decl: InternPool.Decl) error{OutOfMemory}!InternPool.DeclIndex { + try ip.decls.append(gpa, decl); + return @intToEnum(InternPool.DeclIndex, ip.decls.items.len - 1); +} +pub fn createStruct(ip: *InternPool, gpa: Allocator, struct_info: InternPool.Struct) error{OutOfMemory}!InternPool.StructIndex { + try ip.structs.append(gpa, struct_info); + return @intToEnum(InternPool.StructIndex, ip.structs.items.len - 1); +} +pub fn createEnum(ip: *InternPool, gpa: Allocator, enum_info: InternPool.Enum) error{OutOfMemory}!InternPool.EnumIndex { + try ip.enums.append(gpa, enum_info); + return @intToEnum(InternPool.EnumIndex, ip.enums.items.len - 1); +} +pub fn createUnion(ip: *InternPool, gpa: Allocator, union_info: InternPool.Union) error{OutOfMemory}!InternPool.UnionIndex { + try ip.unions.append(gpa, union_info); + return @intToEnum(InternPool.UnionIndex, ip.unions.items.len - 1); +} + fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { const T = @TypeOf(extra); comptime if (@sizeOf(T) <= 4) { @@ -2997,41 +3077,6 @@ test "array type" { try testExpectFmtType(ip, u32_0_0_array_type, "[3:0]u32"); } -test "struct type" { - const gpa = std.testing.allocator; - - var ip: InternPool = .{}; - defer ip.deinit(gpa); - - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); - - const foo_name = try ip.get(gpa, .{ .bytes = "foo" }); - const bar_name = try ip.get(gpa, .{ .bytes = "bar" }); - const baz_name = try ip.get(gpa, .{ .bytes = "baz" }); - - const field1 = Struct.Field{ .name = foo_name, .ty = u64_type }; - const field2 = Struct.Field{ .name = bar_name, .ty = i32_type }; - const field3 = Struct.Field{ .name = baz_name, .ty = bool_type }; - - const struct_type_0 = try ip.get(gpa, .{ .struct_type = .{ - .fields = &.{ field1, field2, field3 }, - .namespace = .none, - .layout = .Auto, - .backing_int_ty = .none, - } }); - - const struct_type_1 = try ip.get(gpa, .{ .struct_type = .{ - .fields = &.{ field1, field2, field3 }, - .namespace = .none, - .layout = .Auto, - .backing_int_ty = .none, - } }); - - try std.testing.expect(struct_type_0 == struct_type_1); -} - test "struct value" { const gpa = std.testing.allocator; @@ -3041,18 +3086,17 @@ test "struct value" { const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const foo_name = try ip.get(gpa, .{ .bytes = "foo" }); - const bar_name = try ip.get(gpa, .{ .bytes = "bar" }); - - const field1 = Struct.Field{ .name = foo_name, .ty = i32_type }; - const field2 = Struct.Field{ .name = bar_name, .ty = bool_type }; - - const struct_type = try ip.get(gpa, .{ .struct_type = .{ - .fields = &.{ field1, field2 }, + const struct_index = try ip.createStruct(gpa, .{ + .fields = .{}, .namespace = .none, .layout = .Auto, .backing_int_ty = .none, - } }); + .status = .none, + }); + const struct_type = try ip.get(gpa, .{ .struct_type = struct_index }); + const struct_info = ip.getStruct(struct_index); + try struct_info.fields.put(gpa, "foo", .{ .ty = i32_type }); + try struct_info.fields.put(gpa, "bar", .{ .ty = bool_type }); const one_value = try ip.get(gpa, .{ .int_i64_value = 1 }); const true_value = try ip.get(gpa, .{ .simple = .bool_true }); @@ -3062,50 +3106,6 @@ test "struct value" { try ip.testExpectFmtValue(aggregate_value, struct_type, ".{.foo = 1, .bar = true}"); } -test "enum type" { - const gpa = std.testing.allocator; - - var ip: InternPool = .{}; - defer ip.deinit(gpa); - - const zig_name = try ip.get(gpa, .{ .bytes = "zig" }); - const cpp_name = try ip.get(gpa, .{ .bytes = "cpp" }); - - const empty_enum1 = try ip.get(gpa, .{ .enum_type = .{ - .tag_type = .none, - .fields = &.{}, - .namespace = .none, - .tag_type_infered = true, - } }); - - const empty_enum2 = try ip.get(gpa, .{ .enum_type = .{ - .tag_type = .none, - .fields = &.{}, - .namespace = .none, - .tag_type_infered = true, - } }); - - const field1 = Enum.Field{ .name = zig_name, .val = .none }; - const field2 = Enum.Field{ .name = cpp_name, .val = .none }; - - const enum1 = try ip.get(gpa, .{ .enum_type = .{ - .tag_type = .none, - .fields = &.{ field1, field2 }, - .namespace = .none, - .tag_type_infered = true, - } }); - const enum2 = try ip.get(gpa, .{ .enum_type = .{ - .tag_type = .none, - .fields = &.{ field2, field1 }, - .namespace = .none, - .tag_type_infered = true, - } }); - - try std.testing.expect(empty_enum1 == empty_enum2); - try std.testing.expect(empty_enum2 != enum1); - try std.testing.expect(enum1 != enum2); -} - test "function type" { const gpa = std.testing.allocator; @@ -3152,40 +3152,6 @@ test "function type" { try testExpectFmtType(ip, @"fn() align(4) callconv(.C) type", "fn() align(4) callconv(.C) type"); } -test "union type" { - const gpa = std.testing.allocator; - - var ip: InternPool = .{}; - defer ip.deinit(gpa); - - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const void_type = try ip.get(gpa, .{ .simple = .void }); - - const ok_name = try ip.get(gpa, .{ .bytes = "Ok" }); - const err_name = try ip.get(gpa, .{ .bytes = "Err" }); - - var field1 = Union.Field{ .name = ok_name, .ty = u32_type, .alignment = 0 }; - var field2 = Union.Field{ .name = err_name, .ty = void_type, .alignment = 0 }; - - const union_type1 = try ip.get(gpa, .{ - .union_type = .{ - .tag_type = .none, - .fields = &.{ field1, field2 }, - .namespace = .none, - }, - }); - - const union_type2 = try ip.get(gpa, .{ - .union_type = .{ - .tag_type = .none, - .fields = &.{ field2, field1 }, - .namespace = .none, - }, - }); - - try std.testing.expect(union_type1 != union_type2); -} - test "union value" { const gpa = std.testing.allocator; @@ -3198,19 +3164,17 @@ test "union value" { const int_value = try ip.get(gpa, .{ .int_u64_value = 1 }); const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 }); - const int_name = try ip.get(gpa, .{ .bytes = "int" }); - const float_name = try ip.get(gpa, .{ .bytes = "float" }); - - var field1 = Union.Field{ .name = int_name, .ty = u32_type, .alignment = 0 }; - var field2 = Union.Field{ .name = float_name, .ty = f16_type, .alignment = 0 }; - - const union_type = try ip.get(gpa, .{ - .union_type = .{ - .tag_type = .none, - .fields = &.{ field1, field2 }, - .namespace = .none, - }, + const union_index = try ip.createUnion(gpa, .{ + .tag_type = .none, + .fields = .{}, + .namespace = .none, + .layout = .Auto, + .status = .none, }); + const union_type = try ip.get(gpa, .{ .union_type = union_index }); + const union_info = ip.getUnion(union_index); + try union_info.fields.put(gpa, "int", .{ .ty = u32_type, .alignment = 0 }); + try union_info.fields.put(gpa, "float", .{ .ty = f16_type, .alignment = 0 }); const union_value1 = try ip.get(gpa, .{ .union_value = .{ .field_index = 0, diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 14e0eee..72d6806 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -24,7 +24,7 @@ pub fn dotCompletions( .simple => |simple| switch (simple) { .type => { const ty_key = ip.indexToKey(val); - const namespace = ty_key.getNamespace(); + const namespace = ty_key.getNamespace(ip.*); if (namespace != .none) { // TODO lookup in namespace } @@ -40,11 +40,12 @@ pub fn dotCompletions( } }, .union_type => {}, // TODO - .enum_type => |enum_info| { - for (enum_info.fields) |field| { - const field_name = ip.indexToKey(field.name).bytes; + .enum_type => |enum_index| { + const enum_info = ip.getEnum(enum_index); + var field_it = enum_info.fields.iterator(); + while (field_it.next()) |entry| { try completions.append(arena, .{ - .label = field_name, + .label = entry.key_ptr.*, .kind = .Constant, // include field.val? }); @@ -85,14 +86,15 @@ pub fn dotCompletions( .detail = try std.fmt.allocPrint(arena, "usize ({d})", .{array_info.len}), // TODO how should this be displayed }); }, - .struct_type => |struct_info| { - for (struct_info.fields) |field| { - const field_name = ip.indexToKey(field.name).bytes; + .struct_type => |struct_index| { + const struct_info = ip.getStruct(struct_index); + var field_it = struct_info.fields.iterator(); + while (field_it.next()) |entry| { try completions.append(arena, types.CompletionItem{ - .label = field_name, + .label = entry.key_ptr.*, .kind = .Field, // TODO include alignment and comptime - .detail = try std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip.*)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{entry.value_ptr.ty.fmtType(ip.*)}), }); } }, @@ -103,26 +105,28 @@ pub fn dotCompletions( .detail = try std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmtType(ip.*)}), }); }, - .enum_type => |enum_info| { - for (enum_info.fields) |field| { - const field_name = ip.indexToKey(field.name).bytes; + .enum_type => |enum_index| { + const enum_info = ip.getEnum(enum_index); + for (enum_info.fields.keys()) |field_name, i| { + const field_val = enum_info.values.keys()[i]; try completions.append(arena, .{ .label = field_name, .kind = .Field, - .detail = try std.fmt.allocPrint(arena, "{}", .{field.val.fmtValue(enum_info.tag_type, ip.*)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{field_val.fmtValue(enum_info.tag_type, ip.*)}), }); } }, - .union_type => |union_info| { - for (union_info.fields) |field| { - const field_name = ip.indexToKey(field.name).bytes; + .union_type => |union_index| { + const union_info = ip.getUnion(union_index); + var field_it = union_info.fields.iterator(); + while (field_it.next()) |entry| { try completions.append(arena, .{ - .label = field_name, + .label = entry.key_ptr.*, .kind = .Field, - .detail = if (field.alignment != 0) - try std.fmt.allocPrint(arena, "align({d}) {}", .{ field.alignment, field.ty.fmtType(ip.*) }) + .detail = if (entry.value_ptr.alignment != 0) + try std.fmt.allocPrint(arena, "align({d}) {}", .{ entry.value_ptr.alignment, entry.value_ptr.ty.fmtType(ip.*) }) else - try std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip.*)}), + try std.fmt.allocPrint(arena, "{}", .{entry.value_ptr.ty.fmtType(ip.*)}), }); } }, diff --git a/src/analysis.zig b/src/analysis.zig index c850790..d5f5105 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -772,7 +772,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl }; var interpreter: *ComptimeInterpreter = handle.interpreter.?; - const root_namespace = @intToEnum(ComptimeInterpreter.NamespaceIndex, 0); + const root_namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); // TODO: Start from current/nearest-current scope const result = interpreter.interpret(node, root_namespace, .{}) catch |err| { diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 920b909..36c442d 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -169,16 +169,15 @@ test "ComptimeInterpreter - call return struct" { try std.testing.expect(result.ty == .simple); try std.testing.expect(result.ty.simple == .type); - const struct_info = result.val.?.struct_type; + const struct_info = context.interpreter.ip.getStruct(result.val.?.struct_type); try std.testing.expectEqual(Index.none, struct_info.backing_int_ty); try std.testing.expectEqual(std.builtin.Type.ContainerLayout.Auto, struct_info.layout); - const field_name = context.interpreter.ip.indexToKey(struct_info.fields[0].name).bytes; const bool_type = try context.interpreter.ip.get(allocator, .{ .simple = .bool }); - try std.testing.expectEqual(@as(usize, 1), struct_info.fields.len); - try std.testing.expectEqualStrings("slay", field_name); - try std.testing.expect(struct_info.fields[0].ty == bool_type); + try std.testing.expectEqual(@as(usize, 1), struct_info.fields.count()); + try std.testing.expectEqualStrings("slay", struct_info.fields.keys()[0]); + try std.testing.expect(struct_info.fields.values()[0].ty == bool_type); } test "ComptimeInterpreter - call comptime argument" { @@ -284,7 +283,7 @@ const Context = struct { }; } - const namespace = @intToEnum(ComptimeInterpreter.NamespaceIndex, 0); // root namespace + const namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); // root namespace const result = (try self.interpreter.call(namespace, func_node, args, .{})).result; try std.testing.expect(result == .value); @@ -297,7 +296,7 @@ const Context = struct { } pub fn interpret(self: *Context, node: Ast.Node.Index) !KV { - const namespace = @intToEnum(ComptimeInterpreter.NamespaceIndex, 0); // root namespace + const namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); // root namespace const result = try (try self.interpreter.interpret(node, namespace, .{})).getValue(); try std.testing.expect(result.ty != .none);