diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index b2a0107..ad56b17 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -705,7 +705,7 @@ pub fn interpret( const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace(); if (value_namespace == .none) return error.InvalidBuiltin; - const name = interpreter.ip.indexToKey(field_name.val).bytes.data; // TODO add checks + const name = interpreter.ip.indexToKey(field_name.val).bytes; // TODO add checks const decls = interpreter.namespaces.items(.decls)[@enumToInt(value_namespace)]; const has_decl = decls.contains(name); @@ -764,7 +764,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = string_literal_type, - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = .{ .data = str } }), // TODO + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), // TODO }; // TODO: Add type casting, sentinel diff --git a/src/InternPool.zig b/src/InternPool.zig index 33dcd8a..e9b0793 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -10,26 +10,12 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; -const KeyAdapter = struct { - intern_pool: *const InternPool, - - pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { - _ = b_void; - return ctx.intern_pool.indexToKey(@intToEnum(Index, b_map_index)).eql(a); - } - - pub fn hash(ctx: @This(), a: Key) u32 { - _ = ctx; - return a.hash(); - } -}; - -pub const Int = struct { +pub const Int = packed struct { signedness: std.builtin.Signedness, bits: u16, }; -pub const Pointer = struct { +pub const Pointer = packed struct { elem_type: Index, sentinel: Index = .none, alignment: u16 = 0, @@ -40,7 +26,7 @@ pub const Pointer = struct { address_space: std.builtin.AddressSpace = .generic, }; -pub const Array = struct { +pub const Array = packed struct { // TODO support big int len: u32, child: Index, @@ -62,19 +48,15 @@ pub const Struct = struct { }; }; -pub const Optional = struct { +pub const Optional = packed struct { payload_type: Index, }; -pub const ErrorUnion = struct { +pub const ErrorUnion = packed struct { error_set_type: Index, payload_type: Index, }; -// pub const Error = struct { -// name: []const u8, -// }; - pub const ErrorSet = struct { /// must be sorted names: []const []const u8, @@ -104,11 +86,11 @@ pub const Fn = struct { return_type: Index, args: []const Param, - pub const Param = struct { + pub const Param = packed struct { + arg_type: Index, is_comptime: bool = false, is_generic: bool = false, is_noalias: bool = false, - arg_type: Index, }; }; @@ -127,31 +109,27 @@ pub const Union = struct { pub const Tuple = struct { types: []const Index, - /// unreachable_value elements are used to indicate runtime-known. + /// Index.none elements are used to indicate runtime-known. values: []const Index, }; -pub const Vector = struct { +pub const Vector = packed struct { // TODO support big int len: u32, child: Index, }; -pub const AnyFrame = struct { +pub const AnyFrame = packed struct { child: Index, }; pub const BigInt = std.math.big.int.Const; -pub const Bytes = struct { - data: []const u8, -}; +pub const Bytes = []const u8; -pub const Aggregate = struct { - data: []const Index, -}; +pub const Aggregate = []const Index; -pub const UnionValue = struct { +pub const UnionValue = packed struct { tag: Index, val: Index, }; @@ -165,14 +143,13 @@ pub const Key = union(enum) { struct_type: Struct, optional_type: Optional, error_union_type: ErrorUnion, - // error_type: Error, error_set_type: ErrorSet, enum_type: Enum, function_type: Fn, union_type: Union, tuple_type: Tuple, vector_type: Vector, - anyframe_t_type: AnyFrame, + anyframe_type: AnyFrame, int_u64_value: u64, int_i64_value: i64, @@ -182,10 +159,8 @@ pub const Key = union(enum) { float_64_value: f64, float_80_value: f80, float_128_value: f128, - // type_value: Index, bytes: Bytes, - // one_pointer: Index, aggregate: Aggregate, union_value: UnionValue, @@ -193,98 +168,10 @@ pub const Key = union(enum) { // error // error union - pub fn hash(key: Key) u32 { - var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, std.meta.activeTag(key)); - switch (key) { - .float_16_value => |f| std.hash.autoHash(&hasher, @bitCast(u16, f)), - .float_32_value => |f| std.hash.autoHash(&hasher, @bitCast(u32, f)), - .float_64_value => |f| std.hash.autoHash(&hasher, @bitCast(u64, f)), - .float_80_value => |f| std.hash.autoHash(&hasher, @bitCast(u80, f)), - .float_128_value => |f| std.hash.autoHash(&hasher, @bitCast(u128, f)), - inline else => |info| std.hash.autoHashStrat(&hasher, info, .Deep), // TODO sad stage1 noises :( - } - return @truncate(u32, hasher.final()); - } - - pub fn eql(a: Key, b: Key) bool { - const KeyTag = std.meta.Tag(Key); - const a_tag: KeyTag = a; - const b_tag: KeyTag = b; - if (a_tag != b_tag) return false; - return switch (a) { - .struct_type => |struct_info| { - if (struct_info.layout != b.struct_type.layout) return false; - if (struct_info.fields.len != b.struct_type.fields.len) return false; - for (struct_info.fields) |field, i| { - if (!std.meta.eql(field, b.struct_type.fields[i])) return false; - } - return true; - }, - // .error_type => |error_info| std.mem.eql(u8, error_info.name, b.error_type.name), - .error_set_type => |error_set_info| { - if (error_set_info.names.len != b.error_set_type.names.len) return false; - for (error_set_info.names) |a_name, i| { - const b_name = b.error_set_type.names[i]; - if (!std.mem.eql(u8, a_name, b_name)) return false; - } - return true; - }, - .enum_type => |enum_info| { - if (enum_info.tag_type != b.enum_type.tag_type) return false; - if (enum_info.tag_type_infered != b.enum_type.tag_type_infered) return false; - if (enum_info.fields.len != b.enum_type.fields.len) return false; - @panic("TODO: implement field equality check"); - }, - .function_type => |function_info| { - if (function_info.calling_convention != b.function_type.calling_convention) return false; - if (function_info.alignment != b.function_type.alignment) return false; - if (function_info.is_generic != b.function_type.is_generic) return false; - if (function_info.is_var_args != b.function_type.is_var_args) return false; - if (function_info.return_type != b.function_type.return_type) return false; - if (function_info.args.len != b.function_type.args.len) return false; - - for (function_info.args) |arg, i| { - if (!std.meta.eql(arg, b.function_type.args[i])) return false; - } - return true; - }, - .union_type => |union_info| { - if (union_info.tag_type != b.union_type.tag_type) return false; - if (union_info.layout != b.union_type.layout) return false; - if (union_info.fields.len != b.union_type.fields.len) return false; - for (union_info.fields) |field, i| { - if (!std.meta.eql(field, b.union_type.fields[i])) return false; - } - return true; - }, - .tuple_type => |tuple_info| { - std.debug.assert(tuple_info.types.len == tuple_info.values.len); - std.debug.assert(b.tuple_type.types.len == b.tuple_type.values.len); - if (tuple_info.types.len != b.tuple_type.types.len) return false; - for (tuple_info.types) |ty, i| { - if (ty != b.tuple_type.types[i]) return false; - } - for (tuple_info.values) |val, i| { - if (val != b.tuple_type.values[i]) return false; - } - return true; - }, - .bytes => |bytes| std.mem.eql(u8, bytes.data, b.bytes.data), - .aggregate => |aggregate| { - if (aggregate.data.len != b.aggregate.data.len) return false; - for (aggregate.data) |ty, i| { - if (ty != b.aggregate.data[i]) return false; - } - return true; - }, - else => std.meta.eql(a, b), - }; - } - pub fn tag(key: Key) Tag { return switch (key) { .simple => .simple, + .int_type => |int_info| switch (int_info.signedness) { .signed => .type_int_signed, .unsigned => .type_int_unsigned, @@ -294,14 +181,13 @@ pub const Key = union(enum) { .struct_type => .type_struct, .optional_type => .type_optional, .error_union_type => .type_error_union, - // .error_type => .type_error, .error_set_type => .type_error_set, .enum_type => .type_enum, .function_type => .type_function, .union_type => .type_union, .tuple_type => .type_tuple, .vector_type => .type_vector, - .anyframe_t_type => .type_anyframe_t, + .anyframe_type => .type_anyframe, .int_u64_value => |int| if (int <= std.math.maxInt(u32)) .int_u32 else .int_u64, .int_i64_value => |int| if (std.math.maxInt(i32) <= int and int <= std.math.maxInt(i32)) .int_i32 else .int_i64, @@ -311,10 +197,8 @@ pub const Key = union(enum) { .float_64_value => .float_f64, .float_80_value => .float_f80, .float_128_value => .float_f128, - // .type_value => .type, .bytes => .bytes, - // .one_pointer => .one_pointer, .aggregate => .aggregate, .union_value => .union_value, }; @@ -378,7 +262,7 @@ pub const Key = union(enum) { .union_type => .Union, .tuple_type => .Struct, // TODO this correct? .vector_type => .Vector, - .anyframe_t_type => .AnyFrame, + .anyframe_type => .AnyFrame, .int_u64_value, .int_i64_value, @@ -500,7 +384,7 @@ pub const Key = union(enum) { .array_type => |array_info| array_info.child, .optional_type => |optional_info| optional_info.payload_type, .vector_type => |vector_info| vector_info.child, - .anyframe_t_type => |anyframe_t_info| anyframe_t_info.child, + .anyframe_type => |anyframe_info| anyframe_info.child, else => unreachable, }; } @@ -717,7 +601,7 @@ pub const Key = union(enum) { vector_info.child.fmtType(ip), }); }, - .anyframe_t_type => |anyframe_info| { + .anyframe_type => |anyframe_info| { try writer.writeAll("anyframe->"); try printType(anyframe_info.child, ip, writer); }, @@ -813,7 +697,7 @@ pub const Key = union(enum) { .union_type, .tuple_type, .vector_type, - .anyframe_t_type, + .anyframe_type, => unreachable, .int_u64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer), @@ -826,19 +710,19 @@ pub const Key = union(enum) { .float_128_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), // .type_value => |tty| tty.fmtType(ip), - .bytes => |data| try writer.print("\"{}\"", .{std.zig.fmtEscapes(data.data)}), + .bytes => |bytes| try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}), // .one_pointer => unreachable, .aggregate => |aggregate| { const struct_info = ip.indexToKey(ty).struct_type; - std.debug.assert(aggregate.data.len == struct_info.fields.len); + std.debug.assert(aggregate.len == struct_info.fields.len); try writer.writeAll(".{"); var i: u32 = 0; - while (i < aggregate.data.len) : (i += 1) { + while (i < aggregate.len) : (i += 1) { if (i != 0) try writer.writeAll(", "); try writer.print(".{s} = ", .{struct_info.fields[i].name}); - try printValue(aggregate.data[i], struct_info.fields[i].ty, ip, writer); + try printValue(aggregate[i], struct_info.fields[i].ty, ip, writer); } try writer.writeByte('}'); }, @@ -917,9 +801,6 @@ pub const Tag = enum(u8) { /// An error union type. /// data is payload to ErrorUnion. type_error_union, - /// An error type. - /// data is payload to Error. - type_error, /// An error set type. /// data is payload to ErrorSet. type_error_set, @@ -940,7 +821,7 @@ pub const Tag = enum(u8) { type_vector, /// An anyframe->T type. /// data is index to type - type_anyframe_t, + type_anyframe, /// An unsigned integer value that can be represented by u32. /// data is integer value @@ -975,16 +856,10 @@ pub const Tag = enum(u8) { /// A float value that can be represented by f128. /// data is payload to f128. float_f128, - // /// A type value. - // /// data is Index. - // type, /// A byte sequence value. /// data is payload to data begin and length. bytes, - // /// A single pointer value. - // /// data is index to value. - // one_pointer, /// A aggregate (struct) value. /// data is index to Aggregate. aggregate, @@ -1039,12 +914,12 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { // TODO deinit fields } - pub fn indexToKey(ip: InternPool, index: Index) Key { const item = ip.items.get(@enumToInt(index)); const data = item.data; return switch (item.tag) { .simple => .{ .simple = @intToEnum(Simple, data) }, + .type_int_signed => .{ .int_type = .{ .signedness = .signed, .bits = @intCast(u16, data), @@ -1057,9 +932,8 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .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_t => .{ .anyframe_t_type = .{ .child = @intToEnum(Index, data) } }, + .type_anyframe => .{ .anyframe_type = .{ .child = @intToEnum(Index, data) } }, .type_error_union => .{ .error_union_type = ip.extraData(ErrorUnion, data) }, - // .type_error => .{ .error_type = ip.extraData(Error, 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(Fn, data) }, @@ -1071,59 +945,51 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .int_i32 => .{ .int_i64_value = @bitCast(i32, data) }, .int_u64 => .{ .int_u64_value = ip.extraData(u64, data) }, .int_i64 => .{ .int_i64_value = ip.extraData(i64, data) }, - .int_big_positive => unreachable, - .int_big_negative => unreachable, + .int_big_positive => .{ .int_big_value = .{ + .positive = true, + .limbs = ip.extraData([]const std.math.big.Limb, data), + } }, + .int_big_negative => .{ .int_big_value = .{ + .positive = true, + .limbs = ip.extraData([]const std.math.big.Limb, data), + } }, .float_f16 => .{ .float_16_value = @bitCast(f16, @intCast(u16, data)) }, .float_f32 => .{ .float_32_value = @bitCast(f32, data) }, .float_f64 => .{ .float_64_value = ip.extraData(f64, data) }, .float_f80 => .{ .float_80_value = ip.extraData(f80, data) }, .float_f128 => .{ .float_128_value = ip.extraData(f128, data) }, - // .type => .{ .type_value = @intToEnum(Index, data) }, - .bytes => unreachable, // TODO - // .one_pointer => .{ .one_pointer = @intToEnum(Index, data) }, - else => @panic("TODO"), + .bytes => .{ .bytes = ip.extraData([]const u8, data) }, + .aggregate => .{ .aggregate = ip.extraData(Aggregate, data) }, + .union_value => .{ .union_value = ip.extraData(UnionValue, data) }, }; } pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { - const adapter: KeyAdapter = .{ .intern_pool = ip }; + const adapter: KeyAdapter = .{ .ip = ip }; const gop = try ip.map.getOrPutAdapted(gpa, key, adapter); if (gop.found_existing) return @intToEnum(Index, gop.index); - const item: Item = switch (key) { - .simple => |simple| .{ .tag = .simple, .data = @enumToInt(simple) }, - .int_type => |int_ty| .{ - .tag = switch (int_ty.signedness) { - .signed => .type_int_signed, - .unsigned => .type_int_unsigned, - }, - .data = int_ty.bits, - }, - .optional_type => |optional_ty| .{ .tag = .type_optional, .data = @enumToInt(optional_ty.payload_type) }, - .anyframe_t_type => |anyframe_t| .{ .tag = .type_anyframe_t, .data = @enumToInt(anyframe_t.child) }, - .int_u64_value => |int_val| if (int_val <= std.math.maxInt(u32)) .{ - .tag = .int_u32, - .data = @intCast(u32, int_val), - } else .{ - .tag = .int_u64, - .data = try ip.addExtra(gpa, int_val), - }, - .int_i64_value => |int_val| if (std.math.maxInt(i32) <= int_val and int_val <= std.math.maxInt(i32)) .{ - .tag = .int_i32, - .data = @bitCast(u32, @intCast(u32, int_val)), - } else .{ - .tag = .int_i64, - .data = try ip.addExtra(gpa, int_val), - }, - .float_16_value => |float_val| .{ .tag = .float_f16, .data = @bitCast(u16, float_val) }, - .float_32_value => |float_val| .{ .tag = .float_f32, .data = @bitCast(u32, float_val) }, - // .type_value => |ty| .{ .tag = .type, .data = @enumToInt(ty) }, - .bytes => unreachable, // TODO - // .one_pointer => |val| .{ .tag = .one_pointer, .data = @enumToInt(val) }, - inline else => |data| .{ .tag = key.tag(), .data = try ip.addExtra(gpa, data) }, // TODO sad stage1 noises :( + const tag: Tag = key.tag(); + const data: u32 = switch (key) { + .simple => |simple| @enumToInt(simple), + + .int_type => |int_ty| int_ty.bits, + .optional_type => |optional_ty| @enumToInt(optional_ty.payload_type), + .anyframe_type => |anyframe_ty| @enumToInt(anyframe_ty.child), + + .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), + .float_16_value => |float_val| @bitCast(u16, float_val), + .float_32_value => |float_val| @bitCast(u32, float_val), + inline else => |data| try ip.addExtra(gpa, data), // TODO sad stage1 noises :( }; - try ip.items.append(gpa, item); + + try ip.items.append(gpa, .{ + .tag = tag, + .data = data, + }); return @intToEnum(Index, ip.items.len - 1); } @@ -1142,6 +1008,135 @@ fn extraData(ip: InternPool, comptime T: type, index: usize) T { return std.mem.bytesToValue(T, bytes); } +const KeyAdapter = struct { + ip: *const InternPool, + + pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { + _ = b_void; + + return deepEql(a, ctx.ip.indexToKey(@intToEnum(Index, b_map_index))); + } + + pub fn hash(ctx: @This(), a: Key) u32 { + _ = ctx; + var hasher = std.hash.Wyhash.init(0); + deepHash(&hasher, a); + return @truncate(u32, hasher.final()); + } +}; + +fn deepEql(a: anytype, b: @TypeOf(a)) bool { + const T = @TypeOf(a); + + switch (@typeInfo(T)) { + .Struct => |info| { + if (info.layout == .Packed) { + return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b)); + } + inline for (info.fields) |field_info| { + if (!deepEql(@field(a, field_info.name), @field(b, field_info.name))) return false; + } + return true; + }, + .Union => |info| { + const UnionTag = info.tag_type.?; + + const tag_a = std.meta.activeTag(a); + const tag_b = std.meta.activeTag(b); + if (tag_a != tag_b) return false; + + inline for (info.fields) |field_info| { + if (@field(UnionTag, field_info.name) == tag_a) { + return deepEql(@field(a, field_info.name), @field(b, field_info.name)); + } + } + return false; + }, + .Pointer => |info| { + if (info.size != .Slice) { + @compileError("cannot compare non slice pointer type " ++ @typeName(T)); + } + + if (@typeInfo(info.child) == .Int) { + return std.mem.eql(info.child, a, b); + } + if (a.len != b.len) return false; + + var i: usize = 0; + while (i < a.len) : (i += 1) { + if (!deepEql(a[i], b[i])) return false; + } + return true; + }, + .Enum, .Int => return a == b, + else => unreachable, + } +} + +fn deepHash(hasher: anytype, key: anytype) void { + const Inner = @TypeOf(key); + + switch (@typeInfo(Inner)) { + .Int => { + if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) { + hasher.update(std.mem.asBytes(&key)); + } else { + const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Inner), 8) catch unreachable; + hasher.update(std.mem.asBytes(&key)[0..byte_size]); + } + }, + + .Bool => deepHash(hasher, @boolToInt(key)), + .Enum => deepHash(hasher, @enumToInt(key)), + .Float => |info| deepHash(hasher, switch (info.bits) { + 16 => @bitCast(u16, key), + 32 => @bitCast(u32, key), + 64 => @bitCast(u64, key), + 80 => @bitCast(u80, key), + 128 => @bitCast(u128, key), + else => unreachable, + }), + + .Pointer => |info| { + if (info.size != .Slice) { + @compileError(""); + } + + if (comptime std.meta.trait.hasUniqueRepresentation(info.child)) { + hasher.update(std.mem.sliceAsBytes(key)); + } else { + for (key) |item| { + deepHash(hasher, item); + } + } + }, + + .Struct => |info| { + if (info.layout == .Packed) { + hasher.update(std.mem.asBytes(&key)); + } else { + inline for (info.fields) |field| { + deepHash(hasher, @field(key, field.name)); + } + } + }, + + .Union => |info| { + const TagType = info.tag_type.?; + + const tag = std.meta.activeTag(key); + deepHash(hasher, tag); + inline for (info.fields) |field| { + if (@field(TagType, field.name) == tag) { + deepHash(hasher, @field(key, field.name)); + break; + } + } + }, + else => unreachable, + } +} + // --------------------------------------------- // UTILITY // --------------------------------------------- @@ -2601,8 +2596,8 @@ test "anyframe type" { const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const @"anyframe->i32" = try ip.get(gpa, Key{ .anyframe_t_type = .{ .child = i32_type } }); - const @"anyframe->bool" = try ip.get(gpa, Key{ .anyframe_t_type = .{ .child = bool_type } }); + const @"anyframe->i32" = try ip.get(gpa, Key{ .anyframe_type = .{ .child = i32_type } }); + const @"anyframe->bool" = try ip.get(gpa, Key{ .anyframe_type = .{ .child = bool_type } }); try testExpectFmtType(&ip, @"anyframe->i32", "anyframe->i32"); try testExpectFmtType(&ip, @"anyframe->bool", "anyframe->bool");