From 4f180712bbf4e5603bd6d44b17b762c27090a01b Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 2 Dec 2022 00:08:45 +0100 Subject: [PATCH 01/78] add initial intern pool implementation --- src/ComptimeInterpreter.zig | 706 ++++-------- src/DocumentStore.zig | 1 + src/InternPool.zig | 2110 +++++++++++++++++++++++++++++++++++ src/Server.zig | 27 +- 4 files changed, 2329 insertions(+), 515 deletions(-) create mode 100644 src/InternPool.zig diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 587ade5..3c20aca 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -9,27 +9,26 @@ const zig = std.zig; const Ast = zig.Ast; const analysis = @import("analysis.zig"); const DocumentStore = @import("DocumentStore.zig"); -const ComptimeInterpreter = @This(); + +pub const InternPool = @import("InternPool.zig"); +pub const IPIndex = InternPool.Index; +pub const IPKey = InternPool.Key; +pub const ComptimeInterpreter = @This(); const log = std.log.scoped(.comptime_interpreter); // TODO: Investigate arena allocator: std.mem.Allocator, +arena: std.heap.ArenaAllocator, +ip: InternPool = .{}, document_store: *DocumentStore, uri: DocumentStore.Uri, -root_type: ?Type = null, +root_type: IPIndex = IPIndex.none, /// Interpreter diagnostic errors errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{}, -// TODO: Deduplicate typeinfo across different interpreters -type_info: std.ArrayListUnmanaged(TypeInfo) = .{}, -type_info_map: std.HashMapUnmanaged(TypeInfo, usize, TypeInfo.Context, std.hash_map.default_max_load_percentage) = .{}, - -// TODO: Use DOD -value_data_list: std.ArrayListUnmanaged(*ValueData) = .{}, - pub fn getHandle(interpreter: *ComptimeInterpreter) *const DocumentStore.Handle { // This interpreter is loaded from a known-valid handle so a valid handle must exist return interpreter.document_store.getOrLoadHandle(interpreter.uri).?; @@ -52,241 +51,30 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void { var err_it = interpreter.errors.iterator(); while (err_it.next()) |entry| interpreter.allocator.free(entry.value_ptr.message); - if (interpreter.root_type) |rt| rt.getTypeInfo().getScopeOfType().?.deinit(); - for (interpreter.type_info.items) |*ti| ti.deinit(interpreter.allocator); - for (interpreter.value_data_list.items) |ti| interpreter.allocator.destroy(ti); - interpreter.errors.deinit(interpreter.allocator); - interpreter.type_info.deinit(interpreter.allocator); - interpreter.type_info_map.deinit(interpreter.allocator); - interpreter.value_data_list.deinit(interpreter.allocator); + interpreter.ip.deinit(interpreter.allocator); } -pub const TypeInfo = union(enum) { - pub const Context = struct { - interpreter: ComptimeInterpreter, - hasher: *std.hash.Wyhash, - - pub fn hash(self: @This(), s: TypeInfo) u64 { - TypeInfo.hash(self, s); - return self.hasher.final(); - } - pub fn eql(self: @This(), a: TypeInfo, b: TypeInfo) bool { - _ = self; - return TypeInfo.eql(a, b); - } - }; - - pub const Signedness = enum { signed, unsigned }; - - pub const Struct = struct { - /// Declarations contained within - scope: *InterpreterScope, - fields: std.StringHashMapUnmanaged(FieldDefinition) = .{}, - }; - - pub const Int = struct { - bits: u16, - signedness: Signedness, - }; - - pub const Pointer = struct { - size: Size, - is_const: bool, - is_volatile: bool, - child: Type, - is_allowzero: bool, - - sentinel: ?*ValueData, - - pub const Size = enum { - one, - many, - slice, - c, - }; - }; - - pub const Fn = struct { - return_type: ?Type, - /// Index into interpreter.declarations - params: std.ArrayListUnmanaged(usize) = .{}, - }; - - pub const Array = struct { - len: usize, - child: Type, - - sentinel: ?*ValueData, - }; - - /// Hack to get anytype working; only valid on fnparams - @"anytype", - @"type", - @"bool", - - @"struct": Struct, - pointer: Pointer, - @"fn": Fn, - - int: Int, - @"comptime_int", - float: u16, - @"comptime_float", - - array: Array, - - pub fn eql(a: TypeInfo, b: TypeInfo) bool { - if (std.meta.activeTag(a) != std.meta.activeTag(b)) return false; - return switch (a) { - .@"struct" => false, // Struct declarations can never be equal (this is a lie, gotta fix this) - .pointer => p: { - const ap = a.pointer; - const bp = b.pointer; - break :p ap.size == bp.size and ap.is_const == bp.is_const and ap.is_volatile == bp.is_volatile and eql( - ap.child.getTypeInfo(), - bp.child.getTypeInfo(), - ) and ap.is_allowzero == bp.is_allowzero and ((ap.sentinel == null and bp.sentinel == null) or ((ap.sentinel != null and bp.sentinel != null) and ap.sentinel.?.eql(bp.sentinel.?))); - }, - .int => a.int.signedness == b.int.signedness and a.int.bits == b.int.bits, - .float => a.float == b.float, - else => return true, - }; - } - - pub fn hash(context: TypeInfo.Context, ti: TypeInfo) void { - context.hasher.update(&[_]u8{@enumToInt(ti)}); - return switch (ti) { - .@"struct" => |s| { - _ = s; - // TODO: Fix - // context.hasher.update(std.mem.sliceAsBytes(s.fields.items)); - // TODO: Fix - // context.hasher.update(std.mem.sliceAsBytes(s.declarations.items)); - }, - .pointer => |p| { - // const ap = a.pointer; - // const bp = b.pointer; - context.hasher.update(&[_]u8{ @enumToInt(p.size), @boolToInt(p.is_const), @boolToInt(p.is_volatile) }); - TypeInfo.hash(context, p.child.getTypeInfo()); - context.hasher.update(&[_]u8{@boolToInt(p.is_allowzero)}); - // TODO: Hash Sentinel - // break :p ap.size == bp.size and ap.is_const == bp.is_const and ap.is_volatile == bp.is_volatile and eql( - // source_unit, - // source_interpreter.type_info.items[ap.child.info_idx], - // source_interpreter.type_info.items[bp.child.info_idx], - // ) and ap.is_allowzero == bp.is_allowzero and ((ap.sentinel == null and bp.sentinel == null) or ((ap.sentinel != null and bp.sentinel != null) and ap.sentinel.?.eql(bp.sentinel.?))); - }, - .int => |i| { - // a.int.signedness == b.int.signedness and a.int.bits == b.int.bits; - context.hasher.update(&[_]u8{@enumToInt(i.signedness)}); - context.hasher.update(&std.mem.toBytes(i.bits)); - }, - .float => |f| context.hasher.update(&std.mem.toBytes(f)), - else => {}, - }; - } - - pub fn deinit(ti: *TypeInfo, allocator: std.mem.Allocator) void { - switch (ti.*) { - .@"struct" => |*s| s.fields.deinit(allocator), - else => {}, - } - } - - pub fn getScopeOfType(ti: TypeInfo) ?*InterpreterScope { - return switch (ti) { - .@"struct" => |s| s.scope, - else => null, - }; - } -}; - pub const Type = struct { interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - info_idx: usize, - - pub fn getTypeInfo(@"type": Type) TypeInfo { - return @"type".interpreter.type_info.items[@"type".info_idx]; - } - - /// Be careful with this; typeinfo resizes reassign pointers! - pub fn getTypeInfoMutable(@"type": Type) *TypeInfo { - return &@"type".interpreter.type_info.items[@"type".info_idx]; - } + ty: IPIndex, }; pub const Value = struct { interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - @"type": Type, - value_data: *ValueData, - - pub fn eql(value: Value, other_value: Value) bool { - return value.value_data.eql(other_value.value_data); - } -}; - -pub const ValueData = union(enum) { - // TODO: Support larger ints, floats; bigints? - - @"type": Type, - @"bool": bool, - - @"struct": struct {}, - /// This is what a pointer is; we don't need to map - /// this to anything because @ptrToInt is comptime-illegal - /// Pointer equality scares me though :( (but that's for later) - one_ptr: *ValueData, - /// Special case slice; this is extremely common at comptime so it makes sense - slice_of_const_u8: []const u8, - - unsigned_int: u64, - signed_int: i64, - /// If the int does not fit into the previous respective slots, - /// use a bit int to store it - big_int: std.math.big.int.Managed, - - float: f64, - - @"fn", - runtime, - comptime_undetermined, - - pub fn eql(data: *ValueData, other_data: *ValueData) bool { - if (std.meta.activeTag(data.*) != std.meta.activeTag(other_data.*)) return false; - // std.enums. - // std.meta.activeTag(u: anytype) - switch (data.*) { - .@"bool" => return data.@"bool" == other_data.@"bool", - .big_int => return data.big_int.eq(other_data.big_int), - .unsigned_int => return data.unsigned_int == other_data.unsigned_int, - .signed_int => return data.signed_int == other_data.signed_int, - .float => return data.float == other_data.float, - - else => return false, - } - } - - /// Get the bit count required to store a certain integer - pub fn bitCount(data: ValueData) ?u16 { - return switch (data) { - // TODO: Implement for signed ints - .unsigned_int => |i| if (i == 0) 0 else std.math.log2_int_ceil(@TypeOf(i), i + 1), - .big_int => |bi| @intCast(u16, bi.bitCountAbs()), - else => null, - }; - } + ty: IPIndex, + val: IPIndex, }; pub const FieldDefinition = struct { node_idx: Ast.Node.Index, /// Store name so tree doesn't need to be used to access field name name: []const u8, - @"type": Type, + ty: Type, default_value: ?Value, }; @@ -332,15 +120,16 @@ pub const Declaration = struct { if (var_decl.ast.type_node != 0) { var type_val = try (try interpreter.interpret(var_decl.ast.type_node, decl.scope, .{})).getValue(); - if (type_val.@"type".getTypeInfo() != .@"type") { + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + if (type_val.ty != type_type) { try interpreter.recordError( decl.node_idx, "expected_type", - std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{s}'", .{interpreter.formatTypeInfo(type_val.@"type".getTypeInfo())}) catch return error.CriticalAstFailure, + std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{type_val.ty.fmtType(&interpreter.ip)}) catch return error.CriticalAstFailure, ); return error.InvalidCast; } - value = try interpreter.cast(var_decl.ast.type_node, type_val.value_data.@"type", value); + value = try interpreter.cast(var_decl.ast.type_node, type_val.value_data.type, value); } decl.value = value; @@ -367,117 +156,6 @@ pub const Declaration = struct { } }; -pub fn createType(interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, type_info: TypeInfo) std.mem.Allocator.Error!Type { - // TODO: Figure out dedup - var hasher = std.hash.Wyhash.init(0); - var gpr = try interpreter.type_info_map.getOrPutContext(interpreter.allocator, type_info, .{ .interpreter = interpreter.*, .hasher = &hasher }); - - if (gpr.found_existing) { - return Type{ .interpreter = interpreter, .node_idx = node_idx, .info_idx = gpr.value_ptr.* }; - } else { - try interpreter.type_info.append(interpreter.allocator, type_info); - const info_idx = interpreter.type_info.items.len - 1; - gpr.value_ptr.* = info_idx; - return Type{ .interpreter = interpreter, .node_idx = node_idx, .info_idx = info_idx }; - } -} - -pub fn createValueData(interpreter: *ComptimeInterpreter, data: ValueData) error{OutOfMemory}!*ValueData { - var vd = try interpreter.allocator.create(ValueData); - try interpreter.value_data_list.append(interpreter.allocator, vd); - vd.* = data; - return vd; -} - -pub const TypeInfoFormatter = struct { - interpreter: *const ComptimeInterpreter, - ti: TypeInfo, - - pub fn format(value: TypeInfoFormatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - return switch (value.ti) { - .int => |ii| switch (ii.signedness) { - .signed => try writer.print("i{d}", .{ii.bits}), - .unsigned => try writer.print("u{d}", .{ii.bits}), - }, // TODO - .float => |f| try writer.print("f{d}", .{f}), - .@"comptime_int" => try writer.writeAll("comptime_int"), - .@"comptime_float" => try writer.writeAll("comptime_float"), - .@"type" => try writer.writeAll("type"), - .@"bool" => try writer.writeAll("bool"), - .@"struct" => |s| { - try writer.writeAll("struct {"); - var field_iterator = s.fields.iterator(); - while (field_iterator.next()) |di| { - try writer.print("{s}: {s}, ", .{ di.key_ptr.*, value.interpreter.formatTypeInfo(di.value_ptr.*.@"type".getTypeInfo()) }); - } - - var iterator = s.scope.declarations.iterator(); - while (iterator.next()) |di| { - const decl = di.value_ptr; - if (decl.isConstant()) { - if (decl.value) |sv| { - try writer.print("const {s}: {any} = { }, ", .{ - decl.name, - value.interpreter.formatTypeInfo(sv.@"type".getTypeInfo()), - value.interpreter.formatValue(sv), - }); - } else { - try writer.print("const {s} (not analyzed), ", .{decl.name}); - } - } else { - if (decl.value) |sv| { - try writer.print("var {s}: {any} = { }, ", .{ - decl.name, - value.interpreter.formatTypeInfo(sv.@"type".getTypeInfo()), - value.interpreter.formatValue(sv), - }); - } else { - try writer.print("var {s} (not analyzed), ", .{decl.name}); - } - } - } - try writer.writeAll("}"); - }, - else => try writer.print("UnimplementedTypeInfoPrint", .{}), - }; - } -}; - -pub fn formatTypeInfo(interpreter: *const ComptimeInterpreter, ti: TypeInfo) TypeInfoFormatter { - return TypeInfoFormatter{ .interpreter = interpreter, .ti = ti }; -} - -pub const ValueFormatter = struct { - interpreter: *const ComptimeInterpreter, - val: Value, - - pub fn format(form: ValueFormatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - - var value = form.val; - var ti = value.@"type".getTypeInfo(); - - return switch (ti) { - .int, .@"comptime_int" => switch (value.value_data.*) { - .unsigned_int => |a| try writer.print("{d}", .{a}), - .signed_int => |a| try writer.print("{d}", .{a}), - .big_int => |a| try writer.print("{d}", .{a}), - - else => unreachable, - }, - .@"type" => try writer.print("{ }", .{form.interpreter.formatTypeInfo(value.value_data.@"type".getTypeInfo())}), - else => try writer.print("UnimplementedValuePrint", .{}), - }; - } -}; - -pub fn formatValue(interpreter: *const ComptimeInterpreter, value: Value) ValueFormatter { - return ValueFormatter{ .interpreter = interpreter, .val = value }; -} - // pub const Comptimeness = enum { @"comptime", runtime }; pub const InterpreterScope = struct { @@ -671,14 +349,14 @@ pub fn cast( const value_data = value.value_data; const to_type_info = dest_type.getTypeInfo(); - const from_type_info = value.@"type".getTypeInfo(); + const from_type_info = value.type.getTypeInfo(); // TODO: Implement more implicit casts if (from_type_info.eql(to_type_info)) return value; const err = switch (from_type_info) { - .@"comptime_int" => switch (to_type_info) { + .comptime_int => switch (to_type_info) { .int => { if (value_data.bitCount().? > to_type_info.int.bits) { switch (value_data.*) { @@ -705,7 +383,7 @@ pub fn cast( .interpreter = interpreter, .node_idx = node_idx, - .@"type" = dest_type, + .type = dest_type, .value_data = value.value_data, }; } @@ -753,18 +431,17 @@ pub fn interpret( .error_set_decl, => { var container_scope = try interpreter.newScope(scope, node_idx); - var type_info = TypeInfo{ - .@"struct" = .{ - .scope = container_scope, - }, - }; - var cont_type = try interpreter.createType(node_idx, type_info); - if (node_idx == 0) interpreter.root_type = cont_type; + var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){}; + errdefer fields.deinit(interpreter.allocator); + + // if (node_idx == 0) interpreter.root_type = cont_type; var buffer: [2]Ast.Node.Index = undefined; const members = ast.declMembers(tree, node_idx, &buffer); + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + for (members) |member| { const maybe_container_field: ?zig.Ast.full.ContainerField = switch (tags[member]) { .container_field => tree.containerField(member), @@ -775,45 +452,59 @@ pub fn interpret( if (maybe_container_field) |field_info| { var init_type_value = try (try interpreter.interpret(field_info.ast.type_expr, container_scope, .{})).getValue(); + var default_value = if (field_info.ast.value_expr == 0) - null + IPIndex.none else - try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue(); + (try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty - if (init_type_value.@"type".getTypeInfo() != .@"type") { + if (init_type_value.ty != type_type) { try interpreter.recordError( field_info.ast.type_expr, "expected_type", - try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{s}'", .{interpreter.formatTypeInfo(init_type_value.@"type".getTypeInfo())}), + try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(&interpreter.ip)}), ); continue; } - const name = tree.tokenSlice(field_info.ast.name_token); - const field = FieldDefinition{ - .node_idx = member, - .name = name, - .@"type" = init_type_value.value_data.@"type", + const name = tree.tokenSlice(field_info.ast.main_token); + + const field: InternPool.Struct.Field = .{ + .ty = init_type_value.val, .default_value = default_value, - // TODO: Default values - // .@"type" = T: { - // var value = (try interpreter.interpret(field_info.ast.type_expr, scope_idx, true)).?.value; - // break :T @ptrCast(*Type, @alignCast(@alignOf(*Type), value)).*; - // }, - // .value = null, + .alignent = 0, // TODO, + .is_comptime = false, // TODO }; - try cont_type.getTypeInfoMutable().@"struct".fields.put(interpreter.allocator, name, field); + try fields.put(interpreter.arena.allocator(), name, field); } else { _ = try interpreter.interpret(member, container_scope, options); } } + const namespace = try interpreter.ip.get(interpreter.allocator, IPKey{ + .namespace = .{ + .parent = IPIndex.none, + .ty = undefined, // TODO + .decls = undefined, // TODO, + .usingnamespaces = .{}, + }, + }); + + const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{ + .struct_type = .{ + .fields = fields, + .namespace = namespace, // TODO + .layout = std.builtin.Type.ContainerLayout.Auto, // TODO + .backing_int_ty = IPIndex.none, // TODO + }, + }); + return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ .@"type" = cont_type }), + .ty = type_type, + .val = struct_type, } }; }, .global_var_decl, @@ -901,34 +592,29 @@ pub fn interpret( if (std.mem.eql(u8, "bool", value)) return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ .@"type" = try interpreter.createType(node_idx, .{ .@"bool" = {} }) }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), } }; if (std.mem.eql(u8, "true", value)) return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"bool" = {} }), - .value_data = try interpreter.createValueData(.{ .@"bool" = true }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }), } }; if (std.mem.eql(u8, "false", value)) return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"bool" = {} }), - .value_data = try interpreter.createValueData(.{ .@"bool" = false }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }), } }; if (value.len == 5 and (value[0] == 'u' or value[0] == 'i') and std.mem.eql(u8, "size", value[1..])) return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ - .@"type" = try interpreter.createType(node_idx, .{ - .int = .{ - .signedness = if (value[0] == 'u') .unsigned else .signed, - .bits = 64, // TODO: Platform specific - }, - }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ + .simple = if (value[0] == 'u') .usize else .isize, }), }, }; @@ -937,20 +623,18 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }) }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), } }; } else if (value.len >= 2 and (value[0] == 'u' or value[0] == 'i')) int: { return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ .@"type" = try interpreter.createType(node_idx, .{ - .int = .{ - .signedness = if (value[0] == 'u') .unsigned else .signed, - .bits = std.fmt.parseInt(u16, value[1..], 10) catch break :int, - }, - }) }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{ + .signedness = if (value[0] == 'u') .unsigned else .signed, + .bits = std.fmt.parseInt(u16, value[1..], 10) catch break :int, + } }), } }; } @@ -973,7 +657,7 @@ pub fn interpret( var ir = try interpreter.interpret(data[node_idx].lhs, scope, options); var irv = try ir.getValue(); - var sub_scope = irv.value_data.@"type".getTypeInfo().getScopeOfType() orelse return error.IdentifierNotFound; + var sub_scope = irv.value_data.type.getTypeInfo().getScopeOfType() orelse return error.IdentifierNotFound; var scope_sub_decl = sub_scope.interpreter.huntItDown(sub_scope, rhs_str, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( node_idx, @@ -1008,7 +692,7 @@ pub fn interpret( // TODO: Don't evaluate runtime ifs // if (options.observe_values) { const ir = try interpreter.interpret(iff.ast.cond_expr, scope, options); - if ((try ir.getValue()).value_data.@"bool") { + if ((try ir.getValue()).value_data.bool) { return try interpreter.interpret(iff.ast.then_expr, scope, options); } else { if (iff.ast.else_expr != 0) { @@ -1019,35 +703,48 @@ pub fn interpret( .equal_equal => { var a = try interpreter.interpret(data[node_idx].lhs, scope, options); var b = try interpreter.interpret(data[node_idx].rhs, scope, options); - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"bool" = {} }), - .value_data = try interpreter.createValueData(.{ .@"bool" = (try a.getValue()).eql(try b.getValue()) }), - } }; + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .val = try interpreter.createValueData(.{ .bool = (try a.getValue()).eql(try b.getValue()) }), // TODO + }, + }; // a.getValue().eql(b.getValue()) }, .number_literal => { const s = tree.getNodeSource(node_idx); const nl = std.zig.parseNumberLiteral(s); - return InterpretResult{ - .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"comptime_int" = {} }), - .value_data = try interpreter.createValueData(switch (nl) { - .float => .{ .float = try std.fmt.parseFloat(f64, s) }, - .int => if (s[0] == '-') ValueData{ .signed_int = try std.fmt.parseInt(i64, s, 0) } else ValueData{ .unsigned_int = try std.fmt.parseInt(u64, s, 0) }, - .big_int => |bii| ppp: { - var bi = try std.math.big.int.Managed.init(interpreter.allocator); - try bi.setString(@enumToInt(bii), s[if (bii != .decimal) @as(usize, 2) else @as(usize, 0)..]); - break :ppp .{ .big_int = bi }; - }, - .failure => return error.CriticalAstFailure, - }), + if (nl == .failure) return error.CriticalAstFailure; + + const comptime_int_type = try interpreter.ip.get(interpreter.allocator, IPKey{ + .simple = if (nl == .float) .comptime_float else .comptime_int, + }); + + const value = try interpreter.ip.get( + interpreter.allocator, + switch (nl) { + .float => IPKey{ + .float_64_value = try std.fmt.parseFloat(f64, s), // shouldn't this be f128? + }, + .int => if (s[0] == '-') IPKey{ + .int_i64_value = try std.fmt.parseInt(i64, s, 0), + } else IPKey{ + .int_u64_value = try std.fmt.parseInt(u64, s, 0), + }, + .big_int => @panic("TODO: implement big int"), + .failure => return error.CriticalAstFailure, }, - }; + ); + + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = comptime_int_type, + .val = value, + } }; }, .assign, .assign_bit_and, @@ -1075,7 +772,7 @@ pub fn interpret( var to_value = try ir.getValue(); var from_value = (try (try interpreter.interpret(data[node_idx].rhs, scope.?, options)).getValue()); - to_value.value_data.* = (try interpreter.cast(node_idx, to_value.@"type", from_value)).value_data.*; + to_value.value_data.* = (try interpreter.cast(node_idx, to_value.type, from_value)).value_data.*; return InterpretResult{ .nothing = {} }; }, @@ -1113,7 +810,7 @@ pub fn interpret( try writer.writeAll("indeterminate"); continue; }; - try writer.print("@as({s}, {s})", .{ interpreter.formatTypeInfo(value.@"type".getTypeInfo()), interpreter.formatValue(value) }); + try writer.print("@as({s}, {s})", .{ interpreter.formatTypeInfo(value.type.getTypeInfo()), interpreter.formatValue(value) }); if (index != params.len - 1) try writer.writeAll(", "); } @@ -1142,8 +839,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"struct" = .{ .scope = try interpreter.newScope(null, 0) } }), - .value_data = try interpreter.createValueData(.{ .@"struct" = .{} }), + .ty = try interpreter.createType(node_idx, .{ .@"struct" = .{ .scope = try interpreter.newScope(null, 0) } }), + .val = try interpreter.createValueData(.{ .@"struct" = .{} }), } }; } @@ -1156,8 +853,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ .@"type" = handle.interpreter.?.root_type.? }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = handle.interpreter.?.root_type.? }), } }; } @@ -1168,8 +865,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"type" = {} }), - .value_data = try interpreter.createValueData(.{ .@"type" = value.@"type" }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = value.ty }), } }; } @@ -1179,17 +876,19 @@ pub fn interpret( const value = try (try interpreter.interpret(params[0], scope, options)).getValue(); const field_name = try (try interpreter.interpret(params[1], scope, options)).getValue(); - if (value.@"type".getTypeInfo() != .@"type") return error.InvalidBuiltin; - if (field_name.@"type".getTypeInfo() != .@"pointer") return error.InvalidBuiltin; // Check if it's a []const u8 + if (value.type.getTypeInfo() != .type) return error.InvalidBuiltin; + if (field_name.type.getTypeInfo() != .pointer) return error.InvalidBuiltin; // Check if it's a []const u8 - const ti = value.value_data.@"type".getTypeInfo(); + const ti = value.value_data.type.getTypeInfo(); if (ti.getScopeOfType() == null) return error.InvalidBuiltin; + const has_decl = ti.getScopeOfType().?.declarations.contains(field_name.value_data.slice_of_const_u8); + return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ .@"bool" = {} }), - .value_data = try interpreter.createValueData(.{ .@"bool" = ti.getScopeOfType().?.declarations.contains(field_name.value_data.slice_of_const_u8) }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (has_decl) .bool_true else .bool_false }), } }; } @@ -1199,37 +898,39 @@ pub fn interpret( const as_type = try (try interpreter.interpret(params[0], scope, options)).getValue(); const value = try (try interpreter.interpret(params[1], scope, options)).getValue(); - if (as_type.@"type".getTypeInfo() != .@"type") return error.InvalidBuiltin; + if (as_type.type.getTypeInfo() != .type) return error.InvalidBuiltin; - return InterpretResult{ .value = try interpreter.cast(node_idx, as_type.value_data.@"type", value) }; + return InterpretResult{ .value = try interpreter.cast(node_idx, as_type.value_data.type, value) }; } log.err("Builtin not implemented: {s}", .{call_name}); return error.InvalidBuiltin; }, .string_literal => { - const value = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1]; + const str = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1]; + + const string_literal_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{ + .elem_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .array_type = .{ + .child = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{ + .signedness = .unsigned, + .bits = 8, + } }), + .sentinel = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_u64_value = 0 }), + } }), + .sentinel = .none, + .alignment = 0, + .size = .one, + .is_const = true, + .is_volatile = false, + .is_allowzero = false, + .address_space = .generic, + } }); + var val = Value{ .interpreter = interpreter, .node_idx = node_idx, - // TODO: This is literally the wrong type lmao - // the actual type is *[len:0]u8 because we're pointing - // to a fixed size value in the data(?) section (when we're compilign zig code) - .@"type" = try interpreter.createType(node_idx, .{ - .pointer = .{ - .size = .one, - .is_const = true, - .is_volatile = false, - .child = try interpreter.createType(0, .{ .int = .{ - .bits = 8, - .signedness = .unsigned, - } }), - .is_allowzero = false, - - .sentinel = null, - }, - }), - .value_data = try interpreter.createValueData(.{ .slice_of_const_u8 = value }), + .ty = string_literal_type, + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), // TODO }; // TODO: Add type casting, sentinel @@ -1250,13 +951,16 @@ pub fn interpret( // var buf: [1]Ast.Node.Index = undefined; // const func = ast.fnProto(tree, node_idx, &buf).?; - // TODO: Add params + // TODO: Resolve function type - var type_info = TypeInfo{ - .@"fn" = .{ - .return_type = null, - }, - }; + const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ + .calling_convention = .Unspecified, + .alignment = 0, + .is_generic = false, + .is_var_args = false, + .return_type = IPIndex.none, + .args = .{}, + } }); // var it = func.iterate(&tree); // while (ast.nextFnParam(&it)) |param| { @@ -1285,8 +989,8 @@ pub fn interpret( var value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, type_info), - .value_data = try interpreter.createValueData(.{ .@"fn" = {} }), + .ty = function_type, + .value_data = IPIndex.none, // TODO }; const name = analysis.getDeclName(tree, node_idx).?; @@ -1294,7 +998,7 @@ pub fn interpret( .scope = scope.?, .node_idx = node_idx, .name = name, - .@"value" = value, + .value = value, }); return InterpretResult{ .nothing = {} }; @@ -1333,16 +1037,25 @@ pub fn interpret( }, .bool_not => { const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); - const value = (try result.getValue()); - if (value.value_data.* != .@"bool") return error.InvalidOperation; - return InterpretResult{ - .value = .{ - .interpreter = interpreter, - .node_idx = node_idx, - .@"type" = value.@"type", - .value_data = try interpreter.createValueData(.{ .@"bool" = !value.value_data.@"bool" }), - }, - }; + const bool_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }); + const value = try result.getValue(); + if (value.type == bool_type) { + const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); + const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); + + const not_value = if (value.value == false_value) true_value else if (value.value == true_value) false_value else return error.InvalidOperation; + return InterpretResult{ + .value = .{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = bool_type, + .val = not_value, + }, + }; + } else { + // TODO + return error.InvalidOperation; + } }, .address_of => { // TODO: Make const pointers if we're drawing from a const; @@ -1351,46 +1064,43 @@ pub fn interpret( const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); const value = (try result.getValue()); - return InterpretResult{ - .value = .{ - .interpreter = interpreter, - .node_idx = node_idx, - .@"type" = try interpreter.createType(node_idx, .{ - .pointer = .{ - .size = .one, - .is_const = false, - .is_volatile = false, - .child = value.@"type", - .is_allowzero = false, + const pointer_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{ + .elem_type = value.type, + .sentinel = .none, + .alignment = 0, + .size = .one, + .is_const = false, + .is_volatile = false, + .is_allowzero = false, + .address_space = .generic, + } }); - .sentinel = null, - }, - }), - .value_data = try interpreter.createValueData(.{ .@"one_ptr" = value.value_data }), - }, - }; + return InterpretResult{ .value = .{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = pointer_type, + .val = try interpreter.createValueData(.{ .one_ptr = value.value_data }), + } }; }, .deref => { const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); const value = (try result.getValue()); - const ti = value.@"type".getTypeInfo(); + const type_key = interpreter.ip.indexToKey(value.ty); - if (ti != .pointer) { + if (type_key != .pointer) { try interpreter.recordError(node_idx, "invalid_deref", try std.fmt.allocPrint(interpreter.allocator, "cannot deference non-pointer", .{})); return error.InvalidOperation; } // TODO: Check if this is a one_ptr or not - return InterpretResult{ - .value = .{ - .interpreter = interpreter, - .node_idx = node_idx, - .@"type" = ti.pointer.child, - .value_data = value.value_data.one_ptr, - }, - }; + return InterpretResult{ .value = .{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = type_key.pointer_type.elem_type, + .val = value.value_data.one_ptr, + } }; }, else => { log.err("Unhandled {any}", .{tags[node_idx]}); @@ -1434,11 +1144,11 @@ pub fn call( while (ast.nextFnParam(&arg_it)) |param| { if (arg_index >= arguments.len) return error.MissingArguments; var tex = try (try interpreter.interpret(param.type_expr, fn_scope, options)).getValue(); - if (tex.@"type".getTypeInfo() != .@"type") { + if (tex.type.getTypeInfo() != .type) { try interpreter.recordError( param.type_expr, "expected_type", - std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{s}'", .{interpreter.formatTypeInfo(tex.@"type".getTypeInfo())}) catch return error.CriticalAstFailure, + std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{s}'", .{interpreter.formatTypeInfo(tex.type.getTypeInfo())}) catch return error.CriticalAstFailure, ); return error.InvalidCast; } @@ -1447,7 +1157,7 @@ pub fn call( .scope = fn_scope, .node_idx = param.type_expr, .name = tree.tokenSlice(nt), - .value = try interpreter.cast(arguments[arg_index].node_idx, tex.value_data.@"type", arguments[arg_index]), + .value = try interpreter.cast(arguments[arg_index].node_idx, tex.value_data.type, arguments[arg_index]), }; try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl); arg_index += 1; @@ -1462,7 +1172,7 @@ pub fn call( .scope = fn_scope, .result = switch (result) { .@"return", .nothing => .{ .nothing = {} }, // nothing could be due to an error - .@"return_with_value" => |v| .{ .value = v }, + .return_with_value => |v| .{ .value = v }, else => @panic("bruh"), }, }; diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 2b82bfe..8690dfc 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -943,6 +943,7 @@ pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void { var int = try self.allocator.create(ComptimeInterpreter); int.* = ComptimeInterpreter{ .allocator = self.allocator, + .arena = std.heap.ArenaAllocator.init(self.allocator), .document_store = self, .uri = uri, }; diff --git a/src/InternPool.zig b/src/InternPool.zig new file mode 100644 index 0000000..0f56419 --- /dev/null +++ b/src/InternPool.zig @@ -0,0 +1,2110 @@ +/// Based on src/InternPool.zig from the zig codebase +/// https://github.com/ziglang/zig/blob/master/src/InternPool.zig +map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, +items: std.MultiArrayList(Item) = .{}, +extra: std.ArrayListUnmanaged(u8) = .{}, + +const InternPool = @This(); +const std = @import("std"); +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 { + signedness: std.builtin.Signedness, + bits: u16, +}; + +pub const Pointer = struct { + elem_type: Index, + sentinel: Index, + alignment: u16, + size: std.builtin.Type.Pointer.Size, + is_const: bool, + is_volatile: bool, + is_allowzero: bool, + address_space: std.builtin.AddressSpace, +}; + +pub const Array = struct { + // TODO support big int + len: u32, + child: Index, + sentinel: Index, +}; + +pub const Struct = struct { + fields: std.StringArrayHashMapUnmanaged(Field), + /// always points to Namespace + namespace: Index, + layout: std.builtin.Type.ContainerLayout, + backing_int_ty: Index, + + pub const Field = struct { + ty: Index, + default_value: Index, + alignent: u16, + is_comptime: bool, + }; +}; + +pub const Optional = struct { + payload_type: Index, +}; + +pub const ErrorUnion = struct { + error_set_type: Index, + payload_type: Index, +}; + +// pub const Error = struct { +// name: []const u8, +// }; + +pub const ErrorSet = struct { + /// must be sorted + names: std.StringArrayHashMapUnmanaged(void), + + pub fn sort(self: *ErrorSet) void { + const Context = struct { + keys: [][]const u8, + pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { + return std.mem.lessThan(u8, ctx.keys[a_index], ctx.keys[b_index]); + } + }; + self.names.sort(Context{ .keys = self.names.keys() }); + } +}; + +pub const Enum = struct { + tag_type: Index, + fields: std.StringArrayHashMapUnmanaged(Index), + /// this always points to Namespace + namespace: Index, + tag_type_infered: bool, +}; + +pub const Fn = struct { + calling_convention: std.builtin.CallingConvention, + alignment: u16, + is_generic: bool, + is_var_args: bool, + return_type: Index, + args: []const Param, + + pub const Param = struct { + is_comptime: bool, + is_generic: bool, + is_noalias: bool, + arg_type: Index, + }; +}; + +pub const Union = struct { + tag_type: Index, + fields: std.StringArrayHashMapUnmanaged(Field), + /// always points to Namespace + namespace: Index, + layout: std.builtin.Type.ContainerLayout, + + pub const Field = struct { + ty: Index, + alignment: u16, + }; +}; + +pub const Tuple = struct { + types: []Index, + /// unreachable_value elements are used to indicate runtime-known. + values: []Index, +}; + +pub const Vector = struct { + // TODO support big int + len: u32, + child: Index, +}; + +pub const BigInt = std.math.big.int.Const; + +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 Namespace = struct { + /// always points to Namespace or Index.none + parent: Index, + /// Will be a struct, enum, union, or opaque. + ty: Index, + /// always points to Decl + decls: []const Index, + usingnamespaces: []const Index, +}; + +pub const Bytes = struct { + data: []const u8, +}; + +pub const Key = union(enum) { + simple: Simple, + + int_type: Int, + pointer_type: Pointer, + array_type: Array, + 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, + + declaration: Decl, + namespace: Namespace, + + int_u64_value: u64, + int_i64_value: i64, + int_big_value: BigInt, + float_16_value: f16, + float_32_value: f32, + float_64_value: f64, + float_80_value: f80, + float_128_value: f128, + type_value: Index, + + bytes: Bytes, + + // slice + // error + // error union + // optional + // aggregate + // union + + pub fn hash(key: Key) u32 { + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, std.meta.activeTag(key)); + switch (key) { + .struct_type => |struct_info| { + var field_it = struct_info.fields.iterator(); + while (field_it.next()) |item| { + hasher.update(item.key_ptr.*); + std.hash.autoHash(&hasher, item.value_ptr.*); + } + std.hash.autoHash(&hasher, struct_info.layout); + }, + // .error_type => |error_info| hasher.update(error_info.name), + .error_set_type => |error_set_info| { + const names = error_set_info.names.keys(); + std.debug.assert(std.sort.isSorted([]const u8, names, u8, std.mem.lessThan)); + for (names) |error_name| { + hasher.update(error_name); + } + }, + .enum_type => |enum_info| { + std.hash.autoHash(&hasher, enum_info.tag_type); + var field_it = enum_info.fields.iterator(); + while (field_it.next()) |item| { + hasher.update(item.key_ptr.*); + std.hash.autoHash(&hasher, item.value_ptr.*); + } + std.hash.autoHash(&hasher, enum_info.tag_type_infered); + }, + .function_type => |function_info| std.hash.autoHashStrat(&hasher, function_info, .Deep), + .union_type => |union_info| { + std.hash.autoHash(&hasher, union_info.tag_type); + var field_it = union_info.fields.iterator(); + while (field_it.next()) |item| { + hasher.update(item.key_ptr.*); + std.hash.autoHash(&hasher, item.value_ptr.*); + } + std.hash.autoHash(&hasher, union_info.layout); + }, + .tuple_type => |tuple_info| std.hash.autoHashStrat(&hasher, tuple_info, .Deep), + .declaration => |decl_info| std.hash.autoHashStrat(&hasher, decl_info, .Deep), + .namespace => |namespace_info| std.hash.autoHashStrat(&hasher, namespace_info, .Deep), + .int_big_value => |big_int| { + std.hash.autoHash(&hasher, big_int.positive); + hasher.update(std.mem.sliceAsBytes(big_int.limbs)); + }, + .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.autoHash(&hasher, info), // 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.count() != b.struct_type.fields.count()) return false; + @panic("TODO: implement field equality check"); + }, + // .error_type => |error_info| std.mem.eql(u8, error_info.name, b.error_type.name), + .error_set_type => |error_set_info| { + const a_names = error_set_info.names.keys(); + const b_names = b.error_set_type.names.keys(); + + if (a_names.len != b_names.len) return false; + for (a_names) |a_name, i| { + const b_name = b_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.count() != b.enum_type.fields.count()) 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_info.tag_type) return false; + if (union_info.layout != b.union_info.layout) return false; + if (union_info.fields.count() != b.union_info.fields.count()) return false; + @panic("TODO: implement union equality"); + }, + .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; + }, + .declaration => |decl_info| { + if (!std.mem.eql(u8, decl_info.name, b.declaration.name)) return false; + if (decl_info.ty != b.declaration.ty) return false; + if (decl_info.val != b.declaration.val) return false; + if (decl_info.alignment != b.declaration.alignment) return false; + if (decl_info.address_space != b.declaration.address_space) return false; + if (decl_info.is_pub != b.declaration.is_pub) return false; + if (decl_info.is_exported != b.declaration.is_exported) return false; + return true; + }, + .namespace => |namespace_info| { + if (!std.meta.eql(namespace_info.parent, b.namespace.parent)) return false; + if (namespace_info.ty != b.namespace.ty) return false; + + if (namespace_info.decls.len != b.namespace.decls.len) return false; + if (namespace_info.usingnamespaces.len != b.namespace.usingnamespaces.len) return false; + + for (namespace_info.decls) |decl, i| { + if (!decl != b.namespace.decls[i]) return false; + } + for (namespace_info.usingnamespaces) |namespace, i| { + if (!namespace != b.namespace.usingnamespaces[i]) return false; + } + return false; + }, + 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, + }, + .pointer_type => .type_pointer, + .array_type => .type_array, + .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, + + .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, + .int_big_value => |big_int| if (big_int.positive) .int_big_positive else .int_big_negative, + .float_16_value => .float_f16, + .float_32_value => .float_f32, + .float_64_value => .float_f64, + .float_80_value => .float_f80, + .float_128_value => .float_f128, + .type_value => .type, + }; + } + + pub fn zigTypeTag(key: Key) std.builtin.TypeId { + return switch (key) { + .simple => |simple| switch (simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + .c_longdouble, + => .Float, + + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + => .Int, + + .comptime_int => .ComptimeInt, + .comptime_float => .ComptimeFloat, + + .anyopaque => .Opaque, + .bool => .Bool, + .void => .Void, + .type => .Type, + .anyerror => .ErrorSet, + .noreturn => .NoReturn, + .@"anyframe" => .AnyFrame, + .null_type => .Null, + .undefined_type => .Undefined, + .enum_literal_type => .EnumLiteral, + + .undefined_value, + .void_value, + .unreachable_value, + .null_value, + .bool_true, + .bool_false, + => unreachable, + }, + }; + } + + /// Asserts the type is an integer, enum, error set, packed struct, or vector of one of them. + pub fn intInfo(ty: Key, target: std.Target, ip: InternPool) Int { + var key: Key = ty; + + while (true) switch (key) { + .simple => |simple| switch (simple) { + .usize => return .{ .signdness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, + .isize => return .{ .signdness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, + + // TODO correctly resolve size based on `target` + .c_short => return .{ .signdness = .signed, .bits = @bitSizeOf(c_short) }, + .c_ushort => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_ushort) }, + .c_int => return .{ .signdness = .signed, .bits = @bitSizeOf(c_int) }, + .c_uint => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_uint) }, + .c_long => return .{ .signdness = .signed, .bits = @bitSizeOf(c_long) }, + .c_ulong => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_ulong) }, + .c_longlong => return .{ .signdness = .signed, .bits = @bitSizeOf(c_longlong) }, + .c_ulonglong => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_ulonglong) }, + .c_longdouble => return .{ .signdness = .signed, .bits = @bitSizeOf(c_longdouble) }, + + // TODO revisit this when error sets support custom int types (comment taken from zig codebase) + .anyerror => return .{ .signedness = .unsigned, .bits = 16 }, + }, + .int_type => |int_info| return int_info, + .enum_type => @panic("TODO"), + .struct_type => |struct_info| { + std.debug.assert(struct_info.layout == .Packed); + key = struct_info.backing_int_ty; + }, + // TODO revisit this when error sets support custom int types (comment taken from zig codebase) + .error_set_type => return .{ .signedness = .unsigned, .bits = 16 }, + .vector_type => |vector_info| { + std.debug.assert(vector_info.len == 1); + key = ip.indexToKey(vector_info.child); + }, + + else => unreachable, + }; + } + + /// Asserts the type is a fixed-size float or comptime_float. + /// Returns 128 for comptime_float types. + pub fn floatBits(ty: Key, target: std.Target) u16 { + std.debug.assert(ty == .simple); + _ = target; + return switch (ty.simple) { + .f16 => 16, + .f32 => 32, + .f64 => 64, + .f80 => 80, + .f128, .comptime_float => 128, + // TODO correctly resolve size based on `target` + .c_longdouble => 80, + + else => unreachable, + }; + } + + pub fn isCType(ty: Key) bool { + return switch (ty) { + .simple => |simple| switch (simple) { + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + => true, + else => false, + }, + else => false, + }; + } + + pub fn isSlice(ty: Key) bool { + return switch (ty) { + .pointer_type => |pointer_info| pointer_info.size == .Slice, + else => false, + }; + } + + pub const TypeFormatContext = struct { + ty: Index, + options: FormatOptions = .{}, + ip: *const InternPool, + }; + + pub const ValueFormatContext = struct { + value: Index, + ty: Index, + options: FormatOptions = .{}, + ip: *const InternPool, + }; + + // TODO implement options + pub const FormatOptions = struct { + include_fields: bool = true, + include_declarations: bool = true, + }; + + pub fn formatType( + ctx: TypeFormatContext, + comptime unused_format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + comptime assert(unused_format_string.len == 0); + _ = options; + return printType(ctx.ty, ctx.ip, writer); + } + + pub fn printType(ty: Index, ip: *const InternPool, writer: anytype) @TypeOf(writer).Error!void { + const key: Key = ip.indexToKey(ty); + switch (key) { + .simple => |simple| switch (simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .anyopaque, + .bool, + .void, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .noreturn, + .@"anyframe", + => try writer.writeAll(@tagName(simple)), + + .null_type => try writer.writeAll("@TypeOf(null)"), + .undefined_type => try writer.writeAll("@TypeOf(undefined)"), + .enum_literal_type => try writer.writeAll("@TypeOf(.enum_literal)"), + + .undefined_value, + .void_value, + .unreachable_value, + .null_value, + .bool_true, + .bool_false, + => unreachable, + }, + .int_type => |int_info| switch (int_info.signedness) { + .signed => try writer.print("i{}", .{int_info.bits}), + .unsigned => try writer.print("u{}", .{int_info.bits}), + }, + .pointer_type => |pointer_info| { + if (pointer_info.sentinel != Index.none) { + switch (pointer_info.size) { + .One, .C => unreachable, + .Many => try writer.print("[*:{}]", .{pointer_info.sentinel.fmtValue(pointer_info.elem_type, ip)}), + .Slice => try writer.print("[:{}]", .{pointer_info.sentinel.fmtValue(pointer_info.elem_type, ip)}), + } + } else switch (pointer_info.size) { + .One => try writer.writeAll("*"), + .Many => try writer.writeAll("[*]"), + .C => try writer.writeAll("[*c]"), + .Slice => try writer.writeAll("[]"), + } + + if (pointer_info.alignment != 0) { + try writer.print("align({d}", .{pointer_info.alignment}); + + // TODO bit offset + // TODO host size + try writer.writeAll(") "); + } + + if (pointer_info.address_space != .generic) { + try writer.print("addrspace(.{s}) ", .{@tagName(pointer_info.address_space)}); + } + + if (pointer_info.is_const) try writer.writeAll("const "); + if (pointer_info.is_volatile) try writer.writeAll("volatile "); + if (pointer_info.is_allowzero and pointer_info.size != .C) try writer.writeAll("allowzero "); + + try printType(pointer_info.elem_type, ip, writer); + }, + .array_type => |array_info| { + try writer.print("[{d}", .{array_info.len}); + if (array_info.sentinel != Index.none) { + try writer.print(":{}", .{array_info.sentinel.fmtValue(array_info.child, ip)}); + } + try writer.writeByte(']'); + try printType(array_info.child, ip, writer); + }, + .struct_type => @panic("TODO"), + .optional_type => |optional_info| { + try writer.writeByte('?'); + try printType(optional_info.payload_type, ip, writer); + }, + .error_union_type => |error_union_info| { + try writer.print("{}!{}", .{ + error_union_info.error_set_type.fmtType(ip), + error_union_info.payload_type.fmtType(ip), + }); + }, + // .error_type => @panic("TODO"), + .error_set_type => @panic("TODO"), + .enum_type => @panic("TODO"), + .function_type => |function_info| { + try writer.writeAll("fn("); + + for (function_info.args) |arg, i| { + if (i != 0) try writer.writeAll(", "); + + if (arg.is_comptime) { + try writer.writeAll("comptime "); + } + // TODO noalias + try printType(arg.arg_type, ip, writer); + } + + if (function_info.is_var_args) { + if (function_info.args.len != 0) { + try writer.writeAll(", "); + } + try writer.writeAll("..."); + } + try writer.writeAll(") "); + + if (function_info.calling_convention != .Unspecified) { + try writer.print("callconv(.{s})", .{@tagName(function_info.calling_convention)}); + } + if (function_info.alignment != 0) { + try writer.print("align({d}) ", .{function_info.alignment}); + } + try printType(function_info.return_type, ip, writer); + }, + .union_type => @panic("TODO"), + .tuple_type => @panic("TODO"), + .vector_type => |vector_info| { + try writer.print("@Vector({d},{})", .{ + vector_info.len, + vector_info.child.fmtType(ip), + }); + }, + + .declaration, .namespace => unreachable, + + .int_u64_value, + .int_i64_value, + .int_big_value, + .float_16_value, + .float_32_value, + .float_64_value, + .float_80_value, + .float_128_value, + => unreachable, + + .type_value, + .bytes, + => unreachable, + } + } + + pub fn formatValue( + ctx: ValueFormatContext, + comptime unused_format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + comptime assert(unused_format_string.len == 0); + _ = options; + return printValue(ctx.value, ctx.ty, ctx.ip, writer); + } + + pub fn printValue( + value: Index, + ty: Index, + ip: *const InternPool, + writer: anytype, + ) @TypeOf(writer).Error!void { + _ = ty; + const value_key: Key = ip.indexToKey(value); + switch (value_key) { + .simple => |simple| switch (simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .anyopaque, + .bool, + .void, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .noreturn, + .@"anyframe", + => try writer.writeAll(@tagName(simple)), + + .null_type => try writer.writeAll("@TypeOf(null)"), + .undefined_type => try writer.writeAll("@TypeOf(undefined)"), + .enum_literal_type => try writer.writeAll("@TypeOf(.enum_literal)"), + + // values + .undefined_value => try writer.writeAll("@Type(.Undefined)"), + .void_value => try writer.writeAll("void"), + .unreachable_value => try writer.writeAll("unreachable"), + .null_value => try writer.writeAll("null"), + .bool_true => try writer.writeAll("true"), + .bool_false => try writer.writeAll("false"), + }, + + .int_type, + .pointer_type, + .array_type, + .struct_type, + .optional_type, + .error_union_type, + .error_set_type, + .enum_type, + .function_type, + .union_type, + .tuple_type, + .vector_type, + => unreachable, + + .declaration => unreachable, + .namespace => unreachable, + + .int_u64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer), + .int_i64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer), + .int_big_value => |big_int| try big_int.format("", .{}, writer), + .float_16_value => |float| try writer.print("{d}", .{float}), + .float_32_value => |float| try writer.print("{d}", .{float}), + .float_64_value => |float| try writer.print("{d}", .{float}), + .float_80_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), + .float_128_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), + + .type_value, + .bytes, + => unreachable, + } + } +}; + +pub const Item = struct { + tag: Tag, + /// The doc comments on the respective Tag explain how to interpret this. + data: u32, +}; + +/// Represents an index into `map`. It represents the canonical index +/// of a `Value` within this `InternPool`. The values are typed. +/// 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`. +pub const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn fmtType(ty: Index, ip: *const InternPool) std.fmt.Formatter(Key.formatType) { + return .{ .data = .{ + .ty = ty, + .ip = ip, + } }; + } + + pub fn fmtValue(value_index: Index, type_index: Index, ip: *const InternPool) std.fmt.Formatter(Key.formatValue) { + return .{ .data = .{ + .value = value_index, + .ty = type_index, + .ip = ip, + } }; + } +}; + +pub const Tag = enum(u8) { + /// A type or value that can be represented with only an enum tag. + /// data is Simple enum value + simple, + + /// An integer type. + /// data is number of bits + type_int_signed, + /// An integer type. + /// data is number of bits + type_int_unsigned, + /// A pointer type. + /// data is payload to Pointer. + type_pointer, + /// An array type. + /// data is payload to Array. + type_array, + /// An struct type. + /// data is payload to Struct. + type_struct, + /// An optional type. + /// data is index to type + type_optional, + /// 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, + /// An enum type. + /// data is payload to Enum. + type_enum, + /// An function type. + /// data is payload to Fn. + type_function, + /// An union type. + /// data is payload to Union. + type_union, + /// An tuple type. + /// data is payload to Tuple. + type_tuple, + /// An vector type. + /// data is payload to Vector. + type_vector, + + /// An unsigned integer value that can be represented by u32. + /// data is integer value + int_u32, + /// An unsigned integer value that can be represented by i32. + /// data is integer value bitcasted to u32. + int_i32, + /// An unsigned integer value that can be represented by u64. + /// data is payload to u64 + int_u64, + /// An unsigned integer value that can be represented by u64. + /// data is payload to i64 bitcasted to u64 + int_i64, + /// A positive integer value that does not fit in 64 bits. + /// data is a extra index to BigInt limbs. + int_big_positive, + /// A negative integer value that does not fit in 64 bits. + /// data is a extra index to BigInt limbs. + int_big_negative, + /// A float value that can be represented by f16. + /// data is f16 bitcasted to u16 cast to u32. + float_f16, + /// A float value that can be represented by f32. + /// data is f32 bitcasted to u32. + float_f32, + /// A float value that can be represented by f64. + /// data is payload to f64. + float_f64, + /// A float value that can be represented by f80. + /// data is payload to f80. + float_f80, + /// A float value that can be represented by f128. + /// data is payload to f128. + float_f128, + /// A type value. + /// data is Index. + type, +}; + +pub const Simple = enum(u32) { + f16, + f32, + f64, + f80, + f128, + usize, + isize, + c_short, + c_ushort, + c_int, + c_uint, + c_long, + c_ulong, + c_longlong, + c_ulonglong, + c_longdouble, + anyopaque, + bool, + void, + type, + anyerror, + comptime_int, + comptime_float, + noreturn, + @"anyframe", + null_type, + undefined_type, + enum_literal_type, + + // values + undefined_value, + void_value, + unreachable_value, + null_value, + bool_true, + bool_false, +}; + +pub fn deinit(ip: *InternPool, gpa: Allocator) void { + ip.map.deinit(gpa); + ip.items.deinit(gpa); + ip.extra.deinit(gpa); + + // 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), + } }, + .type_int_unsigned => .{ .int_type = .{ + .signedness = .unsigned, + .bits = @intCast(u16, data), + } }, + .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_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) }, + .type_union => .{ .union_type = ip.extraData(Union, data) }, + .type_tuple => .{ .tuple_type = ip.extraData(Tuple, data) }, + .type_vector => .{ .vector_type = ip.extraData(Vector, 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) }, + .int_i64 => .{ .int_i64_value = ip.extraData(i64, data) }, + .int_big_positive => unreachable, + .int_big_negative => unreachable, + .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) }, + else => @panic("TODO"), + }; +} + +pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { + const adapter: KeyAdapter = .{ .intern_pool = 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) }, + .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 = ty }, + inline else => |data| .{ .tag = key.tag(), .data = try ip.addExtra(gpa, data) }, // TODO sad stage1 noises :( + }; + try ip.items.append(gpa, item); + return @intToEnum(Index, ip.items.len - 1); +} + +fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { + comptime if (@sizeOf(@TypeOf(extra)) <= 4) { + @compileError(@typeName(@TypeOf(extra)) ++ " fits into a u32! Consider directly storing this extra in Item's data field"); + }; + const result = @intCast(u32, ip.extra.items.len); + try ip.extra.appendSlice(gpa, &std.mem.toBytes(extra)); + return result; +} + +fn extraData(ip: InternPool, comptime T: type, index: usize) T { + const size = @sizeOf(T); + const bytes = @ptrCast(*const [size]u8, ip.extra.items.ptr + index); + return std.mem.bytesToValue(T, bytes); +} + +// --------------------------------------------- +// UTILITY +// --------------------------------------------- + +pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, target: std.Target) Allocator.Error!Index { + switch (types.len) { + 0 => return Key{ .simple = .noreturn }, + 1 => types[0], + } + + var chosen = types[0]; + var any_are_null = false; + var seen_const = false; + var convert_to_slice = false; + var chosen_i: usize = 0; + for (types[1..]) |candidate, candidate_i| { + const candidate_key: Key = ip.indexToKey(candidate); + const chosen_key = ip.indexToKey(chosen); + + if (candidate_key == chosen_key) continue; + + switch (candidate_key) { + .simple => |candidate_simple| switch (candidate_simple) { + // TODO usize, isize + // TODO c integer types + .f16, .f32, .f64, .f80, .f128 => switch (chosen_key) { + .simple => |chosen_simple| switch (chosen_simple) { + .f16, .f32, .f64, .f80, .f128 => { + // NOTE we don't have to handle the equality case + + @panic("TODO: choose larger"); + }, + .comptime_int, .comptime_float => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + else => {}, + }, + }, + + .noreturn, .undefined_type => continue, + + .comptime_int => switch (chosen_key) { + .simple => |chosen_simple| switch (chosen_simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + => continue, + .usize, .isize => continue, + .comptime_int => unreachable, + .comptime_float => continue, + }, + .int_type => continue, + .pointer_type => |chosen_info| if (chosen_info.size == .C) continue, + else => {}, + }, + .comptime_float => switch (chosen_key) { + .simple => |chosen_simple| switch (chosen_simple) { + .f16, .f32, .f64, .f80, .f128 => continue, + .comptime_int => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + }, + else => {}, + }, + .null_type => { + any_are_null = true; + continue; + }, + else => {}, + }, + .int_type => |candidate_info| switch (chosen_key) { + .simple => |chosen_simple| switch (chosen_simple) { + .usize, .isize => { + // TODO + }, + // TODO c integer types + .comptime_int => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + else => {}, + }, + .int_type => |chosen_info| { + if (chosen_info.bits < candidate_info.bits) { + chosen = candidate; + chosen_i = candidate_i + 1; + } + continue; + }, + .pointer_type => |chosen_info| if (chosen_info.size == .C) continue, + else => {}, + }, + .pointer_type => |candidate_info| switch (chosen_key) { + .simple => |chosen_simple| switch (chosen_simple) { + .comptime_int => { + if (candidate_info.size == .C) { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + }, + else => {}, + }, + .pointer_type => |chosen_info| { + seen_const = seen_const or chosen_info.is_const or candidate_info.is_const; + + const candidate_elem_info = ip.indexToKey(candidate_info.elem_type); + const chosen_elem_info = ip.indexToKey(chosen_info.elem_type); + + // *[N]T to [*]T + // *[N]T to []T + if ((candidate_info.size == .Many or candidate_info.size == .Slice) and + chosen_info.size == .One and + chosen_elem_info == .array_type) + { + // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` + convert_to_slice = false; + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + if (candidate_info.size == .One and + candidate_elem_info == .array_type and + (chosen_info.size == .Many or chosen_info.size == .Slice)) + { + // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T` + convert_to_slice = false; + continue; + } + + // *[N]T and *[M]T + // Verify both are single-pointers to arrays. + // Keep the one whose element type can be coerced into. + if (chosen_info.size == .One and + candidate_info.size == .One and + chosen_elem_info == .array_type and + candidate_elem_info == .array_type) + { + const chosen_elem_ty = chosen_elem_info.array_type.child; + const cand_elem_ty = candidate_elem_info.array_type.child; + + const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target); + if (chosen_ok) { + convert_to_slice = true; + continue; + } + + const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, cand_elem_ty, chosen_elem_ty, candidate_info.mutable, target); + if (cand_ok) { + convert_to_slice = true; + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + + // They're both bad. Report error. + // In the future we probably want to use the + // coerceInMemoryAllowed error reporting mechanism, + // however, for now we just fall through for the + // "incompatible types" error below. + } + + // [*c]T and any other pointer size + // Whichever element type can coerce to the other one, is + // the one we will keep. If they're both OK then we keep the + // C pointer since it matches both single and many pointers. + if (candidate_info.size == .C or chosen_info.size == .C) { + const cand_ok = .ok == try ip.coerceInMemoryAllowed(candidate_info.elem_type, chosen_info.elem_type, candidate_info.mutable, target); + const chosen_ok = .ok == try ip.coerceInMemoryAllowed(chosen_info.elem_type, candidate_info.elem_type, chosen_info.mutable, target); + + if (cand_ok) { + if (!chosen_ok or chosen_info.size != .C) { + chosen = candidate; + chosen_i = candidate_i + 1; + } + continue; + } else { + if (chosen_ok) continue; + // They're both bad. Report error. + // In the future we probably want to use the + // coerceInMemoryAllowed error reporting mechanism, + // however, for now we just fall through for the + // "incompatible types" error below. + } + } + // TODO + }, + .int_type => { + if (candidate_info.size == .C) { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + }, + .optional_type => |chosen_info| switch (ip.indexToKey(chosen_info.payload_type)) { + .pointer_type => |chosen_ptr_info| { + seen_const = seen_const or chosen_ptr_info.is_const or candidate_info.is_const; + + // *[N]T to ?![*]T + // *[N]T to ?![]T + if (candidate_info.size == .One and + ip.indexToKey(candidate_info.elem_type) == .array_type and + (chosen_ptr_info.size == .Many or chosen_ptr_info.size == .Slice)) + { + continue; + } + }, + else => {}, + }, + .error_union_type => |chosen_info| { + const chosen_ptr_key = ip.indexToKey(chosen_info.payload_type); + + if (chosen_ptr_key == .pointer_type) { + const chosen_ptr_info = chosen_ptr_key.pointer_type; + seen_const = seen_const or chosen_ptr_info.is_const or candidate_info.is_const; + + // *[N]T to E![*]T + // *[N]T to E![]T + if (candidate_info.size == .One and + (chosen_ptr_info.size == .Many or chosen_ptr_info.size == .Slice) and + ip.indexToKey(candidate_info.elem_type) == .array_type) + { + continue; + } + } + }, + .function_type => { + if (candidate_info.is_const and + ip.indexToKey(candidate_info.elem_type) == .function_type and + .ok == try ip.coerceInMemoryAllowedFns(chosen, candidate_info.pointee_type, target)) + { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + }, + else => {}, + }, + .array_type => switch (chosen_key) { + .vector_type => continue, + else => {}, + }, + .optional_type => |candidate_info| { + const is_chosen_const_ptr = switch (chosen_key) { + .pointer_type => |chosen_info| chosen_info.is_const, + else => false, + }; + + if ((try ip.coerceInMemoryAllowed(chosen, candidate_info.payload_type, false, target)) == .ok) { + seen_const = seen_const or candidate_info.payload_type.isConstPtr(); + any_are_null = true; + continue; + } + + seen_const = seen_const or is_chosen_const_ptr; + any_are_null = false; + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + .vector_type => switch (chosen_key) { + .array_type => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + else => {}, + }, + else => {}, + } + + switch (chosen_key) { + .simple => |simple| switch (simple) { + .noreturn, .undefined_type => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + } + .NoReturn, + .Undefined => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + .Null => { + any_are_null = true; + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + .Optional => { + if ((try ip.coerceInMemoryAllowed(chosen_key.optional_type.payload_type, candidate, false, target)) == .ok) { + continue; + } + if ((try ip.coerceInMemoryAllowed(candidate, chosen_key.optional_type.payload_type, false, target)) == .ok) { + any_are_null = true; + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + }, + .ErrorUnion => { + const payload_ty = chosen_key.error_union_type.payload_type; + if ((try ip.coerceInMemoryAllowed(payload_ty, candidate, false, target)) == .ok) { + continue; + } + }, + else => {}, + } + } +} + +const InMemoryCoercionResult = union(enum) { + ok, + no_match: Pair, + int_not_coercible: IntMismatch, + error_union_payload: PairAndChild, + array_len: IntPair, + array_sentinel: Sentinel, + array_elem: PairAndChild, + vector_len: IntPair, + vector_elem: PairAndChild, + optional_shape: Pair, + optional_child: PairAndChild, + from_anyerror, + missing_error: []const []const u8, + /// true if wanted is var args + fn_var_args: bool, + /// true if wanted is generic + fn_generic: bool, + fn_param_count: IntPair, + fn_param_noalias: IntPair, + fn_param_comptime: ComptimeParam, + fn_param: Param, + fn_cc: CC, + fn_return_type: PairAndChild, + ptr_child: PairAndChild, + ptr_addrspace: AddressSpace, + ptr_sentinel: Sentinel, + ptr_size: Size, + ptr_qualifiers: Qualifiers, + ptr_allowzero: Pair, + ptr_bit_range: BitRange, + ptr_alignment: IntPair, + + const Pair = struct { + actual: Index, // type + wanted: Index, // type + }; + + const PairAndChild = struct { + child: *InMemoryCoercionResult, + actual: Index, // type + wanted: Index, // type + }; + + const Param = struct { + child: *InMemoryCoercionResult, + actual: Index, // type + wanted: Index, // type + index: u64, + }; + + const ComptimeParam = struct { + index: u64, + wanted: bool, + }; + + const Sentinel = struct { + // unreachable_value indicates no sentinel + actual: Index, // value + wanted: Index, // value + ty: Index, + }; + + const IntMismatch = struct { + actual_signedness: std.builtin.Signedness, + wanted_signedness: std.builtin.Signedness, + actual_bits: u16, + wanted_bits: u16, + }; + + const IntPair = struct { + actual: u64, + wanted: u64, + }; + + const Size = struct { + actual: std.builtin.Type.Pointer.Size, + wanted: std.builtin.Type.Pointer.Size, + }; + + const Qualifiers = struct { + actual_const: bool, + wanted_const: bool, + actual_volatile: bool, + wanted_volatile: bool, + }; + + const AddressSpace = struct { + actual: std.builtin.AddressSpace, + wanted: std.builtin.AddressSpace, + }; + + const CC = struct { + actual: std.builtin.CallingConvention, + wanted: std.builtin.CallingConvention, + }; + + const BitRange = struct { + actual_host: u16, + wanted_host: u16, + actual_offset: u16, + wanted_offset: u16, + }; + + fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { + const res = try arena.create(InMemoryCoercionResult); + res.* = child.*; + return res; + } +}; + +/// If pointers have the same representation in runtime memory, a bitcast AIR instruction +/// may be used for the coercion. +/// * `const` attribute can be gained +/// * `volatile` attribute can be gained +/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut +/// * alignment can be decreased +/// * bit offset attributes must match exactly +/// * `*`/`[*]` must match exactly, but `[*c]` matches either one +/// * sentinel-terminated pointers can coerce into `[*]` +fn coerceInMemoryAllowed( + ip: *InternPool, + // gpa: Allocator, + arena: Allocator, + dest_ty: Index, + src_ty: Index, + dest_is_mut: bool, + target: std.Target, +) !InMemoryCoercionResult { + if (dest_ty == src_ty) return .ok; + + const dest_key = ip.indexToKey(dest_ty); + const src_key = ip.indexToKey(src_ty); + + const dest_tag = dest_key.zigTypeTag(); + const src_tag = src_key.zigTypeTag(); + + // integers with the same number of bits. + if (dest_tag == .Int and src_tag == .Int) { + const dest_info = dest_key.intInfo(target, ip); + const src_info = src_key.intInfo(target, ip); + + if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) return .ok; + + if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or + // small enough unsigned ints can get casted to large enough signed ints + (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or + (dest_info.signedness == .unsigned and src_info.signedness == .signed)) + { + return InMemoryCoercionResult{ .int_not_coercible = .{ + .actual_signedness = src_info.signedness, + .wanted_signedness = dest_info.signedness, + .actual_bits = src_info.bits, + .wanted_bits = dest_info.bits, + } }; + } + } + + // floats with the same number of bits. + if (dest_tag == .Float and src_tag == .Float and + // this is an optimization because only a long double can have the same size as a other Float + // SAFETY: every Float is a Simple + dest_key.simple == .c_longdouble or src_tag.simple == .c_longdouble) + { + const dest_bits = dest_key.floatBits(target); + const src_bits = src_key.floatBits(target); + if (dest_bits == src_bits) return .ok; + } + + // Pointers / Pointer-like Optionals + const maybe_dest_ptr_ty = try ip.typePtrOrOptionalPtrTy(dest_ty); + const maybe_src_ptr_ty = try ip.typePtrOrOptionalPtrTy(src_ty); + if (maybe_dest_ptr_ty != Index.none and maybe_src_ptr_ty != Index.none) { + return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, maybe_dest_ptr_ty, maybe_src_ptr_ty, dest_is_mut, target); + } + + // Slices + if (dest_key.isSlice() and src_key.isSlice()) { + return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); + } + + // Functions + if (dest_tag == .Fn and src_tag == .Fn) { + return try ip.coerceInMemoryAllowedFns(dest_ty, src_ty, target); + } + + // Error Unions + if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { + const dest_payload = dest_key.error_union_type.payload_type; + const src_payload = src_key.error_union_type.payload_type; + const child = try ip.coerceInMemoryAllowed(dest_payload, src_payload, dest_is_mut, target); + if (child != .ok) { + return InMemoryCoercionResult{ .error_union_payload = .{ + .child = try child.dupe(arena), + .actual = src_payload, + .wanted = dest_payload, + } }; + } + return try ip.coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target); + } + + // Error Sets + if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { + return .ok; // TODO: implement coerceInMemoryAllowedErrorSets + // return try ip.coerceInMemoryAllowedErrorSets(dest_ty, src_ty); + } + + // Arrays + if (dest_tag == .Array and src_tag == .Array) { + const dest_info = dest_key.array_type.len; + const src_info = src_key.array_type.len; + if (dest_info.len != src_info.len) { + return InMemoryCoercionResult{ .array_len = .{ + .actual = src_info.len, + .wanted = dest_info.len, + } }; + } + + const child = try ip.coerceInMemoryAllowed(dest_key.array_type.child, src_key.array_type.child, dest_is_mut, target); + if (child != .ok) { + return InMemoryCoercionResult{ .array_elem = .{ + .child = try child.dupe(arena), + .actual = src_key.array_type.child, + .wanted = dest_key.array_type.child, + } }; + } + + const ok_sent = dest_key.array_type.sentinel == Index.none or + (src_key.array_type.sentinel != Index.none and + dest_key.array_type.sentinel == src_key.array_type.sentinel // is this enough for a value equality check? + ); + if (!ok_sent) { + return InMemoryCoercionResult{ .array_sentinel = .{ + .actual = src_info.sentinel, + .wanted = dest_info.sentinel, + .ty = dest_key.array_type.child, + } }; + } + return .ok; + } + + // Vectors + if (dest_tag == .Vector and src_tag == .Vector) { + const dest_len = dest_key.vector_type.len; + const src_len = src_key.vector_type.len; + + if (dest_len != src_len) { + return InMemoryCoercionResult{ .vector_len = .{ + .actual = src_len, + .wanted = dest_len, + } }; + } + + const dest_elem_ty = dest_key.vector_type.child; + const src_elem_ty = src_key.vector_type.child; + const child = try ip.coerceInMemoryAllowed(dest_elem_ty, src_elem_ty, dest_is_mut, target); + if (child != .ok) { + return InMemoryCoercionResult{ .vector_elem = .{ + .child = try child.dupe(arena), + .actual = src_elem_ty, + .wanted = dest_elem_ty, + } }; + } + + return .ok; + } + + // Optionals + if (dest_tag == .Optional and src_tag == .Optional) { + if (maybe_dest_ptr_ty != maybe_src_ptr_ty) { + return InMemoryCoercionResult{ .optional_shape = .{ + .actual = src_ty, + .wanted = dest_ty, + } }; + } + + const dest_child_type = dest_key.optional_type.payload_type; + const src_child_type = src_key.optional_type.payload_type; + + const child = try ip.coerceInMemoryAllowed(dest_child_type, src_child_type, dest_is_mut, target); + if (child != .ok) { + return InMemoryCoercionResult{ .optional_child = .{ + .child = try child.dupe(arena), + .actual = src_child_type, + .wanted = dest_child_type, + } }; + } + + return .ok; + } + + return InMemoryCoercionResult{ .no_match = .{ + .actual = dest_ty, + .wanted = src_ty, + } }; +} + +// fn coerceInMemoryAllowedErrorSets( +// ip: *InternPool, +// gpa: Allocator, +// arena: Allocator, +// dest_ty: Index, +// src_ty: Index, +// ) !InMemoryCoercionResult { +// if(dest_ty == src_ty) return .ok; + +// const dest_key = ip.indexToKey(dest_ty); + +// // Coercion to `anyerror`. Note that this check can return false negatives +// // in case the error sets did not get resolved. +// if(dest_key.simple) |simple| if(simple == .anyerror) return .ok; + +// const src_key = ip.indexToKey(src_ty); + +// // const dest_tag = dest_key.zigTypeTag(); +// // const src_tag = src_key.zigTypeTag(); + +// if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { +// const dst_ies = dst_payload.data; +// // We will make an effort to return `ok` without resolving either error set, to +// // avoid unnecessary "unable to resolve error set" dependency loop errors. +// switch (src_ty.tag()) { +// .error_set_inferred => { +// // If both are inferred error sets of functions, and +// // the dest includes the source function, the coercion is OK. +// // This check is important because it works without forcing a full resolution +// // of inferred error sets. +// const src_ies = src_ty.castTag(.error_set_inferred).?.data; + +// if (dst_ies.inferred_error_sets.contains(src_ies)) { +// return .ok; +// } +// }, +// .error_set_single => { +// const name = src_ty.castTag(.error_set_single).?.data; +// if (dst_ies.errors.contains(name)) return .ok; +// }, +// .error_set_merged => { +// const names = src_ty.castTag(.error_set_merged).?.data.keys(); +// for (names) |name| { +// if (!dst_ies.errors.contains(name)) break; +// } else return .ok; +// }, +// .error_set => { +// const names = src_ty.castTag(.error_set).?.data.names.keys(); +// for (names) |name| { +// if (!dst_ies.errors.contains(name)) break; +// } else return .ok; +// }, +// .anyerror => {}, +// else => unreachable, +// } + +// if (dst_ies.func == sema.owner_func) { +// // We are trying to coerce an error set to the current function's +// // inferred error set. +// try dst_ies.addErrorSet(sema.gpa, src_ty); +// return .ok; +// } + +// try sema.resolveInferredErrorSet(block, dest_src, dst_payload.data); +// // isAnyError might have changed from a false negative to a true positive after resolution. +// if (dest_ty.isAnyError()) { +// return .ok; +// } +// } + +// var missing_error_buf = std.ArrayList([]const u8).init(sema.gpa); +// defer missing_error_buf.deinit(); + +// switch (src_ty.tag()) { +// .error_set_inferred => { +// const src_data = src_ty.castTag(.error_set_inferred).?.data; + +// try sema.resolveInferredErrorSet(block, src_src, src_data); +// // src anyerror status might have changed after the resolution. +// if (src_ty.isAnyError()) { +// // dest_ty.isAnyError() == true is already checked for at this point. +// return .from_anyerror; +// } + +// for (src_data.errors.keys()) |key| { +// if (!dest_ty.errorSetHasField(key)) { +// try missing_error_buf.append(key); +// } +// } + +// if (missing_error_buf.items.len != 0) { +// return InMemoryCoercionResult{ +// .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), +// }; +// } + +// return .ok; +// }, +// .error_set_single => { +// const name = src_ty.castTag(.error_set_single).?.data; +// if (dest_ty.errorSetHasField(name)) { +// return .ok; +// } +// const list = try sema.arena.alloc([]const u8, 1); +// list[0] = name; +// return InMemoryCoercionResult{ .missing_error = list }; +// }, +// .error_set_merged => { +// const names = src_ty.castTag(.error_set_merged).?.data.keys(); +// for (names) |name| { +// if (!dest_ty.errorSetHasField(name)) { +// try missing_error_buf.append(name); +// } +// } + +// if (missing_error_buf.items.len != 0) { +// return InMemoryCoercionResult{ +// .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), +// }; +// } + +// return .ok; +// }, +// .error_set => { +// const names = src_ty.castTag(.error_set).?.data.names.keys(); +// for (names) |name| { +// if (!dest_ty.errorSetHasField(name)) { +// try missing_error_buf.append(name); +// } +// } + +// if (missing_error_buf.items.len != 0) { +// return InMemoryCoercionResult{ +// .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), +// }; +// } + +// return .ok; +// }, +// .anyerror => switch (dest_ty.tag()) { +// .error_set_inferred => unreachable, // Caught by dest_ty.isAnyError() above. +// .error_set_single, .error_set_merged, .error_set => return .from_anyerror, +// .anyerror => unreachable, // Filtered out above. +// else => unreachable, +// }, +// else => unreachable, +// } + +// unreachable; +// } + +fn coerceInMemoryAllowedFns( + ip: *InternPool, + arena: std.mem.Allocator, + dest_ty: Index, + src_ty: Index, + target: std.Target, +) !InMemoryCoercionResult { + const dest_info = dest_ty.fnInfo(); + const src_info = src_ty.fnInfo(); + + if (dest_info.is_var_args != src_info.is_var_args) { + return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; + } + + if (dest_info.is_generic != src_info.is_generic) { + return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; + } + + if (dest_info.cc != src_info.cc) { + return InMemoryCoercionResult{ .fn_cc = .{ + .actual = src_info.cc, + .wanted = dest_info.cc, + } }; + } + + if (!src_info.return_type.isNoReturn()) { + const rt = try ip.coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target); + if (rt != .ok) { + return InMemoryCoercionResult{ .fn_return_type = .{ + .child = try rt.dupe(arena), + .actual = src_info.return_type, + .wanted = dest_info.return_type, + } }; + } + } + + if (dest_info.param_types.len != src_info.param_types.len) { + return InMemoryCoercionResult{ .fn_param_count = .{ + .actual = src_info.param_types.len, + .wanted = dest_info.param_types.len, + } }; + } + + if (dest_info.noalias_bits != src_info.noalias_bits) { + return InMemoryCoercionResult{ .fn_param_noalias = .{ + .actual = src_info.noalias_bits, + .wanted = dest_info.noalias_bits, + } }; + } + + for (dest_info.param_types) |dest_param_ty, i| { + const src_param_ty = src_info.param_types[i]; + + if (dest_info.comptime_params[i] != src_info.comptime_params[i]) { + return InMemoryCoercionResult{ .fn_param_comptime = .{ + .index = i, + .wanted = dest_info.comptime_params[i], + } }; + } + + // Note: Cast direction is reversed here. + const param = try ip.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target); + if (param != .ok) { + return InMemoryCoercionResult{ .fn_param = .{ + .child = try param.dupe(arena), + .actual = src_param_ty, + .wanted = dest_param_ty, + .index = i, + } }; + } + } + + return .ok; +} + +/// For pointer-like optionals, it returns the pointer type. For pointers, +/// the type is returned unmodified. +/// This can return `error.AnalysisFail` because it sometimes requires resolving whether +/// a type has zero bits, which can cause a "foo depends on itself" compile error. +/// This logic must be kept in sync with `Type.isPtrLikeOptional`. +fn typePtrOrOptionalPtrTy( + ty: Index, + ip: InternPool, +) !Index { + const key = ip.indexToKey(ty); + switch (key) { + .pointer_type => |pointer_info| switch (pointer_info.size) { + .Slice => return Index.none, + else => return ty, + }, + + .optional_type => |optional_info| { + const child_type = optional_info.payload_type; + const child_key = ip.indexToKey(child_type); + + if (child_key != .pointer_type) return Index.none; + const child_ptr_key = child_key.pointer_type; + + switch (child_ptr_key) { + .Slice, .C => return Index.none, + .Many, .One => { + if (child_ptr_key.is_allowzero) return Index.none; + + // TODO optionals of zero sized types behave like bools, not pointers + // if ((try sema.typeHasOnePossibleValue(child_type)) != null) { + // return null; + // } + + return child_type; + }, + } + }, + + else => return Index.none, + } +} + +// --------------------------------------------- +// TESTS +// --------------------------------------------- + +fn testExpectFmtType(ip: *const InternPool, index: Index, expected: []const u8) !void { + const gpa = std.testing.allocator; + const actual = try std.fmt.allocPrint(gpa, "{}", .{index.fmtType(ip)}); + defer gpa.free(actual); + try std.testing.expectEqualStrings(expected, actual); +} + +fn testExpectFmtValue(ip: *const InternPool, val: Index, ty: Index, expected: []const u8) !void { + const gpa = std.testing.allocator; + const actual = try std.fmt.allocPrint(gpa, "{}", .{val.fmtValue(ty, ip)}); + defer gpa.free(actual); + try std.testing.expectEqualStrings(expected, actual); +} + +test "int 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 i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); + const u7_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 7 } }); + const another_i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + + try std.testing.expect(i32_type == another_i32_type); + try std.testing.expect(i32_type != u7_type); + + try std.testing.expect(i16_type != another_i32_type); + try std.testing.expect(i16_type != u7_type); + + try testExpectFmtType(&ip, i32_type, "i32"); + try testExpectFmtType(&ip, i16_type, "i16"); + try testExpectFmtType(&ip, u7_type, "u7"); +} + +test "int value" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const unsigned_zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); + const unsigned_one_value = try ip.get(gpa, .{ .int_u64_value = 1 }); + const signed_zero_value = try ip.get(gpa, .{ .int_i64_value = 0 }); + const signed_one_value = try ip.get(gpa, .{ .int_i64_value = 1 }); + + const u64_max_value = try ip.get(gpa, .{ .int_u64_value = std.math.maxInt(u64) }); + const i64_max_value = try ip.get(gpa, .{ .int_i64_value = std.math.maxInt(i64) }); + + try std.testing.expect(unsigned_zero_value != unsigned_one_value); + try std.testing.expect(unsigned_one_value != signed_zero_value); + try std.testing.expect(signed_zero_value != signed_one_value); + + try std.testing.expect(signed_one_value != u64_max_value); + try std.testing.expect(u64_max_value != i64_max_value); + + try testExpectFmtValue(&ip, unsigned_zero_value, undefined, "0"); + try testExpectFmtValue(&ip, unsigned_one_value, undefined, "1"); + try testExpectFmtValue(&ip, signed_zero_value, undefined, "0"); + try testExpectFmtValue(&ip, signed_one_value, undefined, "1"); + + try testExpectFmtValue(&ip, u64_max_value, undefined, "18446744073709551615"); + try testExpectFmtValue(&ip, i64_max_value, undefined, "9223372036854775807"); +} + +test "pointer type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + + try std.testing.expect(i32_type_0 == i32_type_1); + try std.testing.expect(i32_type_0 != u32_type); + + var ptr: Pointer = .{ + .elem_type = undefined, + .sentinel = Index.none, + .alignment = 0, // TODO + .size = std.builtin.Type.Pointer.Size.One, + .is_const = false, + .is_volatile = false, + .is_allowzero = false, + .address_space = std.builtin.AddressSpace.generic, + }; + + ptr.elem_type = i32_type_0; + const i32_pointer_type_0 = try ip.get(gpa, .{ .pointer_type = ptr }); + ptr.elem_type = i32_type_0; + const i32_pointer_type_1 = try ip.get(gpa, .{ .pointer_type = ptr }); + ptr.elem_type = i32_type_1; + const i32_pointer_type_2 = try ip.get(gpa, .{ .pointer_type = ptr }); + ptr.elem_type = u32_type; + const u32_pointer_type = try ip.get(gpa, .{ .pointer_type = ptr }); + + try std.testing.expect(i32_pointer_type_0 == i32_pointer_type_1); + try std.testing.expect(i32_pointer_type_1 == i32_pointer_type_2); + try std.testing.expect(i32_pointer_type_0 != u32_pointer_type); + + ptr.is_const = true; + const const_u32_pointer_type = try ip.get(gpa, .{ .pointer_type = ptr }); + + try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); + try testExpectFmtType(&ip, u32_pointer_type, "*u32"); + try testExpectFmtType(&ip, const_u32_pointer_type, "*const u32"); +} + +test "optional type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + + const i32_optional_type_0 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_0 } }); + const i32_optional_type_1 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_1 } }); + const u32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = u32_type } }); + + try std.testing.expect(i32_optional_type_0 == i32_optional_type_1); + try std.testing.expect(i32_optional_type_0 != u32_optional_type); + + try testExpectFmtType(&ip, i32_optional_type_0, "?i32"); + try testExpectFmtType(&ip, u32_optional_type, "?u32"); +} + +test "array type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + const zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); + + const i32_3_array_type_0 = try ip.get(gpa, .{ .array_type = .{ + .len = 3, + .child = i32_type_0, + .sentinel = Index.none, + } }); + const i32_3_array_type_1 = try ip.get(gpa, .{ .array_type = .{ + .len = 3, + .child = i32_type_1, + .sentinel = Index.none, + } }); + const u32_0_0_array_type = try ip.get(gpa, .{ .array_type = .{ + .len = 3, + .child = u32_type, + .sentinel = zero_value, + } }); + + try std.testing.expect(i32_3_array_type_0 == i32_3_array_type_1); + try std.testing.expect(i32_3_array_type_1 != u32_0_0_array_type); + + try testExpectFmtType(&ip, i32_3_array_type_0, "[3]i32"); + try testExpectFmtType(&ip, u32_0_0_array_type, "[3:0]u32"); +} diff --git a/src/Server.zig b/src/Server.zig index d8de20e..951fc49 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -504,11 +504,12 @@ fn typeToCompletion( ), .primitive, .array_index => {}, .@"comptime" => |co| { - const ti = co.type.getTypeInfo(); - switch (ti) { - .@"struct" => |st| { - var fit = st.fields.iterator(); - while (fit.next()) |entry| { + const key = co.interpreter.ip.indexToKey(co.type.ty); + + switch (key) { + .struct_type => |struct_info| { + var field_it = struct_info.fields.iterator(); + while (field_it.next()) |entry| { try list.append(allocator, .{ .label = entry.key_ptr.*, .kind = .Field, @@ -516,16 +517,8 @@ fn typeToCompletion( .insertTextFormat = .PlainText, }); } - - var it = st.scope.declarations.iterator(); - while (it.next()) |entry| { - try list.append(allocator, .{ - .label = entry.key_ptr.*, - .kind = if (entry.value_ptr.isConstant()) .Constant else .Variable, - .insertText = entry.key_ptr.*, - .insertTextFormat = .PlainText, - }); - } + + // TODO declaration completion }, else => {}, } @@ -846,10 +839,10 @@ fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutO var bound_type_params = analysis.BoundTypeParams{}; const resolved_type = try decl_handle.resolveType(&server.document_store, &server.arena, &bound_type_params); - + const resolved_type_str = if (resolved_type) |rt| if (rt.type.is_type_val) switch (rt.type.data) { - .@"comptime" => |*co| try std.fmt.allocPrint(server.arena.allocator(), "{ }", .{co.interpreter.formatTypeInfo(co.type.getTypeInfo())}), + .@"comptime" => |*co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.type.ty.fmtType(&co.interpreter.ip)}), else => "type", } else switch (rt.type.data) { // TODO: Investigate random weird numbers like 897 that cause index of bounds .pointer, From 04448d1a7b4e348dea25f1ad8bcb10b88e68d75d Mon Sep 17 00:00:00 2001 From: Techarix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 27 Dec 2022 02:46:57 +0100 Subject: [PATCH 02/78] more intern pool work --- src/ComptimeInterpreter.zig | 420 +++++++++++++++++------------------- src/InternPool.zig | 44 +++- src/analysis.zig | 24 ++- 3 files changed, 255 insertions(+), 233 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index b811a34..7d7f606 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -4,6 +4,7 @@ // TODO: DODify const std = @import("std"); +const builtin = @import("builtin"); const ast = @import("ast.zig"); const zig = std.zig; const Ast = zig.Ast; @@ -24,7 +25,7 @@ arena: std.heap.ArenaAllocator, ip: InternPool = .{}, document_store: *DocumentStore, uri: DocumentStore.Uri, -root_type: IPIndex = IPIndex.none, +scopes: std.MultiArrayList(Scope) = .{}, /// Interpreter diagnostic errors errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{}, @@ -53,6 +54,12 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void { interpreter.errors.deinit(interpreter.allocator); interpreter.ip.deinit(interpreter.allocator); + + var i: usize = 0; + while (i < interpreter.scopes.len) : (i += 1) { + interpreter.scopes.items(.child_scopes)[i].deinit(interpreter.allocator); + } + interpreter.scopes.deinit(interpreter.allocator); } pub const Type = struct { @@ -80,7 +87,7 @@ pub const FieldDefinition = struct { }; pub const Declaration = struct { - scope: *InterpreterScope, + scope: u32, node_idx: Ast.Node.Index, /// Store name so tree doesn't need to be used to access declaration name @@ -159,20 +166,19 @@ pub const Declaration = struct { // pub const Comptimeness = enum { @"comptime", runtime }; -pub const InterpreterScope = struct { +pub const Scope = struct { interpreter: *ComptimeInterpreter, // TODO: Actually use this value // comptimeness: Comptimeness, - parent: ?*InterpreterScope = null, + parent: u32, // zero indicates root scope node_idx: Ast.Node.Index, - declarations: std.StringHashMapUnmanaged(Declaration) = .{}, - /// Resizes can modify element pointer locations, so we use a list of pointers - child_scopes: std.ArrayListUnmanaged(*InterpreterScope) = .{}, + namespace: IPIndex, + child_scopes: std.ArrayListUnmanaged(u32) = .{}, pub const ScopeKind = enum { container, block, function }; - pub fn scopeKind(scope: InterpreterScope) ScopeKind { + pub fn scopeKind(scope: Scope) ScopeKind { const tree = scope.interpreter.getHandle().tree; return switch (tree.nodes.items(.tag)[scope.node_idx]) { .container_decl, @@ -194,7 +200,7 @@ pub const InterpreterScope = struct { }; } - pub fn getLabel(scope: InterpreterScope) ?Ast.TokenIndex { + pub fn getLabel(scope: Scope) ?Ast.TokenIndex { const tree = scope.interpreter.getHandle().tree; const token_tags = tree.tokens.items(.tag); @@ -209,40 +215,14 @@ pub const InterpreterScope = struct { else => null, }; } - - pub const ParentScopeIterator = struct { - maybe_scope: ?*InterpreterScope, - - pub fn next(psi: *ParentScopeIterator) ?*InterpreterScope { - if (psi.maybe_scope) |scope| { - const curr = scope; - psi.maybe_scope = scope.parent; - return curr; - } else return null; - } - }; - - pub fn parentScopeIterator(scope: *InterpreterScope) ParentScopeIterator { - return ParentScopeIterator{ .maybe_scope = scope }; - } - - pub fn deinit(scope: *InterpreterScope) void { - const allocator = scope.interpreter.allocator; - - scope.declarations.deinit(allocator); - for (scope.child_scopes.items) |child| child.deinit(); - scope.child_scopes.deinit(allocator); - - allocator.destroy(scope); - } }; pub fn newScope( interpreter: *ComptimeInterpreter, - maybe_parent: ?*InterpreterScope, + maybe_parent: ?Scope, node_idx: Ast.Node.Index, -) std.mem.Allocator.Error!*InterpreterScope { - var ls = try interpreter.allocator.create(InterpreterScope); +) std.mem.Allocator.Error!*Scope { + var ls = try interpreter.allocator.create(Scope); if (maybe_parent) |parent| try parent.child_scopes.append(interpreter.allocator, ls); ls.* = .{ .interpreter = interpreter, @@ -299,42 +279,23 @@ fn getDeclCount(tree: Ast, node_idx: Ast.Node.Index) usize { pub fn huntItDown( interpreter: *ComptimeInterpreter, - scope: *InterpreterScope, + namespace: IPIndex, decl_name: []const u8, options: InterpretOptions, -) InterpretError!*Declaration { - const tree = interpreter.getHandle().tree; - const tags = tree.nodes.items(.tag); +) InterpretError!InternPool.Decl { + _ = options; - var psi = scope.parentScopeIterator(); - while (psi.next()) |pscope| { - const known_decl = pscope.declarations.getEntry(decl_name); - if (pscope.scopeKind() == .container and - known_decl == null and - pscope.declarations.count() != getDeclCount(tree, pscope.node_idx)) - { - log.info("Order-independent evaluating {s}...", .{decl_name}); + var current_namespace = namespace; + while (current_namespace != IPIndex.none) { + const namespace_info = interpreter.ip.indexToKey(current_namespace).namespace; + defer current_namespace = namespace_info.parent; - var buffer: [2]Ast.Node.Index = undefined; - const members = ast.declMembers(tree, pscope.node_idx, &buffer); - - for (members) |member| { - switch (tags[member]) { - .global_var_decl, - .local_var_decl, - .aligned_var_decl, - .simple_var_decl, - => { - if (std.mem.eql(u8, analysis.getDeclName(tree, member).?, decl_name)) { - _ = try interpreter.interpret(member, pscope, options); - return pscope.declarations.getEntry(decl_name).?.value_ptr; - } - }, - else => {}, - } + for (namespace_info.decls) |decl_index| { + const decl_info = interpreter.ip.indexToKey(decl_index).declaration; + if (std.mem.eql(u8, decl_info.name, decl_name)) { + return decl_info; } } - return (known_decl orelse continue).value_ptr; } log.err("Identifier not found: {s}", .{decl_name}); @@ -344,49 +305,12 @@ pub fn huntItDown( pub fn cast( interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - dest_type: Type, - value: Value, -) error{ OutOfMemory, InvalidCast }!Value { - const value_data = value.value_data; - - const to_type_info = dest_type.getTypeInfo(); - const from_type_info = value.type.getTypeInfo(); - - // TODO: Implement more implicit casts - - if (from_type_info.eql(to_type_info)) return value; - - const err = switch (from_type_info) { - .comptime_int => switch (to_type_info) { - .int => { - if (value_data.bitCount().? > to_type_info.int.bits) { - switch (value_data.*) { - .unsigned_int => |bi| try interpreter.recordError(node_idx, "invalid_cast", try std.fmt.allocPrint(interpreter.allocator, "integer value {d} cannot be coerced to type '{s}'", .{ bi, interpreter.formatTypeInfo(to_type_info) })), - .signed_int => |bi| try interpreter.recordError(node_idx, "invalid_cast", try std.fmt.allocPrint(interpreter.allocator, "integer value {d} cannot be coerced to type '{s}'", .{ bi, interpreter.formatTypeInfo(to_type_info) })), - .big_int => |bi| try interpreter.recordError(node_idx, "invalid_cast", try std.fmt.allocPrint(interpreter.allocator, "integer value {d} cannot be coerced to type '{s}'", .{ bi, interpreter.formatTypeInfo(to_type_info) })), - - else => unreachable, - } - return error.InvalidCast; - } - }, - else => error.InvalidCast, - }, - else => error.InvalidCast, - }; - - err catch |e| { - try interpreter.recordError(node_idx, "invalid_cast", try std.fmt.allocPrint(interpreter.allocator, "invalid cast from '{s}' to '{s}'", .{ interpreter.formatTypeInfo(from_type_info), interpreter.formatTypeInfo(to_type_info) })); - return e; - }; - - return Value{ - .interpreter = interpreter, - - .node_idx = node_idx, - .type = dest_type, - .value_data = value.value_data, - }; + destination_ty: IPIndex, + source_ty: IPIndex, +) error{ OutOfMemory, InvalidCast }!IPIndex { + _ = node_idx; + // TODO return errors + return try interpreter.ip.cast(interpreter.allocator, destination_ty, source_ty, builtin.target); } // Might be useful in the future @@ -404,10 +328,11 @@ pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || s ImportFailure, InvalidCast, }; + pub fn interpret( interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - scope: ?*InterpreterScope, + scope: ?Scope, options: InterpretOptions, ) InterpretError!InterpretResult { const tree = interpreter.getHandle().tree; @@ -429,20 +354,22 @@ pub fn interpret( // .tagged_union_enum_tag, // .tagged_union_enum_tag_trailing, .root, - .error_set_decl, + // .error_set_decl, // TODO => { - var container_scope = try interpreter.newScope(scope, node_idx); + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + + var container_scope = Scope{ + .interpreter = interpreter, + .parent = if (scope) |sc| sc.parent else 0, + .node_idx = node_idx, + .namespace = IPIndex.none, // declarations have not been resolved yet + }; var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){}; - errdefer fields.deinit(interpreter.allocator); - - // if (node_idx == 0) interpreter.root_type = cont_type; + defer fields.deinit(interpreter.allocator); var buffer: [2]Ast.Node.Index = undefined; const members = ast.declMembers(tree, node_idx, &buffer); - - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - for (members) |member| { const maybe_container_field: ?zig.Ast.full.ContainerField = switch (tags[member]) { .container_field => tree.containerField(member), @@ -451,44 +378,45 @@ pub fn interpret( else => null, }; - if (maybe_container_field) |field_info| { - var init_type_value = try (try interpreter.interpret(field_info.ast.type_expr, container_scope, .{})).getValue(); - - var default_value = if (field_info.ast.value_expr == 0) - IPIndex.none - else - (try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty - - if (init_type_value.ty != type_type) { - try interpreter.recordError( - field_info.ast.type_expr, - "expected_type", - try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(&interpreter.ip)}), - ); - continue; - } - - const name = tree.tokenSlice(field_info.ast.main_token); - - const field: InternPool.Struct.Field = .{ - .ty = init_type_value.val, - .default_value = default_value, - .alignent = 0, // TODO, - .is_comptime = false, // TODO - }; - - try fields.put(interpreter.arena.allocator(), name, field); - } else { + const field_info = maybe_container_field orelse { _ = try interpreter.interpret(member, container_scope, options); + continue; + }; + + var init_type_value = try (try interpreter.interpret(field_info.ast.type_expr, container_scope, .{})).getValue(); + + var default_value = if (field_info.ast.value_expr == 0) + IPIndex.none + else + (try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty + + if (init_type_value.ty != type_type) { + try interpreter.recordError( + field_info.ast.type_expr, + "expected_type", + try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(&interpreter.ip)}), + ); + continue; } + + const name = tree.tokenSlice(field_info.ast.main_token); + + const field: InternPool.Struct.Field = .{ + .ty = init_type_value.val, + .default_value = default_value, + .alignent = 0, // TODO, + .is_comptime = false, // TODO + }; + + try fields.put(interpreter.arena.allocator(), name, field); } const namespace = try interpreter.ip.get(interpreter.allocator, IPKey{ .namespace = .{ .parent = IPIndex.none, - .ty = undefined, // TODO + // .ty = struct_type, .decls = undefined, // TODO, - .usingnamespaces = .{}, + .usingnamespaces = &.{}, }, }); @@ -514,25 +442,25 @@ pub fn interpret( .simple_var_decl, => { // TODO: Add 0 check - const name = analysis.getDeclName(tree, node_idx).?; - if (scope.?.declarations.contains(name)) - return InterpretResult{ .nothing = {} }; + // const name = analysis.getDeclName(tree, node_idx).?; + // if (scope.?.declarations.contains(name)) + // return InterpretResult{ .nothing = {} }; - const decl = ast.varDecl(tree, node_idx).?; - if (decl.ast.init_node == 0) - return InterpretResult{ .nothing = {} }; + // const decl = ast.varDecl(tree, node_idx).?; + // if (decl.ast.init_node == 0) + // return InterpretResult{ .nothing = {} }; - try scope.?.declarations.put(interpreter.allocator, name, .{ - .scope = scope.?, - .node_idx = node_idx, - .name = name, - }); + // try scope.?.declarations.put(interpreter.allocator, name, .{ + // .scope = scope.?, + // .node_idx = node_idx, + // .name = name, + // }); // TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...) // if (scope.?.scopeKind() != .container) { - if (scope.?.node_idx != 0) - _ = try scope.?.declarations.getPtr(name).?.getValue(); + // if (scope.?.node_idx != 0) + // _ = try scope.?.declarations.getPtr(name).?.getValue(); return InterpretResult{ .nothing = {} }; }, @@ -541,13 +469,12 @@ pub fn interpret( .block_two, .block_two_semicolon, => { - // try interpreter.scopes.append(interpreter.allocator, .{ - // .node_idx = node_idx, - // .parent_scope = parent_scope_idx orelse std.math.maxInt(usize), - // }); - // const scope_idx = interpreter.scopes.items.len - 1; - - var block_scope = try interpreter.newScope(scope, node_idx); + var block_scope = Scope{ + .interpreter = interpreter, + .parent = if (scope) |sc| sc.parent else 0, + .node_idx = node_idx, + .namespace = IPIndex.none, + }; var buffer: [2]Ast.Node.Index = undefined; const statements = ast.blockStatements(tree, node_idx, &buffer).?; @@ -642,14 +569,21 @@ pub fn interpret( // TODO: Floats // Logic to find identifiers in accessible scopes - return InterpretResult{ .value = try (interpreter.huntItDown(scope.?, value, options) catch |err| { + const decl = interpreter.huntItDown(scope.?.namespace, value, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( node_idx, "undeclared_identifier", try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{value}), ); return err; - }).getValue() }; + }; + + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = decl.ty, + .val = decl.val, + } }; }, .field_access => { if (data[node_idx].rhs == 0) return error.CriticalAstFailure; @@ -658,8 +592,9 @@ pub fn interpret( var ir = try interpreter.interpret(data[node_idx].lhs, scope, options); var irv = try ir.getValue(); - var sub_scope = irv.value_data.type.getTypeInfo().getScopeOfType() orelse return error.IdentifierNotFound; - var scope_sub_decl = sub_scope.interpreter.huntItDown(sub_scope, rhs_str, options) catch |err| { + const namespace = interpreter.ip.indexToKey(irv.val).getNamespace() orelse return error.IdentifierNotFound; + + var scope_sub_decl = irv.interpreter.huntItDown(namespace, rhs_str, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( node_idx, "undeclared_identifier", @@ -668,9 +603,12 @@ pub fn interpret( return err; }; - return InterpretResult{ - .value = try scope_sub_decl.getValue(), - }; + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = scope_sub_decl.ty, + .val = scope_sub_decl.val, + } }; }, .grouped_expression => { return try interpreter.interpret(data[node_idx].lhs, scope, options); @@ -693,7 +631,13 @@ pub fn interpret( // TODO: Don't evaluate runtime ifs // if (options.observe_values) { const ir = try interpreter.interpret(iff.ast.cond_expr, scope, options); - if ((try ir.getValue()).value_data.bool) { + + const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); + const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); + + const condition = (try ir.getValue()).val; + std.debug.assert(condition == false_value or condition == true_value); + if (condition == true_value) { return try interpreter.interpret(iff.ast.then_expr, scope, options); } else { if (iff.ast.else_expr != 0) { @@ -709,9 +653,10 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.createValueData(.{ .bool = (try a.getValue()).eql(try b.getValue()) }), // TODO + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (a.value.val == b.value.val) .bool_true else .bool_false }), // TODO eql function required? }, }; + // a.getValue().eql(b.getValue()) }, .number_literal => { @@ -773,7 +718,7 @@ pub fn interpret( var to_value = try ir.getValue(); var from_value = (try (try interpreter.interpret(data[node_idx].rhs, scope.?, options)).getValue()); - to_value.value_data.* = (try interpreter.cast(node_idx, to_value.type, from_value)).value_data.*; + _ = try interpreter.cast(undefined, to_value.ty, from_value.ty); return InterpretResult{ .nothing = {} }; }, @@ -811,7 +756,7 @@ pub fn interpret( try writer.writeAll("indeterminate"); continue; }; - try writer.print("@as({s}, {s})", .{ interpreter.formatTypeInfo(value.type.getTypeInfo()), interpreter.formatValue(value) }); + try writer.print("@as({s}, {s})", .{ value.ty.fmtType(&interpreter.ip), value.val.fmtValue(value.ty, &interpreter.ip) }); if (index != params.len - 1) try writer.writeAll(", "); } @@ -840,8 +785,13 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.createType(node_idx, .{ .@"struct" = .{ .scope = try interpreter.newScope(null, 0) } }), - .val = try interpreter.createValueData(.{ .@"struct" = .{} }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ + .fields = .{}, + .namespace = IPIndex.none, + .layout = .Auto, + .backing_int_ty = IPIndex.none, + } }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .undefined_value }), } }; } @@ -855,7 +805,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = handle.interpreter.?.root_type.? }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = undefined }), // TODO } }; } @@ -877,13 +827,20 @@ pub fn interpret( const value = try (try interpreter.interpret(params[0], scope, options)).getValue(); const field_name = try (try interpreter.interpret(params[1], scope, options)).getValue(); - if (value.type.getTypeInfo() != .type) return error.InvalidBuiltin; - if (field_name.type.getTypeInfo() != .pointer) return error.InvalidBuiltin; // Check if it's a []const u8 + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - const ti = value.value_data.type.getTypeInfo(); - if (ti.getScopeOfType() == null) return error.InvalidBuiltin; + 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 has_decl = ti.getScopeOfType().?.declarations.contains(field_name.value_data.slice_of_const_u8); + const namespace_index = interpreter.ip.indexToKey(value.val).getNamespace() orelse return error.InvalidBuiltin; + const namespace = interpreter.ip.indexToKey(namespace_index).namespace; + + const name = interpreter.ip.indexToKey(field_name.val).bytes.data; // TODO add checks + + const has_decl = for (namespace.decls) |decl|{ + const decl_name = interpreter.ip.indexToKey(decl).declaration.name; + if(std.mem.eql(u8, decl_name, name)) break true; + } else false; return InterpretResult{ .value = Value{ .interpreter = interpreter, @@ -899,9 +856,16 @@ pub fn interpret( const as_type = try (try interpreter.interpret(params[0], scope, options)).getValue(); const value = try (try interpreter.interpret(params[1], scope, options)).getValue(); - if (as_type.type.getTypeInfo() != .type) return error.InvalidBuiltin; + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - return InterpretResult{ .value = try interpreter.cast(node_idx, as_type.value_data.type, value) }; + if (as_type.ty != type_type) return error.InvalidBuiltin; + + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = type_type, + .val = try interpreter.cast(node_idx, as_type.val, value.val), + } }; } log.err("Builtin not implemented: {s}", .{call_name}); @@ -916,11 +880,12 @@ pub fn interpret( .signedness = .unsigned, .bits = 8, } }), + .len = @intCast(u32, str.len), .sentinel = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_u64_value = 0 }), } }), .sentinel = .none, .alignment = 0, - .size = .one, + .size = .One, .is_const = true, .is_volatile = false, .is_allowzero = false, @@ -931,7 +896,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = string_literal_type, - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), // TODO + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = .{ .data = str } }), // TODO }; // TODO: Add type casting, sentinel @@ -954,14 +919,14 @@ pub fn interpret( // TODO: Resolve function type - const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ - .calling_convention = .Unspecified, - .alignment = 0, - .is_generic = false, - .is_var_args = false, - .return_type = IPIndex.none, - .args = .{}, - } }); + // const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ + // .calling_convention = .Unspecified, + // .alignment = 0, + // .is_generic = false, + // .is_var_args = false, + // .return_type = IPIndex.none, + // .args = &.{}, + // } }); // var it = func.iterate(&tree); // while (ast.nextFnParam(&it)) |param| { @@ -987,20 +952,21 @@ pub fn interpret( // if ((try interpreter.interpret(func.ast.return_type, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value| // fnd.return_type = value.value_data.@"type"; - var value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = function_type, - .value_data = IPIndex.none, // TODO - }; + // var value = Value{ + // .interpreter = interpreter, + // .node_idx = node_idx, + // .ty = function_type, + // .val = IPIndex.none, // TODO + // }; - const name = analysis.getDeclName(tree, node_idx).?; - try scope.?.declarations.put(interpreter.allocator, name, .{ - .scope = scope.?, - .node_idx = node_idx, - .name = name, - .value = value, - }); + // const name = analysis.getDeclName(tree, node_idx).?; + // var namespace = interpreter.ip.indexToKey(scope.?.namespace).namespace; + // try namespace.decls.put(interpreter.allocator, name, .{ + // .scope = scope.?, + // .node_idx = node_idx, + // .name = name, + // .value = value, + // }); return InterpretResult{ .nothing = {} }; }, @@ -1040,11 +1006,11 @@ pub fn interpret( const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); const bool_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }); const value = try result.getValue(); - if (value.type == bool_type) { + if (value.ty == bool_type) { const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); - const not_value = if (value.value == false_value) true_value else if (value.value == true_value) false_value else return error.InvalidOperation; + const not_value = if (value.val == false_value) true_value else if (value.val == true_value) false_value else return error.InvalidOperation; return InterpretResult{ .value = .{ .interpreter = interpreter, @@ -1066,10 +1032,10 @@ pub fn interpret( const value = (try result.getValue()); const pointer_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{ - .elem_type = value.type, + .elem_type = value.ty, .sentinel = .none, .alignment = 0, - .size = .one, + .size = .One, .is_const = false, .is_volatile = false, .is_allowzero = false, @@ -1080,7 +1046,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = pointer_type, - .val = try interpreter.createValueData(.{ .one_ptr = value.value_data }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .one_pointer = value.val }), } }; }, .deref => { @@ -1089,7 +1055,7 @@ pub fn interpret( const type_key = interpreter.ip.indexToKey(value.ty); - if (type_key != .pointer) { + if (type_key != .pointer_type) { try interpreter.recordError(node_idx, "invalid_deref", try std.fmt.allocPrint(interpreter.allocator, "cannot deference non-pointer", .{})); return error.InvalidOperation; } @@ -1100,7 +1066,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = type_key.pointer_type.elem_type, - .val = value.value_data.one_ptr, + .val = interpreter.ip.indexToKey(value.val).one_pointer, } }; }, else => { @@ -1111,7 +1077,7 @@ pub fn interpret( } pub const CallResult = struct { - scope: *InterpreterScope, + scope: *Scope, result: union(enum) { value: Value, nothing, @@ -1120,7 +1086,7 @@ pub const CallResult = struct { pub fn call( interpreter: *ComptimeInterpreter, - scope: ?*InterpreterScope, + scope: ?Scope, func_node_idx: Ast.Node.Index, arguments: []const Value, options: InterpretOptions, @@ -1136,6 +1102,8 @@ pub fn call( // TODO: Make argument scope to evaluate arguments in var fn_scope = try interpreter.newScope(scope, func_node_idx); + + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); var buf: [1]Ast.Node.Index = undefined; var proto = ast.fnProto(tree, func_node_idx, &buf).?; @@ -1145,11 +1113,11 @@ pub fn call( while (ast.nextFnParam(&arg_it)) |param| { if (arg_index >= arguments.len) return error.MissingArguments; var tex = try (try interpreter.interpret(param.type_expr, fn_scope, options)).getValue(); - if (tex.type.getTypeInfo() != .type) { + if (tex.ty != type_type) { try interpreter.recordError( param.type_expr, "expected_type", - std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{s}'", .{interpreter.formatTypeInfo(tex.type.getTypeInfo())}) catch return error.CriticalAstFailure, + std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{tex.ty.fmtType(&interpreter.ip)}) catch return error.CriticalAstFailure, ); return error.InvalidCast; } @@ -1158,7 +1126,7 @@ pub fn call( .scope = fn_scope, .node_idx = param.type_expr, .name = tree.tokenSlice(nt), - .value = try interpreter.cast(arguments[arg_index].node_idx, tex.value_data.type, arguments[arg_index]), + .value = try interpreter.cast(arguments[arg_index].node_idx, tex.val, arguments[arg_index]), }; try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl); arg_index += 1; diff --git a/src/InternPool.zig b/src/InternPool.zig index 0f56419..465959b 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -154,7 +154,7 @@ pub const Namespace = struct { /// always points to Namespace or Index.none parent: Index, /// Will be a struct, enum, union, or opaque. - ty: Index, + // ty: Index, /// always points to Decl decls: []const Index, usingnamespaces: []const Index, @@ -195,6 +195,7 @@ pub const Key = union(enum) { type_value: Index, bytes: Bytes, + one_pointer: Index, // slice // error @@ -377,6 +378,11 @@ pub const Key = union(enum) { .float_80_value => .float_f80, .float_128_value => .float_f128, .type_value => .type, + + .declaration => .declaration, + .namespace => .namespace, + .bytes => .bytes, + .one_pointer => .one_pointer, }; } @@ -511,6 +517,15 @@ pub const Key = union(enum) { else => false, }; } + + pub fn getNamespace(ty: Key) ?Index { + return switch (ty) { + .struct_type => |struct_info| struct_info.namespace, + .enum_type => |enum_info| enum_info.namespace, + .union_type => |union_info| union_info.namespace, + else => null, + }; + } pub const TypeFormatContext = struct { ty: Index, @@ -695,6 +710,7 @@ pub const Key = union(enum) { .type_value, .bytes, + .one_pointer, => unreachable, } } @@ -788,6 +804,7 @@ pub const Key = union(enum) { .type_value, .bytes, + .one_pointer, => unreachable, } } @@ -908,6 +925,20 @@ pub const Tag = enum(u8) { /// A type value. /// data is Index. type, + + /// A declaration. + /// data is payload to Decl. + declaration, + /// A namespace. + /// data is payload to Namespace. + namespace, + + /// A byte sequence value. + /// data is payload to data begin and length. + bytes, + /// A single pointer value. + /// data is index to value. + one_pointer, }; pub const Simple = enum(u32) { @@ -997,6 +1028,9 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .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"), }; } @@ -1032,7 +1066,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }, .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 = ty }, + .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 :( }; try ip.items.append(gpa, item); @@ -1058,6 +1094,10 @@ fn extraData(ip: InternPool, comptime T: type, index: usize) T { // UTILITY // --------------------------------------------- +pub fn cast(ip: *InternPool, gpa: Allocator, destination_ty: Index, source_ty: Index, target: std.Target) Allocator.Error!Index { + return resolvePeerTypes(ip, gpa, &.{destination_ty, source_ty}, target); +} + pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, target: std.Target) Allocator.Error!Index { switch (types.len) { 0 => return Key{ .simple = .noreturn }, diff --git a/src/analysis.zig b/src/analysis.zig index 757d602..d2b2d12 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -779,8 +779,15 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl }; var interpreter = handle.interpreter.?; + var root_scope = ComptimeInterpreter.Scope{ + .interpreter = interpreter, + .parent = 0, + .node_idx = 0, + .namespace = ComptimeInterpreter.IPIndex.none, + }; + // TODO: Start from current/nearest-current scope - const result = interpreter.interpret(node, interpreter.root_type.?.getTypeInfo().getScopeOfType().?, .{}) catch |err| { + const result = interpreter.interpret(node, root_scope, .{}) catch |err| { log.err("Interpreter error: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -795,15 +802,22 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl return null; }; - const ti = val.@"type".getTypeInfo(); - if (ti != .@"type") { - log.err("Not a type: { }", .{interpreter.formatTypeInfo(ti)}); + const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.IPKey{ .simple = .type }); + if (val.ty != type_type) { + log.err("Not a type: { }", .{val.ty.fmtType(&interpreter.ip)}); return null; } return TypeWithHandle{ .type = .{ - .data = .{ .@"comptime" = .{ .interpreter = interpreter, .type = val.value_data.@"type" } }, + .data = .{ .@"comptime" = .{ + .interpreter = interpreter, + .type = ComptimeInterpreter.Type{ + .interpreter = interpreter, + .node_idx = val.node_idx, + .ty = val.val, + }, + } }, .is_type_val = true, }, .handle = node_handle.handle, From 6118686d005544a3ab70258bbec778b72f11e6b6 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 27 Dec 2022 03:02:07 +0100 Subject: [PATCH 03/78] fix remaining compiler errors --- src/ComptimeInterpreter.zig | 188 +++++++---------------- src/DocumentStore.zig | 2 +- src/InternPool.zig | 295 +++++++++++++++++++++--------------- src/analysis.zig | 10 +- 4 files changed, 232 insertions(+), 263 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 7c33fe9..a9c9c2b 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -77,93 +77,6 @@ pub const Value = struct { val: IPIndex, }; -pub const FieldDefinition = struct { - node_idx: Ast.Node.Index, - /// Store name so tree doesn't need to be used to access field name - /// When the field is a tuple field, `name` will be an empty slice - name: []const u8, - ty: Type, - default_value: ?Value, -}; - -pub const Declaration = struct { - scope: u32, - - node_idx: Ast.Node.Index, - /// Store name so tree doesn't need to be used to access declaration name - name: []const u8, - - /// If value is null, declaration has not been interpreted yet - value: ?Value = null, - - // TODO: figure this out - // pub const DeclarationKind = enum{variable, function}; - // pub fn declarationKind(declaration: Declaration, tree: Ast) DeclarationKind { - // return switch(tree.nodes.items(.tag)[declaration.node_idx]) { - // .fn_proto, - // .fn_proto_one, - // .fn_proto_simple, - // .fn_proto_multi, - // .fn_decl - // } - // } - - pub fn getValue(decl: *Declaration) InterpretError!Value { - var interpreter = decl.scope.interpreter; - const tree = decl.scope.interpreter.getHandle().tree; - const tags = tree.nodes.items(.tag); - - if (decl.value == null) { - switch (tags[decl.node_idx]) { - .global_var_decl, - .local_var_decl, - .aligned_var_decl, - .simple_var_decl, - => { - const var_decl = ast.varDecl(tree, decl.node_idx).?; - if (var_decl.ast.init_node == 0) - return error.CriticalAstFailure; - - var value = try (try interpreter.interpret(var_decl.ast.init_node, decl.scope, .{})).getValue(); - - if (var_decl.ast.type_node != 0) { - var type_val = try (try interpreter.interpret(var_decl.ast.type_node, decl.scope, .{})).getValue(); - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - if (type_val.ty != type_type) { - try interpreter.recordError( - decl.node_idx, - "expected_type", - std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{type_val.ty.fmtType(&interpreter.ip)}) catch return error.CriticalAstFailure, - ); - return error.InvalidCast; - } - value = try interpreter.cast(var_decl.ast.type_node, type_val.value_data.type, value); - } - - decl.value = value; - }, - else => @panic("No other case supported for lazy declaration evaluation"), - } - } - - return decl.value.?; - } - - pub fn isConstant(declaration: Declaration) bool { - const tree = declaration.scope.interpreter.getHandle().tree; - return switch (tree.nodes.items(.tag)[declaration.node_idx]) { - .global_var_decl, - .local_var_decl, - .aligned_var_decl, - .simple_var_decl, - => { - return tree.tokenSlice(ast.varDecl(tree, declaration.node_idx).?.ast.mut_token).len != 3; - }, - else => false, - }; - } -}; - // pub const Comptimeness = enum { @"comptime", runtime }; pub const Scope = struct { @@ -217,21 +130,6 @@ pub const Scope = struct { } }; -pub fn newScope( - interpreter: *ComptimeInterpreter, - maybe_parent: ?Scope, - node_idx: Ast.Node.Index, -) std.mem.Allocator.Error!*Scope { - var ls = try interpreter.allocator.create(Scope); - if (maybe_parent) |parent| try parent.child_scopes.append(interpreter.allocator, ls); - ls.* = .{ - .interpreter = interpreter, - .parent = maybe_parent, - .node_idx = node_idx, - }; - return ls; -} - pub const InterpretResult = union(enum) { @"break": ?[]const u8, break_with_value: struct { @@ -332,7 +230,7 @@ pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || s pub fn interpret( interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - scope: ?Scope, + scope: u32, options: InterpretOptions, ) InterpretError!InterpretResult { const tree = interpreter.getHandle().tree; @@ -358,12 +256,13 @@ pub fn interpret( => { const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - var container_scope = Scope{ + try interpreter.scopes.append(interpreter.allocator, .{ .interpreter = interpreter, - .parent = if (scope) |sc| sc.parent else 0, + .parent = scope, .node_idx = node_idx, .namespace = IPIndex.none, // declarations have not been resolved yet - }; + }); + const container_scope = @intCast(u32, interpreter.scopes.len - 1); var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){}; defer fields.deinit(interpreter.allocator); @@ -469,12 +368,13 @@ pub fn interpret( .block_two, .block_two_semicolon, => { - var block_scope = Scope{ + try interpreter.scopes.append(interpreter.allocator, .{ .interpreter = interpreter, - .parent = if (scope) |sc| sc.parent else 0, + .parent = scope, .node_idx = node_idx, .namespace = IPIndex.none, - }; + }); + const block_scope = @intCast(u32, interpreter.scopes.len - 1); var buffer: [2]Ast.Node.Index = undefined; const statements = ast.blockStatements(tree, node_idx, &buffer).?; @@ -483,7 +383,7 @@ pub fn interpret( const ret = try interpreter.interpret(idx, block_scope, options); switch (ret) { .@"break" => |lllll| { - const maybe_block_label_string = if (scope.?.getLabel()) |i| tree.tokenSlice(i) else null; + const maybe_block_label_string = if (interpreter.scopes.get(scope).getLabel()) |i| tree.tokenSlice(i) else null; if (lllll) |l| { if (maybe_block_label_string) |ls| { if (std.mem.eql(u8, l, ls)) { @@ -495,7 +395,7 @@ pub fn interpret( } }, .break_with_value => |bwv| { - const maybe_block_label_string = if (scope.?.getLabel()) |i| tree.tokenSlice(i) else null; + const maybe_block_label_string = if (interpreter.scopes.get(scope).getLabel()) |i| tree.tokenSlice(i) else null; if (bwv.label) |l| { if (maybe_block_label_string) |ls| { @@ -569,7 +469,8 @@ pub fn interpret( // TODO: Floats // Logic to find identifiers in accessible scopes - const decl = interpreter.huntItDown(scope.?.namespace, value, options) catch |err| { + const namespace = interpreter.scopes.items(.namespace)[scope]; + const decl = interpreter.huntItDown(namespace, value, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( node_idx, "undeclared_identifier", @@ -710,13 +611,13 @@ pub fn interpret( // TODO: Actually consider operators if (std.mem.eql(u8, tree.getNodeSource(data[node_idx].lhs), "_")) { - _ = try interpreter.interpret(data[node_idx].rhs, scope.?, options); + _ = try interpreter.interpret(data[node_idx].rhs, scope, options); return InterpretResult{ .nothing = {} }; } var ir = try interpreter.interpret(data[node_idx].lhs, scope, options); var to_value = try ir.getValue(); - var from_value = (try (try interpreter.interpret(data[node_idx].rhs, scope.?, options)).getValue()); + var from_value = (try (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue()); _ = try interpreter.cast(undefined, to_value.ty, from_value.ty); @@ -756,7 +657,7 @@ pub fn interpret( try writer.writeAll("indeterminate"); continue; }; - try writer.print("@as({s}, {s})", .{ value.ty.fmtType(&interpreter.ip), value.val.fmtValue(value.ty, &interpreter.ip) }); + try writer.print("@as({}, {})", .{ value.ty.fmtType(&interpreter.ip), value.val.fmtValue(value.ty, &interpreter.ip) }); if (index != params.len - 1) try writer.writeAll(", "); } @@ -801,12 +702,14 @@ pub fn interpret( var handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure; try interpreter.document_store.ensureInterpreterExists(handle.uri); - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = undefined }), // TODO - } }; + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = undefined }), // TODO + }, + }; } if (std.mem.eql(u8, call_name, "@TypeOf")) { @@ -837,9 +740,9 @@ pub fn interpret( const name = interpreter.ip.indexToKey(field_name.val).bytes.data; // TODO add checks - const has_decl = for (namespace.decls) |decl|{ + const has_decl = for (namespace.decls) |decl| { const decl_name = interpreter.ip.indexToKey(decl).declaration.name; - if(std.mem.eql(u8, decl_name, name)) break true; + if (std.mem.eql(u8, decl_name, name)) break true; } else false; return InterpretResult{ .value = Value{ @@ -989,10 +892,10 @@ pub fn interpret( try args.append(interpreter.allocator, try (try interpreter.interpret(param, scope, .{})).getValue()); } - const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, interpreter.root_type.?.getTypeInfo().getScopeOfType().?, .{}); + const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, scope, .{}); const func_id_val = try func_id_result.getValue(); - const call_res = try interpreter.call(interpreter.root_type.?.getTypeInfo().getScopeOfType().?, func_id_val.node_idx, args.items, options); + const call_res = try interpreter.call(scope, func_id_val.node_idx, args.items, options); // defer call_res.scope.deinit(); // TODO: Figure out call result memory model; this is actually fine because newScope // makes this a child of the decl scope which is freed on refresh... in theory @@ -1077,7 +980,7 @@ pub fn interpret( } pub const CallResult = struct { - scope: *Scope, + scope: u32, result: union(enum) { value: Value, nothing, @@ -1086,7 +989,7 @@ pub const CallResult = struct { pub fn call( interpreter: *ComptimeInterpreter, - scope: ?Scope, + scope: u32, func_node_idx: Ast.Node.Index, arguments: []const Value, options: InterpretOptions, @@ -1101,9 +1004,15 @@ pub fn call( if (tags[func_node_idx] != .fn_decl) return error.CriticalAstFailure; // TODO: Make argument scope to evaluate arguments in - var fn_scope = try interpreter.newScope(scope, func_node_idx); - - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + try interpreter.scopes.append(interpreter.allocator, Scope{ + .interpreter = interpreter, + .parent = scope, + .node_idx = func_node_idx, + .namespace = IPIndex.none, + }); + const fn_scope = @intCast(u32, interpreter.scopes.len - 1); + + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); var buf: [1]Ast.Node.Index = undefined; var proto = ast.fnProto(tree, func_node_idx, &buf).?; @@ -1122,13 +1031,18 @@ pub fn call( return error.InvalidCast; } if (param.name_token) |nt| { - const decl = Declaration{ - .scope = fn_scope, - .node_idx = param.type_expr, - .name = tree.tokenSlice(nt), - .value = try interpreter.cast(arguments[arg_index].node_idx, tex.val, arguments[arg_index]), - }; - try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl); + _ = nt; + // const decl = InternPool.Decl{ + // .name = tree.tokenSlice(nt), + // .ty = tex.val, + // .val = try interpreter.cast(arguments[arg_index].node_idx, tex.val, arguments[arg_index].val), + // .alignment = 0, // TODO + // .address_space = .generic, // TODO + // .is_pub = true, // TODO + // .is_exported = false, // TODO + // }; + // TODO + // try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl); arg_index += 1; } } diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 8276b08..f5cd582 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -949,6 +949,6 @@ pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void { .uri = uri, }; handle.interpreter = int; - _ = try int.interpret(0, null, .{}); + _ = try int.interpret(0, 0, .{}); } } diff --git a/src/InternPool.zig b/src/InternPool.zig index 465959b..9de7018 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -255,6 +255,8 @@ pub const Key = union(enum) { .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)), + + .bytes => |bytes| hasher.update(bytes.data), inline else => |info| std.hash.autoHash(&hasher, info), // TODO sad stage1 noises :( } return @truncate(u32, hasher.final()); @@ -303,9 +305,9 @@ pub const Key = union(enum) { return true; }, .union_type => |union_info| { - if (union_info.tag_type != b.union_info.tag_type) return false; - if (union_info.layout != b.union_info.layout) return false; - if (union_info.fields.count() != b.union_info.fields.count()) return false; + 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.count() != b.union_type.fields.count()) return false; @panic("TODO: implement union equality"); }, .tuple_type => |tuple_info| { @@ -331,17 +333,16 @@ pub const Key = union(enum) { return true; }, .namespace => |namespace_info| { - if (!std.meta.eql(namespace_info.parent, b.namespace.parent)) return false; - if (namespace_info.ty != b.namespace.ty) return false; + if (namespace_info.parent != b.namespace.parent) return false; if (namespace_info.decls.len != b.namespace.decls.len) return false; if (namespace_info.usingnamespaces.len != b.namespace.usingnamespaces.len) return false; for (namespace_info.decls) |decl, i| { - if (!decl != b.namespace.decls[i]) return false; + if (decl != b.namespace.decls[i]) return false; } for (namespace_info.usingnamespaces) |namespace, i| { - if (!namespace != b.namespace.usingnamespaces[i]) return false; + if (namespace != b.namespace.usingnamespaces[i]) return false; } return false; }, @@ -431,37 +432,71 @@ pub const Key = union(enum) { .bool_false, => unreachable, }, + + .int_type => .Int, + .pointer_type => .Pointer, + .array_type => .Array, + .struct_type => .Struct, + .optional_type => .Optional, + .error_union_type => .ErrorUnion, + .error_set_type => .ErrorSet, + .enum_type => .Enum, + .function_type => .Fn, + .union_type => .Union, + .tuple_type => .Struct, // TODO this correct? + .vector_type => .Vector, + + .declaration, + .namespace, + => unreachable, + + .int_u64_value, + .int_i64_value, + .int_big_value, + .float_16_value, + .float_32_value, + .float_64_value, + .float_80_value, + .float_128_value, + .type_value, + => unreachable, + + .bytes, + .one_pointer, + => unreachable, }; } /// Asserts the type is an integer, enum, error set, packed struct, or vector of one of them. - pub fn intInfo(ty: Key, target: std.Target, ip: InternPool) Int { + pub fn intInfo(ty: Key, target: std.Target, ip: *const InternPool) Int { var key: Key = ty; while (true) switch (key) { .simple => |simple| switch (simple) { - .usize => return .{ .signdness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, - .isize => return .{ .signdness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, + .usize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, + .isize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, // TODO correctly resolve size based on `target` - .c_short => return .{ .signdness = .signed, .bits = @bitSizeOf(c_short) }, - .c_ushort => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_ushort) }, - .c_int => return .{ .signdness = .signed, .bits = @bitSizeOf(c_int) }, - .c_uint => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_uint) }, - .c_long => return .{ .signdness = .signed, .bits = @bitSizeOf(c_long) }, - .c_ulong => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_ulong) }, - .c_longlong => return .{ .signdness = .signed, .bits = @bitSizeOf(c_longlong) }, - .c_ulonglong => return .{ .signdness = .unsigned, .bits = @bitSizeOf(c_ulonglong) }, - .c_longdouble => return .{ .signdness = .signed, .bits = @bitSizeOf(c_longdouble) }, + .c_short => return .{ .signedness = .signed, .bits = @bitSizeOf(c_short) }, + .c_ushort => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ushort) }, + .c_int => return .{ .signedness = .signed, .bits = @bitSizeOf(c_int) }, + .c_uint => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_uint) }, + .c_long => return .{ .signedness = .signed, .bits = @bitSizeOf(c_long) }, + .c_ulong => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ulong) }, + .c_longlong => return .{ .signedness = .signed, .bits = @bitSizeOf(c_longlong) }, + .c_ulonglong => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ulonglong) }, + .c_longdouble => return .{ .signedness = .signed, .bits = @bitSizeOf(c_longdouble) }, // TODO revisit this when error sets support custom int types (comment taken from zig codebase) .anyerror => return .{ .signedness = .unsigned, .bits = 16 }, + + else => unreachable, }, .int_type => |int_info| return int_info, .enum_type => @panic("TODO"), .struct_type => |struct_info| { std.debug.assert(struct_info.layout == .Packed); - key = struct_info.backing_int_ty; + key = ip.indexToKey(struct_info.backing_int_ty); }, // TODO revisit this when error sets support custom int types (comment taken from zig codebase) .error_set_type => return .{ .signedness = .unsigned, .bits = 16 }, @@ -469,7 +504,6 @@ pub const Key = union(enum) { std.debug.assert(vector_info.len == 1); key = ip.indexToKey(vector_info.child); }, - else => unreachable, }; } @@ -511,13 +545,20 @@ pub const Key = union(enum) { }; } + pub fn isConstPtr(ty: Key) bool { + return switch (ty) { + .pointer_type => |pointer_info| pointer_info.is_const, + else => false, + }; + } + pub fn isSlice(ty: Key) bool { return switch (ty) { .pointer_type => |pointer_info| pointer_info.size == .Slice, else => false, }; } - + pub fn getNamespace(ty: Key) ?Index { return switch (ty) { .struct_type => |struct_info| struct_info.namespace, @@ -1030,7 +1071,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .type => .{ .type_value = @intToEnum(Index, data) }, .bytes => unreachable, // TODO - .one_pointer => .{.one_pointer = @intToEnum(Index, data)}, + .one_pointer => .{ .one_pointer = @intToEnum(Index, data) }, else => @panic("TODO"), }; } @@ -1068,7 +1109,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .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)}, + .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 :( }; try ip.items.append(gpa, item); @@ -1095,26 +1136,31 @@ fn extraData(ip: InternPool, comptime T: type, index: usize) T { // --------------------------------------------- pub fn cast(ip: *InternPool, gpa: Allocator, destination_ty: Index, source_ty: Index, target: std.Target) Allocator.Error!Index { - return resolvePeerTypes(ip, gpa, &.{destination_ty, source_ty}, target); + return resolvePeerTypes(ip, gpa, &.{ destination_ty, source_ty }, target); } pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, target: std.Target) Allocator.Error!Index { switch (types.len) { - 0 => return Key{ .simple = .noreturn }, - 1 => types[0], + 0 => return try ip.get(gpa, .{ .simple = .noreturn }), + 1 => return types[0], + else => {}, } + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + var arena = arena_allocator.allocator(); + var chosen = types[0]; var any_are_null = false; var seen_const = false; var convert_to_slice = false; var chosen_i: usize = 0; for (types[1..]) |candidate, candidate_i| { + if (candidate == chosen) continue; + const candidate_key: Key = ip.indexToKey(candidate); const chosen_key = ip.indexToKey(chosen); - if (candidate_key == chosen_key) continue; - switch (candidate_key) { .simple => |candidate_simple| switch (candidate_simple) { // TODO usize, isize @@ -1133,6 +1179,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t }, else => {}, }, + else => {}, }, .noreturn, .undefined_type => continue, @@ -1148,6 +1195,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .usize, .isize => continue, .comptime_int => unreachable, .comptime_float => continue, + else => {}, }, .int_type => continue, .pointer_type => |chosen_info| if (chosen_info.size == .C) continue, @@ -1161,6 +1209,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t chosen_i = candidate_i + 1; continue; }, + else => {}, }, else => {}, }, @@ -1242,13 +1291,13 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t const chosen_elem_ty = chosen_elem_info.array_type.child; const cand_elem_ty = candidate_elem_info.array_type.child; - const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target); + const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, chosen_elem_ty, cand_elem_ty, !chosen_info.is_const, target); if (chosen_ok) { convert_to_slice = true; continue; } - const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, cand_elem_ty, chosen_elem_ty, candidate_info.mutable, target); + const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, cand_elem_ty, chosen_elem_ty, !candidate_info.is_const, target); if (cand_ok) { convert_to_slice = true; chosen = candidate; @@ -1268,8 +1317,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t // the one we will keep. If they're both OK then we keep the // C pointer since it matches both single and many pointers. if (candidate_info.size == .C or chosen_info.size == .C) { - const cand_ok = .ok == try ip.coerceInMemoryAllowed(candidate_info.elem_type, chosen_info.elem_type, candidate_info.mutable, target); - const chosen_ok = .ok == try ip.coerceInMemoryAllowed(chosen_info.elem_type, candidate_info.elem_type, chosen_info.mutable, target); + const cand_ok = .ok == try ip.coerceInMemoryAllowed(arena, candidate_info.elem_type, chosen_info.elem_type, !candidate_info.is_const, target); + const chosen_ok = .ok == try ip.coerceInMemoryAllowed(arena, chosen_info.elem_type, candidate_info.elem_type, !chosen_info.is_const, target); if (cand_ok) { if (!chosen_ok or chosen_info.size != .C) { @@ -1327,14 +1376,16 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t } } }, - .function_type => { - if (candidate_info.is_const and - ip.indexToKey(candidate_info.elem_type) == .function_type and - .ok == try ip.coerceInMemoryAllowedFns(chosen, candidate_info.pointee_type, target)) - { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; + .function_type => |chosen_info| { + if (candidate_info.is_const) { + const candidate_elem_key = ip.indexToKey(candidate_info.elem_type); + if (candidate_elem_key == .function_type and + .ok == try ip.coerceInMemoryAllowedFns(arena, chosen_info, candidate_elem_key.function_type, target)) + { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } } }, else => {}, @@ -1349,8 +1400,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => false, }; - if ((try ip.coerceInMemoryAllowed(chosen, candidate_info.payload_type, false, target)) == .ok) { - seen_const = seen_const or candidate_info.payload_type.isConstPtr(); + if ((try ip.coerceInMemoryAllowed(arena, chosen, candidate_info.payload_type, false, target)) == .ok) { + seen_const = seen_const or ip.indexToKey(candidate_info.payload_type).isConstPtr(); any_are_null = true; continue; } @@ -1374,44 +1425,42 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t switch (chosen_key) { .simple => |simple| switch (simple) { - .noreturn, .undefined_type => { + .noreturn, + .undefined_type, + => { chosen = candidate; chosen_i = candidate_i + 1; continue; }, - } - .NoReturn, - .Undefined => { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; + .null_type => { + any_are_null = true; + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + else => {}, }, - .Null => { - any_are_null = true; - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - }, - .Optional => { - if ((try ip.coerceInMemoryAllowed(chosen_key.optional_type.payload_type, candidate, false, target)) == .ok) { + .optional_type => |chosen_info| { + if ((try ip.coerceInMemoryAllowed(arena, chosen_info.payload_type, candidate, false, target)) == .ok) { continue; } - if ((try ip.coerceInMemoryAllowed(candidate, chosen_key.optional_type.payload_type, false, target)) == .ok) { + if ((try ip.coerceInMemoryAllowed(arena, candidate, chosen_info.payload_type, false, target)) == .ok) { any_are_null = true; chosen = candidate; chosen_i = candidate_i + 1; continue; } }, - .ErrorUnion => { - const payload_ty = chosen_key.error_union_type.payload_type; - if ((try ip.coerceInMemoryAllowed(payload_ty, candidate, false, target)) == .ok) { + .error_union_type => |chosen_info| { + if ((try ip.coerceInMemoryAllowed(arena, chosen_info.payload_type, candidate, false, target)) == .ok) { continue; } }, else => {}, } } + + @panic("TODO"); } const InMemoryCoercionResult = union(enum) { @@ -1542,7 +1591,7 @@ fn coerceInMemoryAllowed( src_ty: Index, dest_is_mut: bool, target: std.Target, -) !InMemoryCoercionResult { +) error{OutOfMemory}!InMemoryCoercionResult { if (dest_ty == src_ty) return .ok; const dest_key = ip.indexToKey(dest_ty); @@ -1576,7 +1625,7 @@ fn coerceInMemoryAllowed( if (dest_tag == .Float and src_tag == .Float and // this is an optimization because only a long double can have the same size as a other Float // SAFETY: every Float is a Simple - dest_key.simple == .c_longdouble or src_tag.simple == .c_longdouble) + dest_key.simple == .c_longdouble or src_key.simple == .c_longdouble) { const dest_bits = dest_key.floatBits(target); const src_bits = src_key.floatBits(target); @@ -1587,24 +1636,26 @@ fn coerceInMemoryAllowed( const maybe_dest_ptr_ty = try ip.typePtrOrOptionalPtrTy(dest_ty); const maybe_src_ptr_ty = try ip.typePtrOrOptionalPtrTy(src_ty); if (maybe_dest_ptr_ty != Index.none and maybe_src_ptr_ty != Index.none) { - return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, maybe_dest_ptr_ty, maybe_src_ptr_ty, dest_is_mut, target); + @panic("TODO: implement coerceInMemoryAllowedPtrs"); + // return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, maybe_dest_ptr_ty, maybe_src_ptr_ty, dest_is_mut, target); } // Slices if (dest_key.isSlice() and src_key.isSlice()) { - return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); + @panic("TODO: implement coerceInMemoryAllowedPtrs"); + // return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); } // Functions if (dest_tag == .Fn and src_tag == .Fn) { - return try ip.coerceInMemoryAllowedFns(dest_ty, src_ty, target); + return try ip.coerceInMemoryAllowedFns(arena, dest_key.function_type, src_key.function_type, target); } // Error Unions if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { const dest_payload = dest_key.error_union_type.payload_type; const src_payload = src_key.error_union_type.payload_type; - const child = try ip.coerceInMemoryAllowed(dest_payload, src_payload, dest_is_mut, target); + const child = try ip.coerceInMemoryAllowed(arena, dest_payload, src_payload, dest_is_mut, target); if (child != .ok) { return InMemoryCoercionResult{ .error_union_payload = .{ .child = try child.dupe(arena), @@ -1612,7 +1663,9 @@ fn coerceInMemoryAllowed( .wanted = dest_payload, } }; } - return try ip.coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target); + const dest_set = dest_key.error_union_type.error_set_type; + const src_set = src_key.error_union_type.error_set_type; + return try ip.coerceInMemoryAllowed(arena, dest_set, src_set, dest_is_mut, target); } // Error Sets @@ -1623,8 +1676,8 @@ fn coerceInMemoryAllowed( // Arrays if (dest_tag == .Array and src_tag == .Array) { - const dest_info = dest_key.array_type.len; - const src_info = src_key.array_type.len; + const dest_info = dest_key.array_type; + const src_info = src_key.array_type; if (dest_info.len != src_info.len) { return InMemoryCoercionResult{ .array_len = .{ .actual = src_info.len, @@ -1632,24 +1685,24 @@ fn coerceInMemoryAllowed( } }; } - const child = try ip.coerceInMemoryAllowed(dest_key.array_type.child, src_key.array_type.child, dest_is_mut, target); + const child = try ip.coerceInMemoryAllowed(arena, dest_info.child, src_info.child, dest_is_mut, target); if (child != .ok) { return InMemoryCoercionResult{ .array_elem = .{ .child = try child.dupe(arena), - .actual = src_key.array_type.child, - .wanted = dest_key.array_type.child, + .actual = src_info.child, + .wanted = dest_info.child, } }; } - const ok_sent = dest_key.array_type.sentinel == Index.none or - (src_key.array_type.sentinel != Index.none and - dest_key.array_type.sentinel == src_key.array_type.sentinel // is this enough for a value equality check? + const ok_sent = dest_info.sentinel == Index.none or + (src_info.sentinel != Index.none and + dest_info.sentinel == src_info.sentinel // is this enough for a value equality check? ); if (!ok_sent) { return InMemoryCoercionResult{ .array_sentinel = .{ .actual = src_info.sentinel, .wanted = dest_info.sentinel, - .ty = dest_key.array_type.child, + .ty = dest_info.child, } }; } return .ok; @@ -1669,7 +1722,7 @@ fn coerceInMemoryAllowed( const dest_elem_ty = dest_key.vector_type.child; const src_elem_ty = src_key.vector_type.child; - const child = try ip.coerceInMemoryAllowed(dest_elem_ty, src_elem_ty, dest_is_mut, target); + const child = try ip.coerceInMemoryAllowed(arena, dest_elem_ty, src_elem_ty, dest_is_mut, target); if (child != .ok) { return InMemoryCoercionResult{ .vector_elem = .{ .child = try child.dupe(arena), @@ -1693,7 +1746,7 @@ fn coerceInMemoryAllowed( const dest_child_type = dest_key.optional_type.payload_type; const src_child_type = src_key.optional_type.payload_type; - const child = try ip.coerceInMemoryAllowed(dest_child_type, src_child_type, dest_is_mut, target); + const child = try ip.coerceInMemoryAllowed(arena, dest_child_type, src_child_type, dest_is_mut, target); if (child != .ok) { return InMemoryCoercionResult{ .optional_child = .{ .child = try child.dupe(arena), @@ -1865,13 +1918,10 @@ fn coerceInMemoryAllowed( fn coerceInMemoryAllowedFns( ip: *InternPool, arena: std.mem.Allocator, - dest_ty: Index, - src_ty: Index, + dest_info: Fn, + src_info: Fn, target: std.Target, ) !InMemoryCoercionResult { - const dest_info = dest_ty.fnInfo(); - const src_info = src_ty.fnInfo(); - if (dest_info.is_var_args != src_info.is_var_args) { return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; } @@ -1880,15 +1930,18 @@ fn coerceInMemoryAllowedFns( return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; } - if (dest_info.cc != src_info.cc) { + if (dest_info.calling_convention != src_info.calling_convention) { return InMemoryCoercionResult{ .fn_cc = .{ - .actual = src_info.cc, - .wanted = dest_info.cc, + .actual = src_info.calling_convention, + .wanted = dest_info.calling_convention, } }; } - if (!src_info.return_type.isNoReturn()) { - const rt = try ip.coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target); + const return_type_key = ip.indexToKey(src_info.return_type); + const is_noreturn = return_type_key == .simple and return_type_key.simple == .noreturn; + + if (!is_noreturn) { + const rt = try ip.coerceInMemoryAllowed(arena, dest_info.return_type, src_info.return_type, false, target); if (rt != .ok) { return InMemoryCoercionResult{ .fn_return_type = .{ .child = try rt.dupe(arena), @@ -1898,41 +1951,43 @@ fn coerceInMemoryAllowedFns( } } - if (dest_info.param_types.len != src_info.param_types.len) { + if (dest_info.args.len != src_info.args.len) { return InMemoryCoercionResult{ .fn_param_count = .{ - .actual = src_info.param_types.len, - .wanted = dest_info.param_types.len, + .actual = src_info.args.len, + .wanted = dest_info.args.len, } }; } - if (dest_info.noalias_bits != src_info.noalias_bits) { - return InMemoryCoercionResult{ .fn_param_noalias = .{ - .actual = src_info.noalias_bits, - .wanted = dest_info.noalias_bits, - } }; - } + // TODO - for (dest_info.param_types) |dest_param_ty, i| { - const src_param_ty = src_info.param_types[i]; + // if (dest_info.noalias_bits != src_info.noalias_bits) { + // return InMemoryCoercionResult{ .fn_param_noalias = .{ + // .actual = src_info.noalias_bits, + // .wanted = dest_info.noalias_bits, + // } }; + // } - if (dest_info.comptime_params[i] != src_info.comptime_params[i]) { - return InMemoryCoercionResult{ .fn_param_comptime = .{ - .index = i, - .wanted = dest_info.comptime_params[i], - } }; - } + // for (dest_info.param_types) |dest_param_ty, i| { + // const src_param_ty = src_info.param_types[i]; - // Note: Cast direction is reversed here. - const param = try ip.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target); - if (param != .ok) { - return InMemoryCoercionResult{ .fn_param = .{ - .child = try param.dupe(arena), - .actual = src_param_ty, - .wanted = dest_param_ty, - .index = i, - } }; - } - } + // if (dest_info.comptime_params[i] != src_info.comptime_params[i]) { + // return InMemoryCoercionResult{ .fn_param_comptime = .{ + // .index = i, + // .wanted = dest_info.comptime_params[i], + // } }; + // } + + // // Note: Cast direction is reversed here. + // const param = try ip.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target); + // if (param != .ok) { + // return InMemoryCoercionResult{ .fn_param = .{ + // .child = try param.dupe(arena), + // .actual = src_param_ty, + // .wanted = dest_param_ty, + // .index = i, + // } }; + // } + // } return .ok; } @@ -1943,8 +1998,8 @@ fn coerceInMemoryAllowedFns( /// a type has zero bits, which can cause a "foo depends on itself" compile error. /// This logic must be kept in sync with `Type.isPtrLikeOptional`. fn typePtrOrOptionalPtrTy( - ty: Index, ip: InternPool, + ty: Index, ) !Index { const key = ip.indexToKey(ty); switch (key) { @@ -1960,7 +2015,7 @@ fn typePtrOrOptionalPtrTy( if (child_key != .pointer_type) return Index.none; const child_ptr_key = child_key.pointer_type; - switch (child_ptr_key) { + switch (child_ptr_key.size) { .Slice, .C => return Index.none, .Many, .One => { if (child_ptr_key.is_allowzero) return Index.none; diff --git a/src/analysis.zig b/src/analysis.zig index 4de63b7..3774b03 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -769,15 +769,15 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl }; var interpreter = handle.interpreter.?; - var root_scope = ComptimeInterpreter.Scope{ + try interpreter.scopes.append(interpreter.allocator, .{ .interpreter = interpreter, .parent = 0, .node_idx = 0, - .namespace = ComptimeInterpreter.IPIndex.none, - }; + .namespace = .none, + }); // TODO: Start from current/nearest-current scope - const result = interpreter.interpret(node, root_scope, .{}) catch |err| { + const result = interpreter.interpret(node, 0, .{}) catch |err| { log.err("Interpreter error: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -794,7 +794,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.IPKey{ .simple = .type }); if (val.ty != type_type) { - log.err("Not a type: { }", .{val.ty.fmtType(&interpreter.ip)}); + log.err("Not a type: {}", .{val.ty.fmtType(&interpreter.ip)}); return null; } From 475da588958bede53cef884fc0fd73ce65956778 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Wed, 4 Jan 2023 06:07:39 +0100 Subject: [PATCH 04/78] finish and test peer type resolution among integers and floats --- src/InternPool.zig | 234 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 222 insertions(+), 12 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 9de7018..b5988a1 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -6,6 +6,7 @@ extra: std.ArrayListUnmanaged(u8) = .{}, const InternPool = @This(); const std = @import("std"); +const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -1163,14 +1164,14 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t switch (candidate_key) { .simple => |candidate_simple| switch (candidate_simple) { - // TODO usize, isize - // TODO c integer types .f16, .f32, .f64, .f80, .f128 => switch (chosen_key) { .simple => |chosen_simple| switch (chosen_simple) { .f16, .f32, .f64, .f80, .f128 => { - // NOTE we don't have to handle the equality case - - @panic("TODO: choose larger"); + if (chosen_key.floatBits(target) < candidate_key.floatBits(target)) { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } }, .comptime_int, .comptime_float => { chosen = candidate; @@ -1182,6 +1183,56 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => {}, }, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, => switch (chosen_key) { + .simple => |chosen_simple| switch (chosen_simple) { + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, => { + const chosen_bits = chosen_key.intInfo(target, ip).bits; + const candidate_bits = candidate_key.intInfo(target, ip).bits; + + if(chosen_bits < candidate_bits) { + chosen = candidate; + chosen_i = candidate_i + 1; + } + continue; + }, + .comptime_int => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + else => {}, + }, + .int_type => |chosen_info| { + if (chosen_info.bits < candidate_key.intInfo(target, ip).bits) { + chosen = candidate; + chosen_i = candidate_i + 1; + } + continue; + }, + .pointer_type => |chosen_info| if (chosen_info.size == .C) continue, + else => {}, + }, + .noreturn, .undefined_type => continue, .comptime_int => switch (chosen_key) { @@ -1191,10 +1242,19 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .f64, .f80, .f128, - => continue, - .usize, .isize => continue, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .comptime_float, => continue, .comptime_int => unreachable, - .comptime_float => continue, else => {}, }, .int_type => continue, @@ -1209,6 +1269,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t chosen_i = candidate_i + 1; continue; }, + .comptime_float => unreachable, else => {}, }, else => {}, @@ -1221,10 +1282,26 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t }, .int_type => |candidate_info| switch (chosen_key) { .simple => |chosen_simple| switch (chosen_simple) { - .usize, .isize => { - // TODO + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, => { + const chosen_bits = chosen_key.intInfo(target, ip).bits; + const candidate_bits = candidate_key.intInfo(target, ip).bits; + + if(chosen_bits < candidate_bits) { + chosen = candidate; + chosen_i = candidate_i + 1; + } + continue; }, - // TODO c integer types .comptime_int => { chosen = candidate; chosen_i = candidate_i + 1; @@ -1458,9 +1535,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t }, else => {}, } + + return Index.none; } - @panic("TODO"); + return chosen; } const InMemoryCoercionResult = union(enum) { @@ -2203,3 +2282,134 @@ test "array type" { try testExpectFmtType(&ip, i32_3_array_type_0, "[3]i32"); try testExpectFmtType(&ip, u32_0_0_array_type, "[3:0]u32"); } + +test "resolvePeerTypes" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const type_type = try ip.get(gpa, .{ .simple = .type }); + const noreturn_type = try ip.get(gpa, .{ .simple = .noreturn }); + const undefined_type = try ip.get(gpa, .{ .simple = .undefined_type }); + + try ip.testResolvePeerTypes(Index.none, Index.none, Index.none); + try ip.testResolvePeerTypes(bool_type, bool_type, bool_type); + try ip.testResolvePeerTypes(bool_type, noreturn_type, bool_type); + try ip.testResolvePeerTypes(bool_type, undefined_type, bool_type); + try ip.testResolvePeerTypes(type_type, noreturn_type, type_type); + try ip.testResolvePeerTypes(type_type, undefined_type, type_type); +} + +test "resolvePeerTypes integers and floats" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); + const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const i64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 64 } }); + const u16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 16 } }); + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } }); + + const usize_type = try ip.get(gpa, .{ .simple = .usize }); + const isize_type = try ip.get(gpa, .{ .simple = .isize }); + + const c_short_type = try ip.get(gpa, .{ .simple = .c_short }); + const c_int_type = try ip.get(gpa, .{ .simple = .c_int }); + const c_long_type = try ip.get(gpa, .{ .simple = .c_long }); + + const comptime_int_type = try ip.get(gpa, .{ .simple = .comptime_int }); + const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); + + const f16_type = try ip.get(gpa, .{ .simple = .f16 }); + const f32_type = try ip.get(gpa, .{ .simple = .f32 }); + const f64_type = try ip.get(gpa, .{ .simple = .f64 }); + + const bool_type = try ip.get(gpa, .{ .simple = .bool }); + + try ip.testResolvePeerTypes(i16_type, i16_type, i16_type); + try ip.testResolvePeerTypes(i16_type, i32_type, i32_type); + try ip.testResolvePeerTypes(i32_type, i64_type, i64_type); + + try ip.testResolvePeerTypes(u16_type, u16_type, u16_type); + try ip.testResolvePeerTypes(u16_type, u32_type, u32_type); + try ip.testResolvePeerTypes(u32_type, u64_type, u64_type); + + try ip.testResolvePeerTypesInOrder(i16_type, u16_type, i16_type); + try ip.testResolvePeerTypesInOrder(u16_type, i16_type, u16_type); + try ip.testResolvePeerTypesInOrder(i32_type, u32_type, i32_type); + try ip.testResolvePeerTypesInOrder(u32_type, i32_type, u32_type); + try ip.testResolvePeerTypesInOrder(isize_type, usize_type, isize_type); + try ip.testResolvePeerTypesInOrder(usize_type, isize_type, usize_type); + + try ip.testResolvePeerTypes(i16_type, u32_type, u32_type); + try ip.testResolvePeerTypes(u16_type, i32_type, i32_type); + try ip.testResolvePeerTypes(i32_type, u64_type, u64_type); + try ip.testResolvePeerTypes(u32_type, i64_type, i64_type); + + try ip.testResolvePeerTypes(i16_type, usize_type, usize_type); + try ip.testResolvePeerTypes(i16_type, isize_type, isize_type); + try ip.testResolvePeerTypes(u16_type, usize_type, usize_type); + try ip.testResolvePeerTypes(u16_type, isize_type, isize_type); + + try ip.testResolvePeerTypes(i16_type, c_long_type, c_long_type); + try ip.testResolvePeerTypes(i16_type, c_long_type, c_long_type); + try ip.testResolvePeerTypes(u16_type, c_long_type, c_long_type); + try ip.testResolvePeerTypes(u16_type, c_long_type, c_long_type); + + try ip.testResolvePeerTypes(comptime_int_type, i16_type, i16_type); + try ip.testResolvePeerTypes(comptime_int_type, u64_type, u64_type); + try ip.testResolvePeerTypes(comptime_int_type, isize_type, isize_type); + try ip.testResolvePeerTypes(comptime_int_type, usize_type, usize_type); + try ip.testResolvePeerTypes(comptime_int_type, c_short_type, c_short_type); + try ip.testResolvePeerTypes(comptime_int_type, c_int_type, c_int_type); + try ip.testResolvePeerTypes(comptime_int_type, c_long_type, c_long_type); + + try ip.testResolvePeerTypes(comptime_float_type, i16_type, Index.none); + try ip.testResolvePeerTypes(comptime_float_type, u64_type, Index.none); + try ip.testResolvePeerTypes(comptime_float_type, isize_type, Index.none); + try ip.testResolvePeerTypes(comptime_float_type, usize_type, Index.none); + try ip.testResolvePeerTypes(comptime_float_type, c_short_type, Index.none); + try ip.testResolvePeerTypes(comptime_float_type, c_int_type, Index.none); + try ip.testResolvePeerTypes(comptime_float_type, c_long_type, Index.none); + + try ip.testResolvePeerTypes(comptime_float_type, comptime_int_type, comptime_float_type); + + try ip.testResolvePeerTypes(comptime_int_type, f16_type, f16_type); + try ip.testResolvePeerTypes(comptime_int_type, f32_type, f32_type); + try ip.testResolvePeerTypes(comptime_int_type, f64_type, f64_type); + + try ip.testResolvePeerTypes(comptime_float_type, f16_type, f16_type); + try ip.testResolvePeerTypes(comptime_float_type, f32_type, f32_type); + try ip.testResolvePeerTypes(comptime_float_type, f64_type, f64_type); + + try ip.testResolvePeerTypes(f16_type, i16_type, Index.none); + try ip.testResolvePeerTypes(f32_type, u64_type, Index.none); + try ip.testResolvePeerTypes(f64_type, isize_type, Index.none); + try ip.testResolvePeerTypes(f16_type, usize_type, Index.none); + try ip.testResolvePeerTypes(f32_type, c_short_type, Index.none); + try ip.testResolvePeerTypes(f64_type, c_int_type, Index.none); + try ip.testResolvePeerTypes(f64_type, c_long_type, Index.none); + + try ip.testResolvePeerTypes(bool_type, i16_type, Index.none); + try ip.testResolvePeerTypes(bool_type, u64_type, Index.none); + try ip.testResolvePeerTypes(bool_type, usize_type, Index.none); + try ip.testResolvePeerTypes(bool_type, c_int_type, Index.none); + try ip.testResolvePeerTypes(bool_type, comptime_int_type, Index.none); + try ip.testResolvePeerTypes(bool_type, comptime_float_type, Index.none); + try ip.testResolvePeerTypes(bool_type, f32_type, Index.none); +} + +fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { + try ip.testResolvePeerTypesInOrder(a, b, expected); + try ip.testResolvePeerTypesInOrder(b, a, expected); +} + +fn testResolvePeerTypesInOrder(ip: *InternPool, lhs: Index, rhs: Index, expected: Index) !void { + const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{lhs, rhs}, builtin.target); + try std.testing.expectEqual(expected, actual); +} \ No newline at end of file From 5598ad032b08db69f8929a269be3f10e6fea02da Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:53:48 +0100 Subject: [PATCH 05/78] partially implement peer type resolution for pointer types --- src/InternPool.zig | 756 +++++++++++++++++++++++++++++++-------------- 1 file changed, 525 insertions(+), 231 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index b5988a1..7e2ee84 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -31,20 +31,20 @@ pub const Int = struct { pub const Pointer = struct { elem_type: Index, - sentinel: Index, - alignment: u16, + sentinel: Index = .none, + alignment: u16 = 0, size: std.builtin.Type.Pointer.Size, - is_const: bool, - is_volatile: bool, - is_allowzero: bool, - address_space: std.builtin.AddressSpace, + is_const: bool = false, + is_volatile: bool = false, + is_allowzero: bool = false, + address_space: std.builtin.AddressSpace = .generic, }; pub const Array = struct { // TODO support big int len: u32, child: Index, - sentinel: Index, + sentinel: Index = .none, }; pub const Struct = struct { @@ -181,6 +181,7 @@ pub const Key = union(enum) { union_type: Union, tuple_type: Tuple, vector_type: Vector, + // TODO anyframe_T declaration: Decl, namespace: Namespace, @@ -560,6 +561,61 @@ pub const Key = union(enum) { }; } + /// For pointer-like optionals, returns true, otherwise returns the allowzero property + /// of pointers. + pub fn ptrAllowsZero(ty: Key, ip: *const InternPool) bool { + if (ty.isPtrLikeOptional(ip)) { + return true; + } + return ty.pointer_type.is_allowzero; + } + + /// Returns true if the type is optional and would be lowered to a single pointer + /// address value, using 0 for null. Note that this returns true for C pointers. + pub fn isPtrLikeOptional(ty: Key, ip: *const InternPool) bool { + switch (ty) { + .optional_type => |optional_info| { + const child_ty = optional_info.payload_type; + const child_key = ip.indexToKey(child_ty); + if(child_key != .pointer_type) return false; + const info = child_key.pointer_type; + switch (info.size) { + .Slice, .C => return false, + .Many, .One => return !info.is_allowzero, + } + }, + .pointer_type => |pointer_info| return pointer_info.size == .C, + else => return false, + } + } + + pub fn elemType2(ty: Key) Index { + return switch (ty) { + .simple => |simple| switch(simple) { + .@"anyframe" => @panic("TODO: return void type"), + else => unreachable, + }, + .pointer_type => |pointer_info| pointer_info.elem_type, + .array_type => |array_info| array_info.child, + .optional_type => |optional_info| optional_info.payload_type, + .vector_type => |vector_info| vector_info.child, + else => unreachable, + }; + } + + /// Asserts the type is an array, pointer or vector. + pub fn sentinel(ty: Key) Index { + return switch (ty) { + .pointer_type => |pointer_info| pointer_info.sentinel, + .array_type => |array_info| array_info.sentinel, + .struct_type, + .tuple_type, + .vector_type, + => Index.none, + else => unreachable, + }; + } + pub fn getNamespace(ty: Key) ?Index { return switch (ty) { .struct_type => |struct_info| struct_info.namespace, @@ -1152,6 +1208,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t var arena = arena_allocator.allocator(); var chosen = types[0]; + // // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(). + // // * ErrorSet: this is an override + // // * ErrorUnion: this is an override of the error set only + // // * other: at the end we make an ErrorUnion with the other thing and this + // var err_set_ty: Index = Index.none; var any_are_null = false; var seen_const = false; var convert_to_slice = false; @@ -1162,6 +1223,17 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t const candidate_key: Key = ip.indexToKey(candidate); const chosen_key = ip.indexToKey(chosen); + // If the candidate can coerce into our chosen type, we're done. + // If the chosen type can coerce into the candidate, use that. + if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen, candidate, true, target)) == .ok) { + continue; + } + if ((try ip.coerceInMemoryAllowed(gpa, arena, candidate, chosen, true, target)) == .ok) { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + switch (candidate_key) { .simple => |candidate_simple| switch (candidate_simple) { .f16, .f32, .f64, .f80, .f128 => switch (chosen_key) { @@ -1170,8 +1242,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t if (chosen_key.floatBits(target) < candidate_key.floatBits(target)) { chosen = candidate; chosen_i = candidate_i + 1; - continue; } + continue; }, .comptime_int, .comptime_float => { chosen = candidate; @@ -1193,7 +1265,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .c_ulong, .c_longlong, .c_ulonglong, - .c_longdouble, => switch (chosen_key) { + .c_longdouble, + => switch (chosen_key) { .simple => |chosen_simple| switch (chosen_simple) { .usize, .isize, @@ -1205,11 +1278,12 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .c_ulong, .c_longlong, .c_ulonglong, - .c_longdouble, => { + .c_longdouble, + => { const chosen_bits = chosen_key.intInfo(target, ip).bits; const candidate_bits = candidate_key.intInfo(target, ip).bits; - if(chosen_bits < candidate_bits) { + if (chosen_bits < candidate_bits) { chosen = candidate; chosen_i = candidate_i + 1; } @@ -1253,7 +1327,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .c_longlong, .c_ulonglong, .c_longdouble, - .comptime_float, => continue, + .comptime_float, + => continue, .comptime_int => unreachable, else => {}, }, @@ -1292,11 +1367,12 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .c_ulong, .c_longlong, .c_ulonglong, - .c_longdouble, => { + .c_longdouble, + => { const chosen_bits = chosen_key.intInfo(target, ip).bits; const candidate_bits = candidate_key.intInfo(target, ip).bits; - if(chosen_bits < candidate_bits) { + if (chosen_bits < candidate_bits) { chosen = candidate; chosen_i = candidate_i + 1; } @@ -1368,13 +1444,13 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t const chosen_elem_ty = chosen_elem_info.array_type.child; const cand_elem_ty = candidate_elem_info.array_type.child; - const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, chosen_elem_ty, cand_elem_ty, !chosen_info.is_const, target); + const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, chosen_elem_ty, cand_elem_ty, chosen_info.is_const, target); if (chosen_ok) { convert_to_slice = true; continue; } - const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, cand_elem_ty, chosen_elem_ty, !candidate_info.is_const, target); + const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, cand_elem_ty, chosen_elem_ty, candidate_info.is_const, target); if (cand_ok) { convert_to_slice = true; chosen = candidate; @@ -1394,8 +1470,8 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t // the one we will keep. If they're both OK then we keep the // C pointer since it matches both single and many pointers. if (candidate_info.size == .C or chosen_info.size == .C) { - const cand_ok = .ok == try ip.coerceInMemoryAllowed(arena, candidate_info.elem_type, chosen_info.elem_type, !candidate_info.is_const, target); - const chosen_ok = .ok == try ip.coerceInMemoryAllowed(arena, chosen_info.elem_type, candidate_info.elem_type, !chosen_info.is_const, target); + const cand_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, candidate_info.elem_type, chosen_info.elem_type, candidate_info.is_const, target); + const chosen_ok = .ok == try ip.coerceInMemoryAllowed(gpa, arena, chosen_info.elem_type, candidate_info.elem_type, chosen_info.is_const, target); if (cand_ok) { if (!chosen_ok or chosen_info.size != .C) { @@ -1412,7 +1488,6 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t // "incompatible types" error below. } } - // TODO }, .int_type => { if (candidate_info.size == .C) { @@ -1457,7 +1532,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t if (candidate_info.is_const) { const candidate_elem_key = ip.indexToKey(candidate_info.elem_type); if (candidate_elem_key == .function_type and - .ok == try ip.coerceInMemoryAllowedFns(arena, chosen_info, candidate_elem_key.function_type, target)) + .ok == try ip.coerceInMemoryAllowedFns(gpa, arena, chosen_info, candidate_elem_key.function_type, target)) { chosen = candidate; chosen_i = candidate_i + 1; @@ -1472,18 +1547,13 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => {}, }, .optional_type => |candidate_info| { - const is_chosen_const_ptr = switch (chosen_key) { - .pointer_type => |chosen_info| chosen_info.is_const, - else => false, - }; - - if ((try ip.coerceInMemoryAllowed(arena, chosen, candidate_info.payload_type, false, target)) == .ok) { + if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen, candidate_info.payload_type, true, target)) == .ok) { seen_const = seen_const or ip.indexToKey(candidate_info.payload_type).isConstPtr(); any_are_null = true; continue; } - seen_const = seen_const or is_chosen_const_ptr; + seen_const = seen_const or chosen_key.isConstPtr(); any_are_null = false; chosen = candidate; chosen_i = candidate_i + 1; @@ -1518,10 +1588,10 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => {}, }, .optional_type => |chosen_info| { - if ((try ip.coerceInMemoryAllowed(arena, chosen_info.payload_type, candidate, false, target)) == .ok) { + if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen_info.payload_type, candidate, true, target)) == .ok) { continue; } - if ((try ip.coerceInMemoryAllowed(arena, candidate, chosen_info.payload_type, false, target)) == .ok) { + if ((try ip.coerceInMemoryAllowed(gpa, arena, candidate, chosen_info.payload_type, true, target)) == .ok) { any_are_null = true; chosen = candidate; chosen_i = candidate_i + 1; @@ -1529,7 +1599,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t } }, .error_union_type => |chosen_info| { - if ((try ip.coerceInMemoryAllowed(arena, chosen_info.payload_type, candidate, false, target)) == .ok) { + if ((try ip.coerceInMemoryAllowed(gpa, arena, chosen_info.payload_type, candidate, true, target)) == .ok) { continue; } }, @@ -1539,6 +1609,78 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t return Index.none; } + if(chosen == .none) return chosen; + const chosen_key = ip.indexToKey(chosen); + + if (convert_to_slice) { + // turn *[N]T => []T + const chosen_elem_key = ip.indexToKey(chosen_key.pointer_type.elem_type); + var info = chosen_key.pointer_type; + info.sentinel = chosen_elem_key.sentinel(); + info.size = .Slice; + info.is_const = seen_const or chosen_elem_key.isConstPtr(); + info.elem_type = chosen_elem_key.elemType2(); + + const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info }); + const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty; + return opt_ptr_ty; + // const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ptr_ty; + // return try ip.get(gpa, .{ .error_union_type = .{ + // .error_set_type = set_ty, + // .payload_type = opt_ptr_ty, + // } }); + } + + if (seen_const) { + // turn []T => []const T + switch (chosen_key) { + .error_union_type => |error_union_info| { + var info: Pointer = ip.indexToKey(error_union_info.payload_type).pointer_type; + info.is_const = true; + + const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info }); + const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty; + // const set_ty = if(err_set_ty != .none) err_set_ty else error_union_info.error_set_type; + const set_ty = error_union_info.error_set_type; + return try ip.get(gpa, .{ .error_union_type = .{ + .error_set_type = set_ty, + .payload_type = opt_ptr_ty, + } }); + }, + .pointer_type => |pointer_info| { + var info = pointer_info; + info.is_const = true; + + const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info }); + const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty; + return opt_ptr_ty; + // const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ptr_ty; + // return try ip.get(gpa, .{ .error_union_type = .{ + // .error_set_type = set_ty, + // .payload_type = opt_ptr_ty, + // } }); + }, + else => return chosen, + } + } + + if (any_are_null) { + const opt_ty = switch (chosen_key) { + .simple => |simple| switch (simple) { + .null_type => chosen, + else => try ip.get(gpa, .{ .optional_type = .{ .payload_type = chosen } }), + }, + .optional_type => chosen, + else => try ip.get(gpa, .{ .optional_type = .{ .payload_type = chosen } }), + }; + return opt_ty; + // const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ty; + // return try ip.get(gpa, .{ .error_union_type = .{ + // .error_set_type = set_ty, + // .payload_type = opt_ty, + // } }); + } + return chosen; } @@ -1653,22 +1795,21 @@ const InMemoryCoercionResult = union(enum) { } }; -/// If pointers have the same representation in runtime memory, a bitcast AIR instruction -/// may be used for the coercion. +/// If pointers have the same representation in runtime memory /// * `const` attribute can be gained /// * `volatile` attribute can be gained -/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if !dest_is_mut +/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if dest_is_const /// * alignment can be decreased /// * bit offset attributes must match exactly /// * `*`/`[*]` must match exactly, but `[*c]` matches either one /// * sentinel-terminated pointers can coerce into `[*]` fn coerceInMemoryAllowed( ip: *InternPool, - // gpa: Allocator, + gpa: Allocator, arena: Allocator, dest_ty: Index, src_ty: Index, - dest_is_mut: bool, + dest_is_const: bool, target: std.Target, ) error{OutOfMemory}!InMemoryCoercionResult { if (dest_ty == src_ty) return .ok; @@ -1679,168 +1820,157 @@ fn coerceInMemoryAllowed( const dest_tag = dest_key.zigTypeTag(); const src_tag = src_key.zigTypeTag(); - // integers with the same number of bits. - if (dest_tag == .Int and src_tag == .Int) { - const dest_info = dest_key.intInfo(target, ip); - const src_info = src_key.intInfo(target, ip); + if (dest_tag != src_tag) { + return InMemoryCoercionResult{ .no_match = .{ + .actual = dest_ty, + .wanted = src_ty, + } }; + } - if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) return .ok; + switch (dest_tag) { + .Int => { + const dest_info = dest_key.intInfo(target, ip); + const src_info = src_key.intInfo(target, ip); + + if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) return .ok; - if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or - // small enough unsigned ints can get casted to large enough signed ints - (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or - (dest_info.signedness == .unsigned and src_info.signedness == .signed)) - { return InMemoryCoercionResult{ .int_not_coercible = .{ .actual_signedness = src_info.signedness, .wanted_signedness = dest_info.signedness, .actual_bits = src_info.bits, .wanted_bits = dest_info.bits, } }; - } - } - - // floats with the same number of bits. - if (dest_tag == .Float and src_tag == .Float and - // this is an optimization because only a long double can have the same size as a other Float - // SAFETY: every Float is a Simple - dest_key.simple == .c_longdouble or src_key.simple == .c_longdouble) - { - const dest_bits = dest_key.floatBits(target); - const src_bits = src_key.floatBits(target); - if (dest_bits == src_bits) return .ok; - } - - // Pointers / Pointer-like Optionals - const maybe_dest_ptr_ty = try ip.typePtrOrOptionalPtrTy(dest_ty); - const maybe_src_ptr_ty = try ip.typePtrOrOptionalPtrTy(src_ty); - if (maybe_dest_ptr_ty != Index.none and maybe_src_ptr_ty != Index.none) { - @panic("TODO: implement coerceInMemoryAllowedPtrs"); - // return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, maybe_dest_ptr_ty, maybe_src_ptr_ty, dest_is_mut, target); - } - - // Slices - if (dest_key.isSlice() and src_key.isSlice()) { - @panic("TODO: implement coerceInMemoryAllowedPtrs"); - // return try ip.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target); - } - - // Functions - if (dest_tag == .Fn and src_tag == .Fn) { - return try ip.coerceInMemoryAllowedFns(arena, dest_key.function_type, src_key.function_type, target); - } - - // Error Unions - if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) { - const dest_payload = dest_key.error_union_type.payload_type; - const src_payload = src_key.error_union_type.payload_type; - const child = try ip.coerceInMemoryAllowed(arena, dest_payload, src_payload, dest_is_mut, target); - if (child != .ok) { - return InMemoryCoercionResult{ .error_union_payload = .{ - .child = try child.dupe(arena), - .actual = src_payload, - .wanted = dest_payload, + }, + .Float => { + const dest_bits = dest_key.floatBits(target); + const src_bits = src_key.floatBits(target); + if (dest_bits == src_bits) return .ok; + // TODO return float_not_coercible + return InMemoryCoercionResult{ .no_match = .{ + .actual = dest_ty, + .wanted = src_ty, } }; - } - const dest_set = dest_key.error_union_type.error_set_type; - const src_set = src_key.error_union_type.error_set_type; - return try ip.coerceInMemoryAllowed(arena, dest_set, src_set, dest_is_mut, target); + }, + .Pointer => { + return try ip.coerceInMemoryAllowedPtrs(gpa, arena, dest_ty, src_ty, dest_key, src_key, dest_is_const, target); + }, + .Optional => { + // Pointer-like Optionals + const maybe_dest_ptr_ty = try ip.optionalPtrTy(dest_key); + const maybe_src_ptr_ty = try ip.optionalPtrTy(src_key); + if (maybe_dest_ptr_ty != .none and maybe_src_ptr_ty != .none) { + const dest_ptr_info = ip.indexToKey(maybe_dest_ptr_ty); + const src_ptr_info = ip.indexToKey(maybe_src_ptr_ty); + return try ip.coerceInMemoryAllowedPtrs(gpa, arena, dest_ty, src_ty, dest_ptr_info, src_ptr_info, dest_is_const, target); + } + + if (maybe_dest_ptr_ty != maybe_src_ptr_ty) { + return InMemoryCoercionResult{ .optional_shape = .{ + .actual = src_ty, + .wanted = dest_ty, + } }; + } + + const dest_child_type = dest_key.optional_type.payload_type; + const src_child_type = src_key.optional_type.payload_type; + + const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_child_type, src_child_type, dest_is_const, target); + if (child != .ok) { + return InMemoryCoercionResult{ .optional_child = .{ + .child = try child.dupe(arena), + .actual = src_child_type, + .wanted = dest_child_type, + } }; + } + + return .ok; + }, + .Fn => { + return try ip.coerceInMemoryAllowedFns(gpa, arena, dest_key.function_type, src_key.function_type, target); + }, + .ErrorUnion => { + const dest_payload = dest_key.error_union_type.payload_type; + const src_payload = src_key.error_union_type.payload_type; + const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_payload, src_payload, dest_is_const, target); + if (child != .ok) { + return InMemoryCoercionResult{ .error_union_payload = .{ + .child = try child.dupe(arena), + .actual = src_payload, + .wanted = dest_payload, + } }; + } + const dest_set = dest_key.error_union_type.error_set_type; + const src_set = src_key.error_union_type.error_set_type; + return try ip.coerceInMemoryAllowed(gpa, arena, dest_set, src_set, dest_is_const, target); + }, + .ErrorSet => { + return .ok; + // TODO: implement coerceInMemoryAllowedErrorSets + // return try ip.coerceInMemoryAllowedErrorSets(dest_ty, src_ty); + }, + .Array => { + const dest_info = dest_key.array_type; + const src_info = src_key.array_type; + if (dest_info.len != src_info.len) { + return InMemoryCoercionResult{ .array_len = .{ + .actual = src_info.len, + .wanted = dest_info.len, + } }; + } + + const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.child, src_info.child, dest_is_const, target); + if (child != .ok) { + return InMemoryCoercionResult{ .array_elem = .{ + .child = try child.dupe(arena), + .actual = src_info.child, + .wanted = dest_info.child, + } }; + } + + const ok_sent = dest_info.sentinel == Index.none or + (src_info.sentinel != Index.none and + dest_info.sentinel == src_info.sentinel // is this enough for a value equality check? + ); + if (!ok_sent) { + return InMemoryCoercionResult{ .array_sentinel = .{ + .actual = src_info.sentinel, + .wanted = dest_info.sentinel, + .ty = dest_info.child, + } }; + } + return .ok; + }, + .Vector => { + const dest_len = dest_key.vector_type.len; + const src_len = src_key.vector_type.len; + + if (dest_len != src_len) { + return InMemoryCoercionResult{ .vector_len = .{ + .actual = src_len, + .wanted = dest_len, + } }; + } + + const dest_elem_ty = dest_key.vector_type.child; + const src_elem_ty = src_key.vector_type.child; + const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_elem_ty, src_elem_ty, dest_is_const, target); + if (child != .ok) { + return InMemoryCoercionResult{ .vector_elem = .{ + .child = try child.dupe(arena), + .actual = src_elem_ty, + .wanted = dest_elem_ty, + } }; + } + + return .ok; + }, + else => { + return InMemoryCoercionResult{ .no_match = .{ + .actual = dest_ty, + .wanted = src_ty, + } }; + }, } - - // Error Sets - if (dest_tag == .ErrorSet and src_tag == .ErrorSet) { - return .ok; // TODO: implement coerceInMemoryAllowedErrorSets - // return try ip.coerceInMemoryAllowedErrorSets(dest_ty, src_ty); - } - - // Arrays - if (dest_tag == .Array and src_tag == .Array) { - const dest_info = dest_key.array_type; - const src_info = src_key.array_type; - if (dest_info.len != src_info.len) { - return InMemoryCoercionResult{ .array_len = .{ - .actual = src_info.len, - .wanted = dest_info.len, - } }; - } - - const child = try ip.coerceInMemoryAllowed(arena, dest_info.child, src_info.child, dest_is_mut, target); - if (child != .ok) { - return InMemoryCoercionResult{ .array_elem = .{ - .child = try child.dupe(arena), - .actual = src_info.child, - .wanted = dest_info.child, - } }; - } - - const ok_sent = dest_info.sentinel == Index.none or - (src_info.sentinel != Index.none and - dest_info.sentinel == src_info.sentinel // is this enough for a value equality check? - ); - if (!ok_sent) { - return InMemoryCoercionResult{ .array_sentinel = .{ - .actual = src_info.sentinel, - .wanted = dest_info.sentinel, - .ty = dest_info.child, - } }; - } - return .ok; - } - - // Vectors - if (dest_tag == .Vector and src_tag == .Vector) { - const dest_len = dest_key.vector_type.len; - const src_len = src_key.vector_type.len; - - if (dest_len != src_len) { - return InMemoryCoercionResult{ .vector_len = .{ - .actual = src_len, - .wanted = dest_len, - } }; - } - - const dest_elem_ty = dest_key.vector_type.child; - const src_elem_ty = src_key.vector_type.child; - const child = try ip.coerceInMemoryAllowed(arena, dest_elem_ty, src_elem_ty, dest_is_mut, target); - if (child != .ok) { - return InMemoryCoercionResult{ .vector_elem = .{ - .child = try child.dupe(arena), - .actual = src_elem_ty, - .wanted = dest_elem_ty, - } }; - } - - return .ok; - } - - // Optionals - if (dest_tag == .Optional and src_tag == .Optional) { - if (maybe_dest_ptr_ty != maybe_src_ptr_ty) { - return InMemoryCoercionResult{ .optional_shape = .{ - .actual = src_ty, - .wanted = dest_ty, - } }; - } - - const dest_child_type = dest_key.optional_type.payload_type; - const src_child_type = src_key.optional_type.payload_type; - - const child = try ip.coerceInMemoryAllowed(arena, dest_child_type, src_child_type, dest_is_mut, target); - if (child != .ok) { - return InMemoryCoercionResult{ .optional_child = .{ - .child = try child.dupe(arena), - .actual = src_child_type, - .wanted = dest_child_type, - } }; - } - - return .ok; - } - - return InMemoryCoercionResult{ .no_match = .{ - .actual = dest_ty, - .wanted = src_ty, - } }; } // fn coerceInMemoryAllowedErrorSets( @@ -1996,6 +2126,7 @@ fn coerceInMemoryAllowed( fn coerceInMemoryAllowedFns( ip: *InternPool, + gpa: std.mem.Allocator, arena: std.mem.Allocator, dest_info: Fn, src_info: Fn, @@ -2020,7 +2151,7 @@ fn coerceInMemoryAllowedFns( const is_noreturn = return_type_key == .simple and return_type_key.simple == .noreturn; if (!is_noreturn) { - const rt = try ip.coerceInMemoryAllowed(arena, dest_info.return_type, src_info.return_type, false, target); + const rt = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.return_type, src_info.return_type, true, target); if (rt != .ok) { return InMemoryCoercionResult{ .fn_return_type = .{ .child = try rt.dupe(arena), @@ -2057,7 +2188,7 @@ fn coerceInMemoryAllowedFns( // } // // Note: Cast direction is reversed here. - // const param = try ip.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target); + // const param = try ip.coerceInMemoryAllowed(gpa, src_param_ty, dest_param_ty, true, target); // if (param != .ok) { // return InMemoryCoercionResult{ .fn_param = .{ // .child = try param.dupe(arena), @@ -2071,22 +2202,129 @@ fn coerceInMemoryAllowedFns( return .ok; } -/// For pointer-like optionals, it returns the pointer type. For pointers, -/// the type is returned unmodified. -/// This can return `error.AnalysisFail` because it sometimes requires resolving whether -/// a type has zero bits, which can cause a "foo depends on itself" compile error. -/// This logic must be kept in sync with `Type.isPtrLikeOptional`. -fn typePtrOrOptionalPtrTy( - ip: InternPool, - ty: Index, -) !Index { - const key = ip.indexToKey(ty); - switch (key) { - .pointer_type => |pointer_info| switch (pointer_info.size) { - .Slice => return Index.none, - else => return ty, - }, +fn coerceInMemoryAllowedPtrs( + ip: *InternPool, + gpa: std.mem.Allocator, + arena: std.mem.Allocator, + dest_ty: Index, + src_ty: Index, + dest_ptr_info: Key, + src_ptr_info: Key, + dest_is_const: bool, + target: std.Target, +) !InMemoryCoercionResult { + const dest_info = dest_ptr_info.pointer_type; + const src_info = src_ptr_info.pointer_type; + const ok_ptr_size = src_info.size == dest_info.size or + src_info.size == .C or dest_info.size == .C; + if (!ok_ptr_size) { + return InMemoryCoercionResult{ .ptr_size = .{ + .actual = src_info.size, + .wanted = dest_info.size, + } }; + } + + const ok_cv_qualifiers = + (!src_info.is_const or dest_info.is_const) and + (!src_info.is_volatile or dest_info.is_volatile); + + if (!ok_cv_qualifiers) { + return InMemoryCoercionResult{ .ptr_qualifiers = .{ + .actual_const = src_info.is_const, + .wanted_const = dest_info.is_const, + .actual_volatile = src_info.is_volatile, + .wanted_volatile = dest_info.is_volatile, + } }; + } + + if (dest_info.address_space != src_info.address_space) { + return InMemoryCoercionResult{ .ptr_addrspace = .{ + .actual = src_info.address_space, + .wanted = dest_info.address_space, + } }; + } + + const child = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.elem_type, src_info.elem_type, dest_info.is_const, target); + if (child != .ok) { + return InMemoryCoercionResult{ .ptr_child = .{ + .child = try child.dupe(arena), + .actual = src_info.elem_type, + .wanted = dest_info.elem_type, + } }; + } + + const dest_allow_zero = dest_ptr_info.ptrAllowsZero(ip); + const src_allow_zero = src_ptr_info.ptrAllowsZero(ip); + + const ok_allows_zero = (dest_allow_zero and (src_allow_zero or dest_is_const)) or (!dest_allow_zero and !src_allow_zero); + if (!ok_allows_zero) { + return InMemoryCoercionResult{ .ptr_allowzero = .{ + .actual = src_ty, + .wanted = dest_ty, + } }; + } + + // if (src_info.host_size != dest_info.host_size or + // src_info.bit_offset != dest_info.bit_offset) + // { + // return InMemoryCoercionResult{ .ptr_bit_range = .{ + // .actual_host = src_info.host_size, + // .wanted_host = dest_info.host_size, + // .actual_offset = src_info.bit_offset, + // .wanted_offset = dest_info.bit_offset, + // } }; + // } + + const ok_sent = dest_info.sentinel == .none or src_info.size == .C or dest_info.sentinel == src_info.sentinel; // is this enough for a value equality check? + if (!ok_sent) { + return InMemoryCoercionResult{ .ptr_sentinel = .{ + .actual = if(src_info.sentinel != .none) src_info.sentinel else try ip.get(gpa, .{.simple = .unreachable_value}), + .wanted = if(dest_info.sentinel != .none) dest_info.sentinel else try ip.get(gpa, .{.simple = .unreachable_value}), + .ty = dest_info.elem_type, + } }; + } + + // If both pointers have alignment 0, it means they both want ABI alignment. + // In this case, if they share the same child type, no need to resolve + // pointee type alignment. Otherwise both pointee types must have their alignment + // resolved and we compare the alignment numerically. + alignment: { + if (src_info.alignment == 0 and dest_info.alignment == 0 and + dest_info.elem_type == src_info.elem_type // is this enough for a value equality check? + ) + { + break :alignment; + } + + // const src_align = if (src_info.alignment != 0) + // src_info.alignment + // else + // src_info.elem_type.abiAlignment(target); + + // const dest_align = if (dest_info.alignment != 0) + // dest_info.alignment + // else + // dest_info.elem_type.abiAlignment(target); + + // if (dest_align > src_align) { + // return InMemoryCoercionResult{ .ptr_alignment = .{ + // .actual = src_align, + // .wanted = dest_align, + // } }; + // } + + break :alignment; + } + + return .ok; +} + +fn optionalPtrTy( + ip: InternPool, + ty: Key, +) !Index { + switch (ty) { .optional_type => |optional_info| { const child_type = optional_info.payload_type; const child_key = ip.indexToKey(child_type); @@ -2108,8 +2346,7 @@ fn typePtrOrOptionalPtrTy( }, } }, - - else => return Index.none, + else => unreachable, } } @@ -2196,32 +2433,34 @@ test "pointer type" { try std.testing.expect(i32_type_0 == i32_type_1); try std.testing.expect(i32_type_0 != u32_type); - var ptr: Pointer = .{ - .elem_type = undefined, - .sentinel = Index.none, - .alignment = 0, // TODO - .size = std.builtin.Type.Pointer.Size.One, - .is_const = false, - .is_volatile = false, - .is_allowzero = false, - .address_space = std.builtin.AddressSpace.generic, - }; - - ptr.elem_type = i32_type_0; - const i32_pointer_type_0 = try ip.get(gpa, .{ .pointer_type = ptr }); - ptr.elem_type = i32_type_0; - const i32_pointer_type_1 = try ip.get(gpa, .{ .pointer_type = ptr }); - ptr.elem_type = i32_type_1; - const i32_pointer_type_2 = try ip.get(gpa, .{ .pointer_type = ptr }); - ptr.elem_type = u32_type; - const u32_pointer_type = try ip.get(gpa, .{ .pointer_type = ptr }); + const i32_pointer_type_0 = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = i32_type_0, + .size = .One, + } }); + const i32_pointer_type_1 = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = i32_type_0, + .size = .One, + } }); + const i32_pointer_type_2 = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = i32_type_1, + .size = .One, + } }); + const u32_pointer_type = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .One, + } }); try std.testing.expect(i32_pointer_type_0 == i32_pointer_type_1); try std.testing.expect(i32_pointer_type_1 == i32_pointer_type_2); try std.testing.expect(i32_pointer_type_0 != u32_pointer_type); - ptr.is_const = true; - const const_u32_pointer_type = try ip.get(gpa, .{ .pointer_type = ptr }); + const const_u32_pointer_type = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .One, + .is_const = true, + } }); + + try std.testing.expect(const_u32_pointer_type != u32_pointer_type); try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); try testExpectFmtType(&ip, u32_pointer_type, "*u32"); @@ -2263,12 +2502,10 @@ test "array type" { const i32_3_array_type_0 = try ip.get(gpa, .{ .array_type = .{ .len = 3, .child = i32_type_0, - .sentinel = Index.none, } }); const i32_3_array_type_1 = try ip.get(gpa, .{ .array_type = .{ .len = 3, .child = i32_type_1, - .sentinel = Index.none, } }); const u32_0_0_array_type = try ip.get(gpa, .{ .array_type = .{ .len = 3, @@ -2379,6 +2616,9 @@ test "resolvePeerTypes integers and floats" { try ip.testResolvePeerTypes(comptime_float_type, comptime_int_type, comptime_float_type); + try ip.testResolvePeerTypes(f16_type, f32_type, f32_type); + try ip.testResolvePeerTypes(f32_type, f64_type, f64_type); + try ip.testResolvePeerTypes(comptime_int_type, f16_type, f16_type); try ip.testResolvePeerTypes(comptime_int_type, f32_type, f32_type); try ip.testResolvePeerTypes(comptime_int_type, f64_type, f64_type); @@ -2404,12 +2644,66 @@ test "resolvePeerTypes integers and floats" { try ip.testResolvePeerTypes(bool_type, f32_type, Index.none); } +test "resolvePeerTypes pointers" { + 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 comptime_int_type = try ip.get(gpa, .{ .simple = .comptime_int }); + const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); + const bool_type = try ip.get(gpa, .{ .simple = .bool }); + + const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C } }); + + const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ + .len = 1, + .child = u32_type, + .sentinel = Index.none, + } }); + const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{ + .len = 2, + .child = u32_type, + .sentinel = Index.none, + } }); + + const @"*[1]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[1]u32", .size = .One } }); + const @"*[2]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[2]u32", .size = .One } }); + + const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); + + const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .One, + .is_const = true, + } }); + + const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many } }); + + const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice } }); + + try ip.testResolvePeerTypes(@"[*c]u32", comptime_int_type, @"[*c]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", u32_type, @"[*c]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", comptime_float_type, Index.none); + try ip.testResolvePeerTypes(@"[*c]u32", bool_type, Index.none); + + try ip.testResolvePeerTypes(@"[*]u32", @"*[2]u32", @"[*]u32"); + try ip.testResolvePeerTypes(@"[]u32", @"*[2]u32", @"[]u32"); + + try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); + try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); + try ip.testResolvePeerTypes(@"*u32", @"*const u32", @"*const u32"); + + try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32"); +} + fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { try ip.testResolvePeerTypesInOrder(a, b, expected); try ip.testResolvePeerTypesInOrder(b, a, expected); } fn testResolvePeerTypesInOrder(ip: *InternPool, lhs: Index, rhs: Index, expected: Index) !void { - const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{lhs, rhs}, builtin.target); + const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{ lhs, rhs }, builtin.target); try std.testing.expectEqual(expected, actual); -} \ No newline at end of file +} From 651955399ecdec19c362821ceb51c25d603f3e34 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:10:46 +0100 Subject: [PATCH 06/78] remove namespace and decl from intern pool --- src/InternPool.zig | 85 ++++++---------------------------------------- 1 file changed, 10 insertions(+), 75 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 7e2ee84..c872517 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -49,8 +49,7 @@ pub const Array = struct { pub const Struct = struct { fields: std.StringArrayHashMapUnmanaged(Field), - /// always points to Namespace - namespace: Index, + namespace: NamespaceIndex, layout: std.builtin.Type.ContainerLayout, backing_int_ty: Index, @@ -93,8 +92,7 @@ pub const ErrorSet = struct { pub const Enum = struct { tag_type: Index, fields: std.StringArrayHashMapUnmanaged(Index), - /// this always points to Namespace - namespace: Index, + namespace: NamespaceIndex, tag_type_infered: bool, }; @@ -117,8 +115,7 @@ pub const Fn = struct { pub const Union = struct { tag_type: Index, fields: std.StringArrayHashMapUnmanaged(Field), - /// always points to Namespace - namespace: Index, + namespace: NamespaceIndex, layout: std.builtin.Type.ContainerLayout, pub const Field = struct { @@ -141,26 +138,6 @@ pub const Vector = struct { pub const BigInt = std.math.big.int.Const; -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 Namespace = struct { - /// always points to Namespace or Index.none - parent: Index, - /// Will be a struct, enum, union, or opaque. - // ty: Index, - /// always points to Decl - decls: []const Index, - usingnamespaces: []const Index, -}; - pub const Bytes = struct { data: []const u8, }; @@ -183,9 +160,6 @@ pub const Key = union(enum) { vector_type: Vector, // TODO anyframe_T - declaration: Decl, - namespace: Namespace, - int_u64_value: u64, int_i64_value: i64, int_big_value: BigInt, @@ -246,8 +220,6 @@ pub const Key = union(enum) { std.hash.autoHash(&hasher, union_info.layout); }, .tuple_type => |tuple_info| std.hash.autoHashStrat(&hasher, tuple_info, .Deep), - .declaration => |decl_info| std.hash.autoHashStrat(&hasher, decl_info, .Deep), - .namespace => |namespace_info| std.hash.autoHashStrat(&hasher, namespace_info, .Deep), .int_big_value => |big_int| { std.hash.autoHash(&hasher, big_int.positive); hasher.update(std.mem.sliceAsBytes(big_int.limbs)); @@ -324,30 +296,6 @@ pub const Key = union(enum) { } return true; }, - .declaration => |decl_info| { - if (!std.mem.eql(u8, decl_info.name, b.declaration.name)) return false; - if (decl_info.ty != b.declaration.ty) return false; - if (decl_info.val != b.declaration.val) return false; - if (decl_info.alignment != b.declaration.alignment) return false; - if (decl_info.address_space != b.declaration.address_space) return false; - if (decl_info.is_pub != b.declaration.is_pub) return false; - if (decl_info.is_exported != b.declaration.is_exported) return false; - return true; - }, - .namespace => |namespace_info| { - if (namespace_info.parent != b.namespace.parent) return false; - - if (namespace_info.decls.len != b.namespace.decls.len) return false; - if (namespace_info.usingnamespaces.len != b.namespace.usingnamespaces.len) return false; - - for (namespace_info.decls) |decl, i| { - if (decl != b.namespace.decls[i]) return false; - } - for (namespace_info.usingnamespaces) |namespace, i| { - if (namespace != b.namespace.usingnamespaces[i]) return false; - } - return false; - }, else => std.meta.eql(a, b), }; } @@ -382,8 +330,6 @@ pub const Key = union(enum) { .float_128_value => .float_f128, .type_value => .type, - .declaration => .declaration, - .namespace => .namespace, .bytes => .bytes, .one_pointer => .one_pointer, }; @@ -448,10 +394,6 @@ pub const Key = union(enum) { .tuple_type => .Struct, // TODO this correct? .vector_type => .Vector, - .declaration, - .namespace, - => unreachable, - .int_u64_value, .int_i64_value, .int_big_value, @@ -616,12 +558,12 @@ pub const Key = union(enum) { }; } - pub fn getNamespace(ty: Key) ?Index { + pub fn getNamespace(ty: Key) 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, - else => null, + else => .none, }; } @@ -794,8 +736,6 @@ pub const Key = union(enum) { }); }, - .declaration, .namespace => unreachable, - .int_u64_value, .int_i64_value, .int_big_value, @@ -888,9 +828,6 @@ pub const Key = union(enum) { .vector_type, => unreachable, - .declaration => unreachable, - .namespace => unreachable, - .int_u64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer), .int_i64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer), .int_big_value => |big_int| try big_int.format("", .{}, writer), @@ -939,6 +876,11 @@ pub const Index = enum(u32) { } }; +pub const NamespaceIndex = enum(u32) { + none = std.math.maxInt(u32), + _, +}; + pub const Tag = enum(u8) { /// A type or value that can be represented with only an enum tag. /// data is Simple enum value @@ -1024,13 +966,6 @@ pub const Tag = enum(u8) { /// data is Index. type, - /// A declaration. - /// data is payload to Decl. - declaration, - /// A namespace. - /// data is payload to Namespace. - namespace, - /// A byte sequence value. /// data is payload to data begin and length. bytes, From 8828ff117da98462c6f2807c5d3787c884472437 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:11:48 +0100 Subject: [PATCH 07/78] more comptime interpreter work --- src/ComptimeInterpreter.zig | 166 ++++++++++++++++++------------------ src/InternPool.zig | 25 +++--- 2 files changed, 97 insertions(+), 94 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index a9c9c2b..83f4264 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -9,6 +9,7 @@ const ast = @import("ast.zig"); const zig = std.zig; const Ast = zig.Ast; const analysis = @import("analysis.zig"); +const offsets = @import("offsets.zig"); const DocumentStore = @import("DocumentStore.zig"); pub const InternPool = @import("InternPool.zig"); @@ -26,6 +27,8 @@ ip: InternPool = .{}, document_store: *DocumentStore, uri: DocumentStore.Uri, scopes: std.MultiArrayList(Scope) = .{}, +decls: std.ArrayListUnmanaged(Decl) = .{}, +namespaces: std.MultiArrayList(Namespace) = .{}, /// Interpreter diagnostic errors errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{}, @@ -56,10 +59,15 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void { interpreter.ip.deinit(interpreter.allocator); var i: usize = 0; - while (i < interpreter.scopes.len) : (i += 1) { - interpreter.scopes.items(.child_scopes)[i].deinit(interpreter.allocator); + while (i < interpreter.namespaces.len) : (i += 1) { + interpreter.namespaces.items(.decls)[i].deinit(interpreter.allocator); + interpreter.namespaces.items(.usingnamespaces)[i].deinit(interpreter.allocator); } + interpreter.namespaces.deinit(interpreter.allocator); + interpreter.decls.deinit(interpreter.allocator); interpreter.scopes.deinit(interpreter.allocator); + + interpreter.arena.deinit(); } pub const Type = struct { @@ -77,6 +85,27 @@ pub const Value = struct { val: IPIndex, }; +pub const Decl = struct { + name: []const u8, + ty: IPIndex, + val: IPIndex, + alignment: u16, + address_space: std.builtin.AddressSpace, + is_pub: bool, + is_exported: bool, +}; + +pub const NamespaceIndex = InternPool.NamespaceIndex; + +pub const Namespace = struct { + /// always points to Namespace or Index.none + parent: NamespaceIndex, + /// Will be a struct, enum, union, or opaque. + // ty: Index, + decls: std.StringArrayHashMapUnmanaged(Decl) = .{}, + usingnamespaces: std.ArrayListUnmanaged(NamespaceIndex) = .{}, +}; + // pub const Comptimeness = enum { @"comptime", runtime }; pub const Scope = struct { @@ -87,8 +116,7 @@ pub const Scope = struct { parent: u32, // zero indicates root scope node_idx: Ast.Node.Index, - namespace: IPIndex, - child_scopes: std.ArrayListUnmanaged(u32) = .{}, + namespace: NamespaceIndex, pub const ScopeKind = enum { container, block, function }; pub fn scopeKind(scope: Scope) ScopeKind { @@ -177,22 +205,19 @@ fn getDeclCount(tree: Ast, node_idx: Ast.Node.Index) usize { pub fn huntItDown( interpreter: *ComptimeInterpreter, - namespace: IPIndex, + namespace: NamespaceIndex, decl_name: []const u8, options: InterpretOptions, -) InterpretError!InternPool.Decl { +) InterpretError!Decl { _ = options; var current_namespace = namespace; - while (current_namespace != IPIndex.none) { - const namespace_info = interpreter.ip.indexToKey(current_namespace).namespace; - defer current_namespace = namespace_info.parent; + while (current_namespace != .none) { + const decls: std.StringArrayHashMapUnmanaged(Decl) = interpreter.namespaces.items(.decls)[@enumToInt(current_namespace)]; + defer current_namespace = interpreter.namespaces.items(.parent)[@enumToInt(current_namespace)]; - for (namespace_info.decls) |decl_index| { - const decl_info = interpreter.ip.indexToKey(decl_index).declaration; - if (std.mem.eql(u8, decl_info.name, decl_name)) { - return decl_info; - } + if (decls.get(decl_name)) |decl| { + return decl; } } @@ -260,45 +285,37 @@ pub fn interpret( .interpreter = interpreter, .parent = scope, .node_idx = node_idx, - .namespace = IPIndex.none, // declarations have not been resolved yet + .namespace = .none, // declarations have not been resolved yet }); const container_scope = @intCast(u32, interpreter.scopes.len - 1); var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){}; - defer fields.deinit(interpreter.allocator); var buffer: [2]Ast.Node.Index = undefined; const members = ast.declMembers(tree, node_idx, &buffer); for (members) |member| { - const maybe_container_field: ?zig.Ast.full.ContainerField = switch (tags[member]) { - .container_field => tree.containerField(member), - .container_field_align => tree.containerFieldAlign(member), - .container_field_init => tree.containerFieldInit(member), - else => null, - }; - - const field_info = maybe_container_field orelse { + const container_field = ast.containerField(tree, member) orelse { _ = try interpreter.interpret(member, container_scope, options); continue; }; - var init_type_value = try (try interpreter.interpret(field_info.ast.type_expr, container_scope, .{})).getValue(); + var init_type_value = try (try interpreter.interpret(container_field.ast.type_expr, container_scope, .{})).getValue(); - var default_value = if (field_info.ast.value_expr == 0) + var default_value = if (container_field.ast.value_expr == 0) IPIndex.none else - (try (try interpreter.interpret(field_info.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty + (try (try interpreter.interpret(container_field.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty if (init_type_value.ty != type_type) { try interpreter.recordError( - field_info.ast.type_expr, + container_field.ast.type_expr, "expected_type", try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(&interpreter.ip)}), ); continue; } - const name = tree.tokenSlice(field_info.ast.main_token); + const name = tree.tokenSlice(container_field.ast.main_token); const field: InternPool.Struct.Field = .{ .ty = init_type_value.val, @@ -310,21 +327,12 @@ pub fn interpret( try fields.put(interpreter.arena.allocator(), name, field); } - const namespace = try interpreter.ip.get(interpreter.allocator, IPKey{ - .namespace = .{ - .parent = IPIndex.none, - // .ty = struct_type, - .decls = undefined, // TODO, - .usingnamespaces = &.{}, - }, - }); - const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ .fields = fields, - .namespace = namespace, // TODO - .layout = std.builtin.Type.ContainerLayout.Auto, // TODO - .backing_int_ty = IPIndex.none, // TODO + .namespace = .none, // TODO + .layout = .Auto, // TODO + .backing_int_ty = .none, // TODO }, }); @@ -372,7 +380,7 @@ pub fn interpret( .interpreter = interpreter, .parent = scope, .node_idx = node_idx, - .namespace = IPIndex.none, + .namespace = .none, }); const block_scope = @intCast(u32, interpreter.scopes.len - 1); @@ -493,7 +501,7 @@ pub fn interpret( var ir = try interpreter.interpret(data[node_idx].lhs, scope, options); var irv = try ir.getValue(); - const namespace = interpreter.ip.indexToKey(irv.val).getNamespace() orelse return error.IdentifierNotFound; + const namespace = interpreter.ip.indexToKey(irv.val).getNamespace(); var scope_sub_decl = irv.interpreter.huntItDown(namespace, rhs_str, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( @@ -688,7 +696,7 @@ pub fn interpret( .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ .fields = .{}, - .namespace = IPIndex.none, + .namespace = .none, .layout = .Auto, .backing_int_ty = IPIndex.none, } }), @@ -735,15 +743,13 @@ 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 namespace_index = interpreter.ip.indexToKey(value.val).getNamespace() orelse return error.InvalidBuiltin; - const namespace = interpreter.ip.indexToKey(namespace_index).namespace; + const namespace = interpreter.ip.indexToKey(value.val).getNamespace(); + if (namespace == .none) return error.InvalidBuiltin; const name = interpreter.ip.indexToKey(field_name.val).bytes.data; // TODO add checks - const has_decl = for (namespace.decls) |decl| { - const decl_name = interpreter.ip.indexToKey(decl).declaration.name; - if (std.mem.eql(u8, decl_name, name)) break true; - } else false; + const decls = interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; + const has_decl = decls.contains(name); return InterpretResult{ .value = Value{ .interpreter = interpreter, @@ -812,24 +818,22 @@ pub fn interpret( .@"comptime" => { return try interpreter.interpret(data[node_idx].lhs, scope, .{}); }, - // .fn_proto, - // .fn_proto_multi, - // .fn_proto_one, - // .fn_proto_simple, - .fn_decl => { - // var buf: [1]Ast.Node.Index = undefined; - // const func = ast.fnProto(tree, node_idx, &buf).?; + .fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_simple, .fn_decl => { + var buf: [1]Ast.Node.Index = undefined; + const func = ast.fnProto(tree, node_idx, &buf).?; // TODO: Resolve function type - // const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ - // .calling_convention = .Unspecified, - // .alignment = 0, - // .is_generic = false, - // .is_var_args = false, - // .return_type = IPIndex.none, - // .args = &.{}, - // } }); + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + + const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ + .calling_convention = .Unspecified, + .alignment = 0, + .is_generic = false, + .is_var_args = false, + .return_type = IPIndex.none, + .args = &.{}, + } }); // var it = func.iterate(&tree); // while (ast.nextFnParam(&it)) |param| { @@ -855,21 +859,21 @@ pub fn interpret( // if ((try interpreter.interpret(func.ast.return_type, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value| // fnd.return_type = value.value_data.@"type"; - // var value = Value{ - // .interpreter = interpreter, - // .node_idx = node_idx, - // .ty = function_type, - // .val = IPIndex.none, // TODO - // }; + const name = offsets.tokenToSlice(tree, func.name_token.?); - // const name = analysis.getDeclName(tree, node_idx).?; - // var namespace = interpreter.ip.indexToKey(scope.?.namespace).namespace; - // try namespace.decls.put(interpreter.allocator, name, .{ - // .scope = scope.?, - // .node_idx = node_idx, - // .name = name, - // .value = value, - // }); + const namespace = interpreter.scopes.items(.namespace)[scope]; + if (namespace != .none) { + const decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; + try decls.put(interpreter.allocator, name, .{ + .name = name, + .ty = type_type, + .val = function_type, + .alignment = 0, // TODO + .address_space = .generic, // TODO + .is_pub = false, // TODO + .is_exported = false, // TODO + }); + } return InterpretResult{ .nothing = {} }; }, @@ -889,7 +893,7 @@ pub fn interpret( defer args.deinit(interpreter.allocator); for (call_full.ast.params) |param| { - try args.append(interpreter.allocator, try (try interpreter.interpret(param, scope, .{})).getValue()); + args.appendAssumeCapacity(try (try interpreter.interpret(param, scope, .{})).getValue()); } const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, scope, .{}); @@ -1008,7 +1012,7 @@ pub fn call( .interpreter = interpreter, .parent = scope, .node_idx = func_node_idx, - .namespace = IPIndex.none, + .namespace = .none, }); const fn_scope = @intCast(u32, interpreter.scopes.len - 1); diff --git a/src/InternPool.zig b/src/InternPool.zig index c872517..ad16f92 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -519,8 +519,8 @@ pub const Key = union(enum) { .optional_type => |optional_info| { const child_ty = optional_info.payload_type; const child_key = ip.indexToKey(child_ty); - if(child_key != .pointer_type) return false; - const info = child_key.pointer_type; + if (child_key != .pointer_type) return false; + const info = child_key.pointer_type; switch (info.size) { .Slice, .C => return false, .Many, .One => return !info.is_allowzero, @@ -533,7 +533,7 @@ pub const Key = union(enum) { pub fn elemType2(ty: Key) Index { return switch (ty) { - .simple => |simple| switch(simple) { + .simple => |simple| switch (simple) { .@"anyframe" => @panic("TODO: return void type"), else => unreachable, }, @@ -1544,7 +1544,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t return Index.none; } - if(chosen == .none) return chosen; + if (chosen == .none) return chosen; const chosen_key = ip.indexToKey(chosen); if (convert_to_slice) { @@ -2163,7 +2163,7 @@ fn coerceInMemoryAllowedPtrs( const ok_cv_qualifiers = (!src_info.is_const or dest_info.is_const) and (!src_info.is_volatile or dest_info.is_volatile); - + if (!ok_cv_qualifiers) { return InMemoryCoercionResult{ .ptr_qualifiers = .{ .actual_const = src_info.is_const, @@ -2188,7 +2188,7 @@ fn coerceInMemoryAllowedPtrs( .wanted = dest_info.elem_type, } }; } - + const dest_allow_zero = dest_ptr_info.ptrAllowsZero(ip); const src_allow_zero = src_ptr_info.ptrAllowsZero(ip); @@ -2199,7 +2199,7 @@ fn coerceInMemoryAllowedPtrs( .wanted = dest_ty, } }; } - + // if (src_info.host_size != dest_info.host_size or // src_info.bit_offset != dest_info.bit_offset) // { @@ -2214,8 +2214,8 @@ fn coerceInMemoryAllowedPtrs( const ok_sent = dest_info.sentinel == .none or src_info.size == .C or dest_info.sentinel == src_info.sentinel; // is this enough for a value equality check? if (!ok_sent) { return InMemoryCoercionResult{ .ptr_sentinel = .{ - .actual = if(src_info.sentinel != .none) src_info.sentinel else try ip.get(gpa, .{.simple = .unreachable_value}), - .wanted = if(dest_info.sentinel != .none) dest_info.sentinel else try ip.get(gpa, .{.simple = .unreachable_value}), + .actual = if (src_info.sentinel != .none) src_info.sentinel else try ip.get(gpa, .{ .simple = .unreachable_value }), + .wanted = if (dest_info.sentinel != .none) dest_info.sentinel else try ip.get(gpa, .{ .simple = .unreachable_value }), .ty = dest_info.elem_type, } }; } @@ -2227,11 +2227,10 @@ fn coerceInMemoryAllowedPtrs( alignment: { if (src_info.alignment == 0 and dest_info.alignment == 0 and dest_info.elem_type == src_info.elem_type // is this enough for a value equality check? - ) - { + ) { break :alignment; } - + // const src_align = if (src_info.alignment != 0) // src_info.alignment // else @@ -2394,7 +2393,7 @@ test "pointer type" { .size = .One, .is_const = true, } }); - + try std.testing.expect(const_u32_pointer_type != u32_pointer_type); try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); From 4b4aafb8ee845b80f451c2e62e85103d9dae4e99 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:12:29 +0100 Subject: [PATCH 08/78] =?UTF-8?q?first=20successful=20test=20case=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comptime_interpreter.zig | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index cc42b3d..f2a979e 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -25,40 +25,38 @@ test "ComptimeInterpreter - basic test" { var interpreter = ComptimeInterpreter{ .allocator = allocator, + .arena = std.heap.ArenaAllocator.init(allocator), .document_store = &doc_store, .uri = "file:///file.zig", }; defer interpreter.deinit(); - _ = try interpreter.interpret(0, null, .{}); + _ = try interpreter.interpret(0, 0, .{}); + + var bool_type = try interpreter.ip.get(allocator, .{ .simple = .bool }); + var bool_true = try interpreter.ip.get(allocator, .{ .simple = .bool_true }); + var bool_false = try interpreter.ip.get(allocator, .{ .simple = .bool_false }); - var bool_type = try interpreter.createType(std.math.maxInt(std.zig.Ast.Node.Index), .{ .bool = {} }); var arg_false = ComptimeInterpreter.Value{ .interpreter = &interpreter, - .node_idx = std.math.maxInt(std.zig.Ast.Node.Index), - .type = bool_type, - .value_data = try interpreter.createValueData(.{ .bool = false }), + .node_idx = std.math.maxInt(Ast.Node.Index), + .ty = bool_type, + .val = bool_false, }; var arg_true = ComptimeInterpreter.Value{ .interpreter = &interpreter, - .node_idx = std.math.maxInt(std.zig.Ast.Node.Index), - .type = bool_type, - .value_data = try interpreter.createValueData(.{ .bool = true }), + .node_idx = std.math.maxInt(Ast.Node.Index), + .ty = bool_type, + .val = bool_true, }; - const rmt = interpreter.root_type.?.getTypeInfo().@"struct".scope.declarations.get("ReturnMyType").?; + const function_node: Ast.Node.Index = 4; - const call_with_false = try interpreter.call(null, rmt.node_idx, &.{ - arg_false, - }, .{}); - defer call_with_false.scope.deinit(); - const call_with_true = try interpreter.call(null, rmt.node_idx, &.{ - arg_true, - }, .{}); - defer call_with_true.scope.deinit(); + const call_with_false = try interpreter.call(0, function_node, &.{arg_false}, .{}); + const call_with_true = try interpreter.call(0, function_node, &.{arg_true}, .{}); - try std.testing.expectFmt("u69", "{any}", .{interpreter.formatTypeInfo(call_with_false.result.value.value_data.type.getTypeInfo())}); - try std.testing.expectFmt("u8", "{any}", .{interpreter.formatTypeInfo(call_with_true.result.value.value_data.type.getTypeInfo())}); + try std.testing.expectFmt("u69", "{any}", .{call_with_false.result.value.val.fmtValue(call_with_false.result.value.ty, &interpreter.ip)}); + try std.testing.expectFmt("u8", "{any}", .{call_with_true.result.value.val.fmtValue(call_with_true.result.value.ty, &interpreter.ip)}); } test "ComptimeInterpreter - struct" { @@ -80,17 +78,21 @@ test "ComptimeInterpreter - struct" { var interpreter = ComptimeInterpreter{ .allocator = allocator, + .arena = std.heap.ArenaAllocator.init(allocator), .document_store = &doc_store, .uri = "file:///file.zig", }; defer interpreter.deinit(); - _ = try interpreter.interpret(0, null, .{}); + _ = try interpreter.interpret(0, 0, .{}); - const rmt = interpreter.root_type.?.getTypeInfo().@"struct".scope.declarations.get("ReturnMyType").?; + const function_node: Ast.Node.Index = 3; - const z = try interpreter.call(null, rmt.node_idx, &.{}, .{}); - defer z.scope.deinit(); + const call_result = try interpreter.call(0, function_node, &.{}, .{}); - try std.testing.expectFmt("struct {slay: bool, var abc: comptime_int = 123, }", "{any}", .{interpreter.formatTypeInfo(z.result.value.value_data.type.getTypeInfo())}); + const result_struct = interpreter.ip.indexToKey(call_result.result.value.val).struct_type; + + try std.testing.expectEqual(@intCast(usize, 1), result_struct.fields.count()); + try std.testing.expect(result_struct.fields.contains("slay")); + try std.testing.expectFmt("bool", "{}", .{result_struct.fields.get("slay").?.ty.fmtType(&interpreter.ip)}); } From f0c888188aa1ad9f465b48a2c71db95d07eb260a Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 6 Jan 2023 14:12:29 +0100 Subject: [PATCH 09/78] refactor and basic struct/union value implementation --- src/ComptimeInterpreter.zig | 21 ++- src/InternPool.zig | 299 ++++++++++++++++++++---------------- src/Server.zig | 7 +- 3 files changed, 179 insertions(+), 148 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 83f4264..b36b583 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -289,7 +289,7 @@ pub fn interpret( }); const container_scope = @intCast(u32, interpreter.scopes.len - 1); - var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){}; + var fields = std.ArrayListUnmanaged(InternPool.Struct.Field){}; var buffer: [2]Ast.Node.Index = undefined; const members = ast.declMembers(tree, node_idx, &buffer); @@ -315,21 +315,20 @@ pub fn interpret( continue; } - const name = tree.tokenSlice(container_field.ast.main_token); - const field: InternPool.Struct.Field = .{ + .name = tree.tokenSlice(container_field.ast.main_token), .ty = init_type_value.val, .default_value = default_value, .alignent = 0, // TODO, .is_comptime = false, // TODO }; - try fields.put(interpreter.arena.allocator(), name, field); + try fields.append(interpreter.arena.allocator(), field); } const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ - .fields = fields, + .fields = fields.items, .namespace = .none, // TODO .layout = .Auto, // TODO .backing_int_ty = .none, // TODO @@ -695,7 +694,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ - .fields = .{}, + .fields = &.{}, .namespace = .none, .layout = .Auto, .backing_int_ty = IPIndex.none, @@ -715,7 +714,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = undefined }), // TODO + .val = undefined, // TODO }, }; } @@ -728,7 +727,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .type_value = value.ty }), + .val = value.ty, } }; } @@ -953,7 +952,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = pointer_type, - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .one_pointer = value.val }), + .val = value.val, } }; }, .deref => { @@ -967,13 +966,11 @@ pub fn interpret( return error.InvalidOperation; } - // TODO: Check if this is a one_ptr or not - return InterpretResult{ .value = .{ .interpreter = interpreter, .node_idx = node_idx, .ty = type_key.pointer_type.elem_type, - .val = interpreter.ip.indexToKey(value.val).one_pointer, + .val = value.val, } }; }, else => { diff --git a/src/InternPool.zig b/src/InternPool.zig index ad16f92..ce308dc 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -48,16 +48,17 @@ pub const Array = struct { }; pub const Struct = struct { - fields: std.StringArrayHashMapUnmanaged(Field), + fields: []const Field, namespace: NamespaceIndex, - layout: std.builtin.Type.ContainerLayout, + layout: std.builtin.Type.ContainerLayout = .Auto, backing_int_ty: Index, pub const Field = struct { + name: []const u8, ty: Index, - default_value: Index, - alignent: u16, - is_comptime: bool, + default_value: Index = .none, + alignment: u16 = 0, + is_comptime: bool = false, }; }; @@ -76,31 +77,30 @@ pub const ErrorUnion = struct { pub const ErrorSet = struct { /// must be sorted - names: std.StringArrayHashMapUnmanaged(void), + names: [][]const u8, pub fn sort(self: *ErrorSet) void { - const Context = struct { - keys: [][]const u8, - pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { - return std.mem.lessThan(u8, ctx.keys[a_index], ctx.keys[b_index]); - } - }; - self.names.sort(Context{ .keys = self.names.keys() }); + std.sort.sort([][]const u8, self.names, u8, std.mem.lessThan); } }; pub const Enum = struct { tag_type: Index, - fields: std.StringArrayHashMapUnmanaged(Index), + fields: []const Field, namespace: NamespaceIndex, tag_type_infered: bool, + + pub const Field = struct { + name: []const u8, + ty: Index, + }; }; pub const Fn = struct { - calling_convention: std.builtin.CallingConvention, - alignment: u16, - is_generic: bool, - is_var_args: bool, + calling_convention: std.builtin.CallingConvention = .Unspecified, + alignment: u16 = 0, + is_generic: bool = false, + is_var_args: bool = false, return_type: Index, args: []const Param, @@ -114,20 +114,21 @@ pub const Fn = struct { pub const Union = struct { tag_type: Index, - fields: std.StringArrayHashMapUnmanaged(Field), + fields: []const Field, namespace: NamespaceIndex, - layout: std.builtin.Type.ContainerLayout, + layout: std.builtin.Type.ContainerLayout = .Auto, pub const Field = struct { + name: []const u8, ty: Index, alignment: u16, }; }; pub const Tuple = struct { - types: []Index, + types: []const Index, /// unreachable_value elements are used to indicate runtime-known. - values: []Index, + values: []const Index, }; pub const Vector = struct { @@ -142,6 +143,15 @@ pub const Bytes = struct { data: []const u8, }; +pub const Aggregate = struct { + data: []const Index, +}; + +pub const UnionValue = struct { + tag: Index, + val: Index, +}; + pub const Key = union(enum) { simple: Simple, @@ -168,10 +178,12 @@ pub const Key = union(enum) { float_64_value: f64, float_80_value: f80, float_128_value: f128, - type_value: Index, + // type_value: Index, bytes: Bytes, - one_pointer: Index, + // one_pointer: Index, + aggregate: Aggregate, + union_value: UnionValue, // slice // error @@ -184,54 +196,12 @@ pub const Key = union(enum) { var hasher = std.hash.Wyhash.init(0); std.hash.autoHash(&hasher, std.meta.activeTag(key)); switch (key) { - .struct_type => |struct_info| { - var field_it = struct_info.fields.iterator(); - while (field_it.next()) |item| { - hasher.update(item.key_ptr.*); - std.hash.autoHash(&hasher, item.value_ptr.*); - } - std.hash.autoHash(&hasher, struct_info.layout); - }, - // .error_type => |error_info| hasher.update(error_info.name), - .error_set_type => |error_set_info| { - const names = error_set_info.names.keys(); - std.debug.assert(std.sort.isSorted([]const u8, names, u8, std.mem.lessThan)); - for (names) |error_name| { - hasher.update(error_name); - } - }, - .enum_type => |enum_info| { - std.hash.autoHash(&hasher, enum_info.tag_type); - var field_it = enum_info.fields.iterator(); - while (field_it.next()) |item| { - hasher.update(item.key_ptr.*); - std.hash.autoHash(&hasher, item.value_ptr.*); - } - std.hash.autoHash(&hasher, enum_info.tag_type_infered); - }, - .function_type => |function_info| std.hash.autoHashStrat(&hasher, function_info, .Deep), - .union_type => |union_info| { - std.hash.autoHash(&hasher, union_info.tag_type); - var field_it = union_info.fields.iterator(); - while (field_it.next()) |item| { - hasher.update(item.key_ptr.*); - std.hash.autoHash(&hasher, item.value_ptr.*); - } - std.hash.autoHash(&hasher, union_info.layout); - }, - .tuple_type => |tuple_info| std.hash.autoHashStrat(&hasher, tuple_info, .Deep), - .int_big_value => |big_int| { - std.hash.autoHash(&hasher, big_int.positive); - hasher.update(std.mem.sliceAsBytes(big_int.limbs)); - }, .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)), - - .bytes => |bytes| hasher.update(bytes.data), - inline else => |info| std.hash.autoHash(&hasher, info), // TODO sad stage1 noises :( + inline else => |info| std.hash.autoHashStrat(&hasher, info, .Deep), // TODO sad stage1 noises :( } return @truncate(u32, hasher.final()); } @@ -244,17 +214,17 @@ pub const Key = union(enum) { return switch (a) { .struct_type => |struct_info| { if (struct_info.layout != b.struct_type.layout) return false; - if (struct_info.fields.count() != b.struct_type.fields.count()) return false; - @panic("TODO: implement field equality check"); + 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| { - const a_names = error_set_info.names.keys(); - const b_names = b.error_set_type.names.keys(); - - if (a_names.len != b_names.len) return false; - for (a_names) |a_name, i| { - const b_name = b_names[i]; + 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; @@ -262,7 +232,7 @@ pub const Key = union(enum) { .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.count() != b.enum_type.fields.count()) return false; + if (enum_info.fields.len != b.enum_type.fields.len) return false; @panic("TODO: implement field equality check"); }, .function_type => |function_info| { @@ -281,8 +251,11 @@ pub const Key = union(enum) { .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.count() != b.union_type.fields.count()) return false; - @panic("TODO: implement union equality"); + 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); @@ -296,6 +269,14 @@ pub const Key = union(enum) { } 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), }; } @@ -328,10 +309,12 @@ pub const Key = union(enum) { .float_64_value => .float_f64, .float_80_value => .float_f80, .float_128_value => .float_f128, - .type_value => .type, + // .type_value => .type, .bytes => .bytes, - .one_pointer => .one_pointer, + // .one_pointer => .one_pointer, + .aggregate => .aggregate, + .union_value => .union_value, }; } @@ -402,11 +385,13 @@ pub const Key = union(enum) { .float_64_value, .float_80_value, .float_128_value, - .type_value, + // .type_value, => unreachable, .bytes, - .one_pointer, + // .one_pointer, + .aggregate, + .union_value, => unreachable, }; } @@ -455,7 +440,6 @@ pub const Key = union(enum) { /// Asserts the type is a fixed-size float or comptime_float. /// Returns 128 for comptime_float types. pub fn floatBits(ty: Key, target: std.Target) u16 { - std.debug.assert(ty == .simple); _ = target; return switch (ty.simple) { .f16 => 16, @@ -464,31 +448,12 @@ pub const Key = union(enum) { .f80 => 80, .f128, .comptime_float => 128, // TODO correctly resolve size based on `target` - .c_longdouble => 80, + .c_longdouble => @bitSizeOf(c_longdouble), else => unreachable, }; } - pub fn isCType(ty: Key) bool { - return switch (ty) { - .simple => |simple| switch (simple) { - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - => true, - else => false, - }, - else => false, - }; - } - pub fn isConstPtr(ty: Key) bool { return switch (ty) { .pointer_type => |pointer_info| pointer_info.is_const, @@ -496,20 +461,11 @@ pub const Key = union(enum) { }; } - pub fn isSlice(ty: Key) bool { - return switch (ty) { - .pointer_type => |pointer_info| pointer_info.size == .Slice, - else => false, - }; - } - /// For pointer-like optionals, returns true, otherwise returns the allowzero property /// of pointers. pub fn ptrAllowsZero(ty: Key, ip: *const InternPool) bool { - if (ty.isPtrLikeOptional(ip)) { - return true; - } - return ty.pointer_type.is_allowzero; + if (ty.pointer_type.is_allowzero) return true; + return ty.isPtrLikeOptional(ip); } /// Returns true if the type is optional and would be lowered to a single pointer @@ -728,7 +684,21 @@ pub const Key = union(enum) { try printType(function_info.return_type, ip, writer); }, .union_type => @panic("TODO"), - .tuple_type => @panic("TODO"), + .tuple_type => |tuple_info| { + try writer.writeAll("tuple{"); + for (tuple_info.types) |field_ty, i| { + if (i != 0) try writer.writeAll(", "); + const val = tuple_info.values[i]; + if (val != Index.none) { + try writer.writeAll("comptime "); + } + try printType(field_ty, ip, writer); + if (val != Index.none) { + try writer.print(" = {}", .{val.fmtValue(field_ty, ip)}); + } + } + try writer.writeByte('}'); + }, .vector_type => |vector_info| { try writer.print("@Vector({d},{})", .{ vector_info.len, @@ -746,9 +716,11 @@ pub const Key = union(enum) { .float_128_value, => unreachable, - .type_value, + // .type_value, .bytes, - .one_pointer, + // .one_pointer, + .aggregate, + .union_value, => unreachable, } } @@ -770,7 +742,6 @@ pub const Key = union(enum) { ip: *const InternPool, writer: anytype, ) @TypeOf(writer).Error!void { - _ = ty; const value_key: Key = ip.indexToKey(value); switch (value_key) { .simple => |simple| switch (simple) { @@ -837,10 +808,32 @@ pub const Key = union(enum) { .float_80_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), .float_128_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), - .type_value, - .bytes, - .one_pointer, - => unreachable, + // .type_value => |tty| tty.fmtType(ip), + .bytes => |data| try writer.print("\"{}\"", .{std.zig.fmtEscapes(data.data)}), + // .one_pointer => unreachable, + .aggregate => |aggregate| { + const struct_info = ip.indexToKey(ty).struct_type; + std.debug.assert(aggregate.data.len == struct_info.fields.len); + + try writer.writeAll(".{"); + var i: u32 = 0; + while (i < aggregate.data.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 writer.writeByte('}'); + }, + .union_value => |union_value| { + const union_info = ip.indexToKey(ty).union_type; + + try writer.writeAll(".{ "); + try printValue(union_info.tag_type, union_value.tag, ip, writer); + try writer.writeAll(" = "); + try printValue(union_value.val, @panic("TODO"), ip, writer); + try writer.writeAll(" }"); + }, } } }; @@ -962,16 +955,22 @@ 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 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 single pointer value. + // /// data is index to value. + // one_pointer, + /// A aggregate (struct) value. + /// data is index to Aggregate. + aggregate, + /// A union value. + /// data is index to UnionValue. + union_value, }; pub const Simple = enum(u32) { @@ -1060,10 +1059,10 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .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) }, + // .type => .{ .type_value = @intToEnum(Index, data) }, .bytes => unreachable, // TODO - .one_pointer => .{ .one_pointer = @intToEnum(Index, data) }, + // .one_pointer => .{ .one_pointer = @intToEnum(Index, data) }, else => @panic("TODO"), }; } @@ -1099,9 +1098,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { }, .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) }, + // .type_value => |ty| .{ .tag = .type, .data = @enumToInt(ty) }, .bytes => unreachable, // TODO - .one_pointer => |val| .{ .tag = .one_pointer, .data = @enumToInt(val) }, + // .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 :( }; try ip.items.append(gpa, item); @@ -2454,6 +2453,42 @@ 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 field1 = Struct.Field{ .name = "foo", .ty = u64_type }; + const field2 = Struct.Field{ .name = "bar", .ty = i32_type }; + const field3 = Struct.Field{ .name = "baz", .ty = bool_type }; + + const struct_type_0 = try ip.get(gpa, Key{ + .struct_type = Struct{ + .fields = &.{ field1, field2, field3 }, + .namespace = .none, + .layout = .Auto, + .backing_int_ty = .none, + }, + }); + + _ = try ip.get(gpa, .{ .simple = .unreachable_value }); + + const struct_type_1 = try ip.get(gpa, Key{ + .struct_type = Struct{ + .fields = &.{ field1, field2, field3 }, + .namespace = .none, + .layout = .Auto, + .backing_int_ty = .none, + }, + }); + std.debug.assert(struct_type_0 == struct_type_1); +} + test "resolvePeerTypes" { const gpa = std.testing.allocator; diff --git a/src/Server.zig b/src/Server.zig index dcb8b9f..4c2fc63 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -586,12 +586,11 @@ fn typeToCompletion( switch (key) { .struct_type => |struct_info| { - var field_it = struct_info.fields.iterator(); - while (field_it.next()) |entry| { + for (struct_info.fields) |field| { try list.append(allocator, .{ - .label = entry.key_ptr.*, + .label = field.name, .kind = .Field, - .insertText = entry.key_ptr.*, + .insertText = field.name, .insertTextFormat = .PlainText, }); } From 650eaeb66cb8fa028a6df22228ac475e6337bb3b Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 6 Jan 2023 14:38:28 +0100 Subject: [PATCH 10/78] implement anyframe->T --- src/ComptimeInterpreter.zig | 2 +- src/InternPool.zig | 58 ++++++++++++++----- .../comptime_interpreter.zig | 6 +- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index b36b583..9208121 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -319,7 +319,7 @@ pub fn interpret( .name = tree.tokenSlice(container_field.ast.main_token), .ty = init_type_value.val, .default_value = default_value, - .alignent = 0, // TODO, + .alignment = 0, // TODO, .is_comptime = false, // TODO }; diff --git a/src/InternPool.zig b/src/InternPool.zig index ce308dc..66ee74a 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -137,6 +137,10 @@ pub const Vector = struct { child: Index, }; +pub const AnyFrame = struct { + child: Index, +}; + pub const BigInt = std.math.big.int.Const; pub const Bytes = struct { @@ -168,7 +172,7 @@ pub const Key = union(enum) { union_type: Union, tuple_type: Tuple, vector_type: Vector, - // TODO anyframe_T + anyframe_t_type: AnyFrame, int_u64_value: u64, int_i64_value: i64, @@ -188,9 +192,6 @@ pub const Key = union(enum) { // slice // error // error union - // optional - // aggregate - // union pub fn hash(key: Key) u32 { var hasher = std.hash.Wyhash.init(0); @@ -300,6 +301,7 @@ pub const Key = union(enum) { .union_type => .type_union, .tuple_type => .type_tuple, .vector_type => .type_vector, + .anyframe_t_type => .type_anyframe_t, .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, @@ -376,6 +378,7 @@ pub const Key = union(enum) { .union_type => .Union, .tuple_type => .Struct, // TODO this correct? .vector_type => .Vector, + .anyframe_t_type => .AnyFrame, .int_u64_value, .int_i64_value, @@ -497,6 +500,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, else => unreachable, }; } @@ -705,6 +709,10 @@ pub const Key = union(enum) { vector_info.child.fmtType(ip), }); }, + .anyframe_t_type => |anyframe_info| { + try writer.writeAll("anyframe->"); + try printType(anyframe_info.child, ip, writer); + }, .int_u64_value, .int_i64_value, @@ -797,6 +805,7 @@ pub const Key = union(enum) { .union_type, .tuple_type, .vector_type, + .anyframe_t_type, => unreachable, .int_u64_value => |int| try std.fmt.formatIntValue(int, "", .{}, writer), @@ -921,6 +930,9 @@ pub const Tag = enum(u8) { /// An vector type. /// data is payload to Vector. type_vector, + /// An anyframe->T type. + /// data is index to type + type_anyframe_t, /// An unsigned integer value that can be represented by u32. /// data is integer value @@ -1036,9 +1048,8 @@ 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_optional => .{ .optional_type = .{ .payload_type = @intToEnum(Index, data) } }, + .type_anyframe_t => .{ .anyframe_t_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) }, @@ -1082,6 +1093,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .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), @@ -2288,17 +2300,11 @@ fn optionalPtrTy( // --------------------------------------------- fn testExpectFmtType(ip: *const InternPool, index: Index, expected: []const u8) !void { - const gpa = std.testing.allocator; - const actual = try std.fmt.allocPrint(gpa, "{}", .{index.fmtType(ip)}); - defer gpa.free(actual); - try std.testing.expectEqualStrings(expected, actual); + try std.testing.expectFmt(expected, "{}", .{index.fmtType(ip)}); } fn testExpectFmtValue(ip: *const InternPool, val: Index, ty: Index, expected: []const u8) !void { - const gpa = std.testing.allocator; - const actual = try std.fmt.allocPrint(gpa, "{}", .{val.fmtValue(ty, ip)}); - defer gpa.free(actual); - try std.testing.expectEqualStrings(expected, actual); + try std.testing.expectFmt(expected, "{}", .{val.fmtValue(ty, ip)}); } test "int type" { @@ -2410,6 +2416,9 @@ test "optional type" { const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + const null_value = try ip.get(gpa, .{ .simple = .null_value }); + const u64_42_value = try ip.get(gpa, .{ .int_u64_value = 42 }); + const i32_optional_type_0 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_0 } }); const i32_optional_type_1 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_1 } }); const u32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = u32_type } }); @@ -2419,6 +2428,9 @@ test "optional type" { try testExpectFmtType(&ip, i32_optional_type_0, "?i32"); try testExpectFmtType(&ip, u32_optional_type, "?u32"); + + try testExpectFmtValue(&ip, null_value, u32_optional_type, "null"); + try testExpectFmtValue(&ip, u64_42_value, u32_optional_type, "42"); } test "array type" { @@ -2489,6 +2501,22 @@ test "struct type" { std.debug.assert(struct_type_0 == struct_type_1); } +test "anyframe 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 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 } }); + + try testExpectFmtType(&ip, @"anyframe->i32", "anyframe->i32"); + try testExpectFmtType(&ip, @"anyframe->bool", "anyframe->bool"); +} + test "resolvePeerTypes" { const gpa = std.testing.allocator; diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index f2a979e..61f8176 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -92,7 +92,7 @@ test "ComptimeInterpreter - struct" { const result_struct = interpreter.ip.indexToKey(call_result.result.value.val).struct_type; - try std.testing.expectEqual(@intCast(usize, 1), result_struct.fields.count()); - try std.testing.expect(result_struct.fields.contains("slay")); - try std.testing.expectFmt("bool", "{}", .{result_struct.fields.get("slay").?.ty.fmtType(&interpreter.ip)}); + try std.testing.expectEqual(@intCast(usize, 1), result_struct.fields.len); + try std.testing.expectEqualStrings("slay", result_struct.fields[0].name); + try std.testing.expectFmt("bool", "{}", .{result_struct.fields[0].ty.fmtType(&interpreter.ip)}); } From 909424cada4e425a5df589ff40707f41b6ebb415 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 6 Jan 2023 15:01:48 +0100 Subject: [PATCH 11/78] error set type formatting --- src/InternPool.zig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 66ee74a..47623b3 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -656,7 +656,15 @@ pub const Key = union(enum) { }); }, // .error_type => @panic("TODO"), - .error_set_type => @panic("TODO"), + .error_set_type => |error_set_info| { + const names = error_set_info.names; + try writer.writeAll("error{"); + for (names) |name, i| { + if (i != 0) try writer.writeByte(','); + try writer.writeAll(name); + } + try writer.writeByte('}'); + }, .enum_type => @panic("TODO"), .function_type => |function_info| { try writer.writeAll("fn("); From 27d91d100f88ece8f8bac009122c1ef3854a400c Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 6 Jan 2023 15:02:45 +0100 Subject: [PATCH 12/78] more tests --- src/InternPool.zig | 97 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 47623b3..33dcd8a 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -77,7 +77,7 @@ pub const ErrorUnion = struct { pub const ErrorSet = struct { /// must be sorted - names: [][]const u8, + names: []const []const u8, pub fn sort(self: *ErrorSet) void { std.sort.sort([][]const u8, self.names, u8, std.mem.lessThan); @@ -105,9 +105,9 @@ pub const Fn = struct { args: []const Param, pub const Param = struct { - is_comptime: bool, - is_generic: bool, - is_noalias: bool, + is_comptime: bool = false, + is_generic: bool = false, + is_noalias: bool = false, arg_type: Index, }; }; @@ -2401,17 +2401,32 @@ test "pointer type" { try std.testing.expect(i32_pointer_type_1 == i32_pointer_type_2); try std.testing.expect(i32_pointer_type_0 != u32_pointer_type); - const const_u32_pointer_type = try ip.get(gpa, .{ .pointer_type = .{ + const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true, } }); + const @"*align(4) u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .One, + .alignment = 4, + } }); + const @"[*c]const volatile u32" = try ip.get(gpa, .{ .pointer_type = Pointer{ + .elem_type = u32_type, + .size = .C, + .is_const = true, + .is_volatile = true, + } }); - try std.testing.expect(const_u32_pointer_type != u32_pointer_type); + try std.testing.expect(@"*const u32" != u32_pointer_type); + try std.testing.expect(u32_pointer_type != @"*align(4) u32"); + try std.testing.expect(@"*align(4) u32" != @"[*c]const volatile u32"); try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); try testExpectFmtType(&ip, u32_pointer_type, "*u32"); - try testExpectFmtType(&ip, const_u32_pointer_type, "*const u32"); + try testExpectFmtType(&ip, @"*const u32", "*const u32"); + try testExpectFmtType(&ip, @"*align(4) u32", "*align(4) u32"); + try testExpectFmtType(&ip, @"[*c]const volatile u32", "[*c]const volatile u32"); } test "optional type" { @@ -2441,6 +2456,47 @@ test "optional type" { try testExpectFmtValue(&ip, u64_42_value, u32_optional_type, "42"); } +test "error set type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); + + const error_set_0 = try ip.get(gpa, .{ .error_set_type = .{ + .names = &.{ "foo", "bar", "baz" }, + } }); + + // const foo: [3]u8 = "foo".*; + + // const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{ + // .names = &.{&foo, "bar", "baz"}, + // } }); + + std.debug.assert(empty_error_set != error_set_0); + // std.debug.assert(error_set_0 == error_set_1); + + try testExpectFmtType(&ip, empty_error_set, "error{}"); + try testExpectFmtType(&ip, error_set_0, "error{foo,bar,baz}"); +} + +test "error union type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); + const bool_type = try ip.get(gpa, .{ .simple = .bool }); + + const @"error{}!bool" = try ip.get(gpa, .{ .error_union_type = ErrorUnion{ + .error_set_type = empty_error_set, + .payload_type = bool_type, + } }); + + try testExpectFmtType(&ip, @"error{}!bool", "error{}!bool"); +} + test "array type" { const gpa = std.testing.allocator; @@ -2509,6 +2565,33 @@ test "struct type" { std.debug.assert(struct_type_0 == struct_type_1); } +test "function 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 bool_type = try ip.get(gpa, .{ .simple = .bool }); + const type_type = try ip.get(gpa, .{ .simple = .type }); + + var param1 = Fn.Param{ .arg_type = i32_type }; + const @"fn(i32) bool" = try ip.get(gpa, .{ .function_type = .{ + .return_type = bool_type, + .args = &.{param1}, + } }); + + var param2 = Fn.Param{ .is_comptime = true, .arg_type = type_type }; + var param3 = Fn.Param{ .arg_type = i32_type }; + const @"fn(comptime type, i32) type" = try ip.get(gpa, .{ .function_type = .{ + .return_type = type_type, + .args = &.{ param2, param3 }, + } }); + + try testExpectFmtType(&ip, @"fn(i32) bool", "fn(i32) bool"); + try testExpectFmtType(&ip, @"fn(comptime type, i32) type", "fn(comptime type, i32) type"); +} + test "anyframe type" { const gpa = std.testing.allocator; From 98899ed0cffddbb4e82b5f5d3c74854a47a03cab Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 14:07:52 +0100 Subject: [PATCH 13/78] some ComptieInterpreter work --- src/ComptimeInterpreter.zig | 331 ++++++++++++++++-------------------- src/DocumentStore.zig | 2 +- src/analysis.zig | 9 +- 3 files changed, 151 insertions(+), 191 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 9208121..b2a0107 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -26,7 +26,6 @@ arena: std.heap.ArenaAllocator, ip: InternPool = .{}, document_store: *DocumentStore, uri: DocumentStore.Uri, -scopes: std.MultiArrayList(Scope) = .{}, decls: std.ArrayListUnmanaged(Decl) = .{}, namespaces: std.MultiArrayList(Namespace) = .{}, @@ -65,7 +64,6 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void { } interpreter.namespaces.deinit(interpreter.allocator); interpreter.decls.deinit(interpreter.allocator); - interpreter.scopes.deinit(interpreter.allocator); interpreter.arena.deinit(); } @@ -95,66 +93,40 @@ pub const Decl = struct { 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, - /// Will be a struct, enum, union, or opaque. - // ty: Index, + node_idx: Ast.Node.Index, + /// Will be a struct, enum, union, opaque or .none + ty: IPIndex, decls: std.StringArrayHashMapUnmanaged(Decl) = .{}, usingnamespaces: std.ArrayListUnmanaged(NamespaceIndex) = .{}, -}; - -// pub const Comptimeness = enum { @"comptime", runtime }; - -pub const Scope = struct { - interpreter: *ComptimeInterpreter, // TODO: Actually use this value // comptimeness: Comptimeness, - parent: u32, // zero indicates root scope - node_idx: Ast.Node.Index, - namespace: NamespaceIndex, - - pub const ScopeKind = enum { container, block, function }; - pub fn scopeKind(scope: Scope) ScopeKind { - const tree = scope.interpreter.getHandle().tree; - return switch (tree.nodes.items(.tag)[scope.node_idx]) { - .container_decl, - .container_decl_trailing, - .container_decl_arg, - .container_decl_arg_trailing, - .container_decl_two, - .container_decl_two_trailing, - .tagged_union, - .tagged_union_trailing, - .tagged_union_two, - .tagged_union_two_trailing, - .tagged_union_enum_tag, - .tagged_union_enum_tag_trailing, - .root, - .error_set_decl, - => .container, - else => .block, - }; - } - - pub fn getLabel(scope: Scope) ?Ast.TokenIndex { - const tree = scope.interpreter.getHandle().tree; + pub fn getLabel(self: Namespace, tree: Ast) ?Ast.TokenIndex { const token_tags = tree.tokens.items(.tag); - return switch (scope.scopeKind()) { - .block => z: { - const lbrace = tree.nodes.items(.main_token)[scope.node_idx]; - break :z if (token_tags[lbrace - 1] == .colon and token_tags[lbrace - 2] == .identifier) - lbrace - 2 - else - null; + switch (tree.nodes.items(.tag)[self.node_idx]) { + .block_two, + .block_two_semicolon, + .block, + .block_semicolon, + => { + const lbrace = tree.nodes.items(.main_token)[self.node_idx]; + if (token_tags[lbrace - 1] == .colon and token_tags[lbrace - 2] == .identifier) { + return lbrace - 2; + } + + return null; }, - else => null, - }; + else => return null, + } } }; @@ -183,26 +155,6 @@ pub const InterpretResult = union(enum) { } }; -fn getDeclCount(tree: Ast, node_idx: Ast.Node.Index) usize { - var buffer: [2]Ast.Node.Index = undefined; - const members = ast.declMembers(tree, node_idx, &buffer); - - var count: usize = 0; - - for (members) |member| { - switch (tree.nodes.items(.tag)[member]) { - .global_var_decl, - .local_var_decl, - .aligned_var_decl, - .simple_var_decl, - => count += 1, - else => {}, - } - } - - return count; -} - pub fn huntItDown( interpreter: *ComptimeInterpreter, namespace: NamespaceIndex, @@ -255,7 +207,7 @@ pub const InterpretError = std.mem.Allocator.Error || std.fmt.ParseIntError || s pub fn interpret( interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - scope: u32, + namespace: NamespaceIndex, options: InterpretOptions, ) InterpretError!InterpretResult { const tree = interpreter.getHandle().tree; @@ -281,13 +233,12 @@ pub fn interpret( => { const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - try interpreter.scopes.append(interpreter.allocator, .{ - .interpreter = interpreter, - .parent = scope, + try interpreter.namespaces.append(interpreter.allocator, .{ + .parent = namespace, .node_idx = node_idx, - .namespace = .none, // declarations have not been resolved yet + .ty = undefined, }); - const container_scope = @intCast(u32, interpreter.scopes.len - 1); + const container_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); var fields = std.ArrayListUnmanaged(InternPool.Struct.Field){}; @@ -295,16 +246,16 @@ pub fn interpret( const members = ast.declMembers(tree, node_idx, &buffer); for (members) |member| { const container_field = ast.containerField(tree, member) orelse { - _ = try interpreter.interpret(member, container_scope, options); + _ = try interpreter.interpret(member, container_namespace, options); continue; }; - var init_type_value = try (try interpreter.interpret(container_field.ast.type_expr, container_scope, .{})).getValue(); + var init_type_value = try (try interpreter.interpret(container_field.ast.type_expr, container_namespace, .{})).getValue(); var default_value = if (container_field.ast.value_expr == 0) IPIndex.none else - (try (try interpreter.interpret(container_field.ast.value_expr, container_scope, .{})).getValue()).val; // TODO check ty + (try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty if (init_type_value.ty != type_type) { try interpreter.recordError( @@ -329,11 +280,12 @@ pub fn interpret( const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ .fields = fields.items, - .namespace = .none, // TODO + .namespace = namespace, .layout = .Auto, // TODO .backing_int_ty = .none, // TODO }, }); + interpreter.namespaces.items(.ty)[@enumToInt(container_namespace)] = struct_type; return InterpretResult{ .value = Value{ .interpreter = interpreter, @@ -347,26 +299,34 @@ pub fn interpret( .aligned_var_decl, .simple_var_decl, => { - // TODO: Add 0 check - // const name = analysis.getDeclName(tree, node_idx).?; - // if (scope.?.declarations.contains(name)) - // return InterpretResult{ .nothing = {} }; + var decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; - // const decl = ast.varDecl(tree, node_idx).?; - // if (decl.ast.init_node == 0) - // return InterpretResult{ .nothing = {} }; + const name = analysis.getDeclName(tree, node_idx).?; + if (decls.contains(name)) + return InterpretResult{ .nothing = {} }; - // try scope.?.declarations.put(interpreter.allocator, name, .{ - // .scope = scope.?, - // .node_idx = node_idx, - // .name = name, - // }); + const decl = ast.varDecl(tree, node_idx).?; + + if (decl.ast.init_node == 0) + return InterpretResult{ .nothing = {} }; + + const init_type_value = try ((try interpreter.interpret(decl.ast.init_node, namespace, .{})).getValue()); + + try decls.putNoClobber(interpreter.allocator, name, .{ + .name = name, + .ty = init_type_value.ty, + .val = init_type_value.val, + .alignment = 0, // TODO + .address_space = .generic, // TODO + .is_pub = true, // TODO + .is_exported = false, // TODO + }); // TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...) // if (scope.?.scopeKind() != .container) { // if (scope.?.node_idx != 0) - // _ = try scope.?.declarations.getPtr(name).?.getValue(); + // _ = try decls.getPtr(name).?.getValue(); return InterpretResult{ .nothing = {} }; }, @@ -375,22 +335,21 @@ pub fn interpret( .block_two, .block_two_semicolon, => { - try interpreter.scopes.append(interpreter.allocator, .{ - .interpreter = interpreter, - .parent = scope, + try interpreter.namespaces.append(interpreter.allocator, .{ + .parent = namespace, .node_idx = node_idx, - .namespace = .none, + .ty = .none, }); - const block_scope = @intCast(u32, interpreter.scopes.len - 1); + const block_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); var buffer: [2]Ast.Node.Index = undefined; const statements = ast.blockStatements(tree, node_idx, &buffer).?; for (statements) |idx| { - const ret = try interpreter.interpret(idx, block_scope, options); + const ret = try interpreter.interpret(idx, block_namespace, options); switch (ret) { .@"break" => |lllll| { - const maybe_block_label_string = if (interpreter.scopes.get(scope).getLabel()) |i| tree.tokenSlice(i) else null; + const maybe_block_label_string = if (interpreter.namespaces.get(@enumToInt(namespace)).getLabel(tree)) |i| tree.tokenSlice(i) else null; if (lllll) |l| { if (maybe_block_label_string) |ls| { if (std.mem.eql(u8, l, ls)) { @@ -402,7 +361,7 @@ pub fn interpret( } }, .break_with_value => |bwv| { - const maybe_block_label_string = if (interpreter.scopes.get(scope).getLabel()) |i| tree.tokenSlice(i) else null; + const maybe_block_label_string = if (interpreter.namespaces.get(@enumToInt(namespace)).getLabel(tree)) |i| tree.tokenSlice(i) else null; if (bwv.label) |l| { if (maybe_block_label_string) |ls| { @@ -422,39 +381,39 @@ pub fn interpret( return InterpretResult{ .nothing = {} }; }, .identifier => { - var value = tree.getNodeSource(node_idx); + const value = offsets.nodeToSlice(tree, node_idx); - if (std.mem.eql(u8, "bool", value)) return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - } }; - if (std.mem.eql(u8, "true", value)) return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }), - } }; - if (std.mem.eql(u8, "false", value)) return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }), - } }; - - if (value.len == 5 and (value[0] == 'u' or value[0] == 'i') and std.mem.eql(u8, "size", value[1..])) return InterpretResult{ - .value = Value{ + if (std.mem.eql(u8, "bool", value)) { + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + } }; + } else if (std.mem.eql(u8, "true", value)) { + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }), + } }; + } else if (std.mem.eql(u8, "false", value)) { + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }), + } }; + } else if (std.mem.eql(u8, "usize", value) or std.mem.eql(u8, "isize", value)) { + return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (value[0] == 'u') .usize else .isize, }), - }, - }; - - if (std.mem.eql(u8, "type", value)) { + } }; + } else if (std.mem.eql(u8, "type", value)) { return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -476,7 +435,6 @@ pub fn interpret( // TODO: Floats // Logic to find identifiers in accessible scopes - const namespace = interpreter.scopes.items(.namespace)[scope]; const decl = interpreter.huntItDown(namespace, value, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( node_idx, @@ -497,12 +455,12 @@ pub fn interpret( if (data[node_idx].rhs == 0) return error.CriticalAstFailure; const rhs_str = tree.tokenSlice(data[node_idx].rhs); - var ir = try interpreter.interpret(data[node_idx].lhs, scope, options); + var ir = try interpreter.interpret(data[node_idx].lhs, namespace, options); var irv = try ir.getValue(); - const namespace = interpreter.ip.indexToKey(irv.val).getNamespace(); + const lhs_namespace = interpreter.ip.indexToKey(irv.val).getNamespace(); - var scope_sub_decl = irv.interpreter.huntItDown(namespace, rhs_str, options) catch |err| { + var scope_sub_decl = irv.interpreter.huntItDown(lhs_namespace, rhs_str, options) catch |err| { if (err == error.IdentifierNotFound) try interpreter.recordError( node_idx, "undeclared_identifier", @@ -519,26 +477,28 @@ pub fn interpret( } }; }, .grouped_expression => { - return try interpreter.interpret(data[node_idx].lhs, scope, options); + return try interpreter.interpret(data[node_idx].lhs, namespace, options); }, .@"break" => { const label = if (data[node_idx].lhs == 0) null else tree.tokenSlice(data[node_idx].lhs); return if (data[node_idx].rhs == 0) InterpretResult{ .@"break" = label } else - InterpretResult{ .break_with_value = .{ .label = label, .value = try (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue() } }; + InterpretResult{ .break_with_value = .{ .label = label, .value = try (try interpreter.interpret(data[node_idx].rhs, namespace, options)).getValue() } }; }, .@"return" => { return if (data[node_idx].lhs == 0) InterpretResult{ .@"return" = {} } else - InterpretResult{ .return_with_value = try (try interpreter.interpret(data[node_idx].lhs, scope, options)).getValue() }; + InterpretResult{ .return_with_value = try (try interpreter.interpret(data[node_idx].lhs, namespace, options)).getValue() }; }, - .@"if", .if_simple => { + .@"if", + .if_simple, + => { const iff = ast.ifFull(tree, node_idx); // TODO: Don't evaluate runtime ifs // if (options.observe_values) { - const ir = try interpreter.interpret(iff.ast.cond_expr, scope, options); + const ir = try interpreter.interpret(iff.ast.cond_expr, namespace, options); const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); @@ -546,16 +506,16 @@ pub fn interpret( const condition = (try ir.getValue()).val; std.debug.assert(condition == false_value or condition == true_value); if (condition == true_value) { - return try interpreter.interpret(iff.ast.then_expr, scope, options); + return try interpreter.interpret(iff.ast.then_expr, namespace, options); } else { if (iff.ast.else_expr != 0) { - return try interpreter.interpret(iff.ast.else_expr, scope, options); + return try interpreter.interpret(iff.ast.else_expr, namespace, options); } else return InterpretResult{ .nothing = {} }; } }, .equal_equal => { - var a = try interpreter.interpret(data[node_idx].lhs, scope, options); - var b = try interpreter.interpret(data[node_idx].rhs, scope, options); + var a = try interpreter.interpret(data[node_idx].lhs, namespace, options); + var b = try interpreter.interpret(data[node_idx].rhs, namespace, options); return InterpretResult{ .value = Value{ .interpreter = interpreter, @@ -618,13 +578,13 @@ pub fn interpret( // TODO: Actually consider operators if (std.mem.eql(u8, tree.getNodeSource(data[node_idx].lhs), "_")) { - _ = try interpreter.interpret(data[node_idx].rhs, scope, options); + _ = try interpreter.interpret(data[node_idx].rhs, namespace, options); return InterpretResult{ .nothing = {} }; } - var ir = try interpreter.interpret(data[node_idx].lhs, scope, options); + var ir = try interpreter.interpret(data[node_idx].lhs, namespace, options); var to_value = try ir.getValue(); - var from_value = (try (try interpreter.interpret(data[node_idx].rhs, scope, options)).getValue()); + var from_value = (try (try interpreter.interpret(data[node_idx].rhs, namespace, options)).getValue()); _ = try interpreter.cast(undefined, to_value.ty, from_value.ty); @@ -660,7 +620,7 @@ pub fn interpret( try writer.writeAll("log: "); for (params) |param, index| { - var value = (try interpreter.interpret(param, scope, options)).maybeGetValue() orelse { + var value = (try interpreter.interpret(param, namespace, options)).maybeGetValue() orelse { try writer.writeAll("indeterminate"); continue; }; @@ -722,7 +682,7 @@ pub fn interpret( if (std.mem.eql(u8, call_name, "@TypeOf")) { if (params.len != 1) return error.InvalidBuiltin; - const value = try (try interpreter.interpret(params[0], scope, options)).getValue(); + const value = try (try interpreter.interpret(params[0], namespace, options)).getValue(); return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -734,20 +694,20 @@ pub fn interpret( if (std.mem.eql(u8, call_name, "@hasDecl")) { if (params.len != 2) return error.InvalidBuiltin; - const value = try (try interpreter.interpret(params[0], scope, options)).getValue(); - const field_name = try (try interpreter.interpret(params[1], scope, options)).getValue(); + const value = try (try interpreter.interpret(params[0], namespace, options)).getValue(); + const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue(); const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); 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 namespace = interpreter.ip.indexToKey(value.val).getNamespace(); - if (namespace == .none) return error.InvalidBuiltin; + 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 decls = interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; + const decls = interpreter.namespaces.items(.decls)[@enumToInt(value_namespace)]; const has_decl = decls.contains(name); return InterpretResult{ .value = Value{ @@ -761,8 +721,8 @@ pub fn interpret( if (std.mem.eql(u8, call_name, "@as")) { if (params.len != 2) return error.InvalidBuiltin; - const as_type = try (try interpreter.interpret(params[0], scope, options)).getValue(); - const value = try (try interpreter.interpret(params[1], scope, options)).getValue(); + const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue(); + const value = try (try interpreter.interpret(params[1], namespace, options)).getValue(); const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); @@ -815,9 +775,14 @@ pub fn interpret( }, // TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8) .@"comptime" => { - return try interpreter.interpret(data[node_idx].lhs, scope, .{}); + return try interpreter.interpret(data[node_idx].lhs, namespace, .{}); }, - .fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_simple, .fn_decl => { + .fn_proto, + .fn_proto_multi, + .fn_proto_one, + .fn_proto_simple, + .fn_decl, + => { var buf: [1]Ast.Node.Index = undefined; const func = ast.fnProto(tree, node_idx, &buf).?; @@ -860,7 +825,6 @@ pub fn interpret( const name = offsets.tokenToSlice(tree, func.name_token.?); - const namespace = interpreter.scopes.items(.namespace)[scope]; if (namespace != .none) { const decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; try decls.put(interpreter.allocator, name, .{ @@ -886,20 +850,19 @@ pub fn interpret( .async_call_one_comma, => { var params: [1]Ast.Node.Index = undefined; - const call_full = ast.callFull(tree, node_idx, ¶ms) orelse unreachable; + const call_full = ast.callFull(tree, node_idx, ¶ms).?; var args = try std.ArrayListUnmanaged(Value).initCapacity(interpreter.allocator, call_full.ast.params.len); defer args.deinit(interpreter.allocator); for (call_full.ast.params) |param| { - args.appendAssumeCapacity(try (try interpreter.interpret(param, scope, .{})).getValue()); + args.appendAssumeCapacity(try (try interpreter.interpret(param, namespace, .{})).getValue()); } - const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, scope, .{}); + const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, namespace, .{}); const func_id_val = try func_id_result.getValue(); - const call_res = try interpreter.call(scope, func_id_val.node_idx, args.items, options); - // defer call_res.scope.deinit(); + const call_res = try interpreter.call(namespace, func_id_val.node_idx, args.items, options); // TODO: Figure out call result memory model; this is actually fine because newScope // makes this a child of the decl scope which is freed on refresh... in theory @@ -909,7 +872,7 @@ pub fn interpret( }; }, .bool_not => { - const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); + const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const bool_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }); const value = try result.getValue(); if (value.ty == bool_type) { @@ -934,7 +897,7 @@ pub fn interpret( // TODO: Make const pointers if we're drawing from a const; // variables are the only non-const(?) - const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); + const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const value = (try result.getValue()); const pointer_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{ @@ -956,7 +919,7 @@ pub fn interpret( } }; }, .deref => { - const result = try interpreter.interpret(data[node_idx].lhs, scope, .{}); + const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const value = (try result.getValue()); const type_key = interpreter.ip.indexToKey(value.ty); @@ -981,7 +944,7 @@ pub fn interpret( } pub const CallResult = struct { - scope: u32, + namespace: NamespaceIndex, result: union(enum) { value: Value, nothing, @@ -990,7 +953,7 @@ pub const CallResult = struct { pub fn call( interpreter: *ComptimeInterpreter, - scope: u32, + namespace: NamespaceIndex, func_node_idx: Ast.Node.Index, arguments: []const Value, options: InterpretOptions, @@ -1004,14 +967,13 @@ pub fn call( if (tags[func_node_idx] != .fn_decl) return error.CriticalAstFailure; - // TODO: Make argument scope to evaluate arguments in - try interpreter.scopes.append(interpreter.allocator, Scope{ - .interpreter = interpreter, - .parent = scope, + // TODO: Make argument namespace to evaluate arguments in + try interpreter.namespaces.append(interpreter.allocator, .{ + .parent = namespace, .node_idx = func_node_idx, - .namespace = .none, + .ty = .none, }); - const fn_scope = @intCast(u32, interpreter.scopes.len - 1); + const fn_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); @@ -1022,7 +984,7 @@ pub fn call( var arg_index: usize = 0; while (ast.nextFnParam(&arg_it)) |param| { if (arg_index >= arguments.len) return error.MissingArguments; - var tex = try (try interpreter.interpret(param.type_expr, fn_scope, options)).getValue(); + var tex = try (try interpreter.interpret(param.type_expr, fn_namespace, options)).getValue(); if (tex.ty != type_type) { try interpreter.recordError( param.type_expr, @@ -1031,29 +993,28 @@ pub fn call( ); return error.InvalidCast; } - if (param.name_token) |nt| { - _ = nt; - // const decl = InternPool.Decl{ - // .name = tree.tokenSlice(nt), - // .ty = tex.val, - // .val = try interpreter.cast(arguments[arg_index].node_idx, tex.val, arguments[arg_index].val), - // .alignment = 0, // TODO - // .address_space = .generic, // TODO - // .is_pub = true, // TODO - // .is_exported = false, // TODO - // }; - // TODO - // try fn_scope.declarations.put(interpreter.allocator, tree.tokenSlice(nt), decl); + 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, .{ + .name = name, + .ty = tex.val, + .val = arguments[arg_index].val, + .alignment = 0, // TODO + .address_space = .generic, // TODO + .is_pub = true, // TODO + .is_exported = false, // TODO + }); arg_index += 1; } } const body = tree.nodes.items(.data)[func_node_idx].rhs; - const result = try interpreter.interpret(body, fn_scope, .{}); + const result = try interpreter.interpret(body, fn_namespace, .{}); // TODO: Defers return CallResult{ - .scope = fn_scope, + .namespace = fn_namespace, .result = switch (result) { .@"return", .nothing => .{ .nothing = {} }, // nothing could be due to an error .return_with_value => |v| .{ .value = v }, diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index deb8bc2..f8d4a15 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -963,6 +963,6 @@ pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void { .uri = uri, }; handle.interpreter = int; - _ = try int.interpret(0, 0, .{}); + _ = try int.interpret(0, .none, .{}); } } diff --git a/src/analysis.zig b/src/analysis.zig index aa02488..7720eca 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -769,15 +769,14 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl }; var interpreter = handle.interpreter.?; - try interpreter.scopes.append(interpreter.allocator, .{ - .interpreter = interpreter, - .parent = 0, + try interpreter.namespaces.append(interpreter.allocator, .{ + .parent = .none, .node_idx = 0, - .namespace = .none, + .ty = .none, }); // TODO: Start from current/nearest-current scope - const result = interpreter.interpret(node, 0, .{}) catch |err| { + const result = interpreter.interpret(node, .none, .{}) catch |err| { log.err("Interpreter error: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); From 04d281340c1a79c9b15be9f13a2a9b365b64e2dd Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 14:08:15 +0100 Subject: [PATCH 14/78] restructure ComptieInterpreter tests --- src/zls.zig | 1 + .../comptime_interpreter.zig | 200 ++++++++++++------ 2 files changed, 134 insertions(+), 67 deletions(-) diff --git a/src/zls.zig b/src/zls.zig index fb362d0..ffb943e 100644 --- a/src/zls.zig +++ b/src/zls.zig @@ -12,3 +12,4 @@ pub const types = @import("lsp.zig"); pub const URI = @import("uri.zig"); pub const DocumentStore = @import("DocumentStore.zig"); pub const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); +pub const InternPool = @import("InternPool.zig"); diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 61f8176..e04e843 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -1,98 +1,164 @@ const std = @import("std"); const zls = @import("zls"); +const builtin = @import("builtin"); const Ast = std.zig.Ast; const ComptimeInterpreter = zls.ComptimeInterpreter; +const InternPool = zls.InternPool; +const Index = InternPool.Index; +const Key = InternPool.Key; const allocator: std.mem.Allocator = std.testing.allocator; -test "ComptimeInterpreter - basic test" { - var config = zls.Config{}; - var doc_store = zls.DocumentStore{ - .allocator = allocator, - .config = &config, - }; - defer doc_store.deinit(); - - _ = try doc_store.openDocument("file:///file.zig", - \\pub fn ReturnMyType(comptime my_arg: bool) type { - \\ var abc = z: {break :z if (!my_arg) 123 else 0;}; - \\ if (abc == 123) return u69; - \\ return u8; +test "ComptimeInterpreter - call return primitive type" { + try testCallCheck( + \\pub fn Foo() type { + \\ return bool; \\} - ); + , &.{}, .{ .simple = .bool }); - var interpreter = ComptimeInterpreter{ - .allocator = allocator, - .arena = std.heap.ArenaAllocator.init(allocator), - .document_store = &doc_store, - .uri = "file:///file.zig", - }; - defer interpreter.deinit(); + try testCallCheck( + \\pub fn Foo() type { + \\ return u32; + \\} + , &.{}, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - _ = try interpreter.interpret(0, 0, .{}); + try testCallCheck( + \\pub fn Foo() type { + \\ return i128; + \\} + , &.{}, .{ .int_type = .{ .signedness = .signed, .bits = 128 } }); - var bool_type = try interpreter.ip.get(allocator, .{ .simple = .bool }); - var bool_true = try interpreter.ip.get(allocator, .{ .simple = .bool_true }); - var bool_false = try interpreter.ip.get(allocator, .{ .simple = .bool_false }); - - var arg_false = ComptimeInterpreter.Value{ - .interpreter = &interpreter, - .node_idx = std.math.maxInt(Ast.Node.Index), - .ty = bool_type, - .val = bool_false, - }; - var arg_true = ComptimeInterpreter.Value{ - .interpreter = &interpreter, - .node_idx = std.math.maxInt(Ast.Node.Index), - .ty = bool_type, - .val = bool_true, - }; - - const function_node: Ast.Node.Index = 4; - - const call_with_false = try interpreter.call(0, function_node, &.{arg_false}, .{}); - const call_with_true = try interpreter.call(0, function_node, &.{arg_true}, .{}); - - try std.testing.expectFmt("u69", "{any}", .{call_with_false.result.value.val.fmtValue(call_with_false.result.value.ty, &interpreter.ip)}); - try std.testing.expectFmt("u8", "{any}", .{call_with_true.result.value.val.fmtValue(call_with_true.result.value.ty, &interpreter.ip)}); + try testCallCheck( + \\pub fn Foo() type { + \\ const alpha = i128; + \\ return alpha; + \\} + , &.{}, .{ .int_type = .{ .signedness = .signed, .bits = 128 } }); } -test "ComptimeInterpreter - struct" { - var config = zls.Config{}; - var doc_store = zls.DocumentStore{ - .allocator = allocator, - .config = &config, - }; - defer doc_store.deinit(); - - _ = try doc_store.openDocument("file:///file.zig", - \\pub fn ReturnMyType() type { +test "ComptimeInterpreter - call return struct" { + var result = try testCall( + \\pub fn Foo() type { \\ return struct { \\ slay: bool, \\ var abc = 123; \\ }; \\} - ); + , &.{}); + defer result.deinit(); + const struct_info = result.key.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); + try std.testing.expectEqual(@as(usize, 1), struct_info.fields.len); + // try std.testing.expectEqualStrings("slay", struct_info.fields[0].name); + // try std.testing.expect(struct_info.fields[0].ty != .none); // TODO check for bool +} + +test "ComptimeInterpreter - call comptime argument" { + const source = + \\pub fn Foo(comptime my_arg: bool) type { + \\ var abc = z: {break :z if (!my_arg) 123 else 0;}; + \\ if (abc == 123) return u69; + \\ return u8; + \\} + ; + + var result1 = try testCall(source, &.{ + Value{ + .ty = .{ .simple = .bool }, + .val = .{ .simple = .bool_true }, + }, + }); + defer result1.deinit(); + try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.key); + + var result2 = try testCall(source, &.{ + Value{ + .ty = .{ .simple = .bool }, + .val = .{ .simple = .bool_false }, + }, + }); + defer result2.deinit(); + try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.key); +} + +// +// Helper functions +// + +const CallResult = struct { + interpreter: ComptimeInterpreter, + key: Key, + + pub fn deinit(self: *CallResult) void { + self.interpreter.deinit(); + } +}; + +const Value = struct { + ty: Key, + val: Key, +}; + +fn testCall(source: []const u8, arguments: []const Value) !CallResult { + var config = zls.Config{}; + var doc_store = zls.DocumentStore{ + .allocator = allocator, + .config = &config, + }; + defer doc_store.deinit(); + + const test_uri: []const u8 = switch (builtin.os.tag) { + .windows => "file:///C:\\test.zig", + else => "file:///test.zig", + }; + + const handle = try doc_store.openDocument(test_uri, source); var interpreter = ComptimeInterpreter{ .allocator = allocator, .arena = std.heap.ArenaAllocator.init(allocator), .document_store = &doc_store, - .uri = "file:///file.zig", + .uri = handle.uri, }; - defer interpreter.deinit(); + errdefer interpreter.deinit(); - _ = try interpreter.interpret(0, 0, .{}); + _ = try interpreter.interpret(0, .none, .{}); - const function_node: Ast.Node.Index = 3; + var args = try allocator.alloc(ComptimeInterpreter.Value, arguments.len); + defer allocator.free(args); - const call_result = try interpreter.call(0, function_node, &.{}, .{}); + for (arguments) |argument, i| { + args[i] = .{ + .interpreter = &interpreter, + .node_idx = 0, + .ty = try interpreter.ip.get(interpreter.allocator, argument.ty), + .val = try interpreter.ip.get(interpreter.allocator, argument.val), + }; + } - const result_struct = interpreter.ip.indexToKey(call_result.result.value.val).struct_type; - - try std.testing.expectEqual(@intCast(usize, 1), result_struct.fields.len); - try std.testing.expectEqualStrings("slay", result_struct.fields[0].name); - try std.testing.expectFmt("bool", "{}", .{result_struct.fields[0].ty.fmtType(&interpreter.ip)}); + const func_node = for (handle.tree.nodes.items(.tag)) |tag, i| { + if (tag == .fn_decl) break @intCast(Ast.Node.Index, i); + } else unreachable; + + const call_result = try interpreter.call(.none, func_node, args, .{}); + + try std.testing.expectEqual(Key{ .simple = .type }, interpreter.ip.indexToKey(call_result.result.value.ty)); + + return CallResult{ + .interpreter = interpreter, + .key = interpreter.ip.indexToKey(call_result.result.value.val), + }; +} + +fn testCallCheck( + source: []const u8, + arguments: []const Value, + expected: Key, +) !void { + var result = try testCall(source, arguments); + defer result.deinit(); + try std.testing.expectEqual(expected, result.key); } From 1456bfa1c619b76a7422ef6be68655d98544d764 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 19:17:06 +0100 Subject: [PATCH 15/78] refactor InternPool KeyAdapter --- src/ComptimeInterpreter.zig | 4 +- src/InternPool.zig | 383 ++++++++++++++++++------------------ 2 files changed, 191 insertions(+), 196 deletions(-) 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"); From 5dca172c318fc42a0d3636cb5b54136c5d19e92e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 19:51:44 +0100 Subject: [PATCH 16/78] small cleanup --- src/InternPool.zig | 47 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index e9b0793..02c03f0 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -27,7 +27,6 @@ pub const Pointer = packed struct { }; pub const Array = packed struct { - // TODO support big int len: u32, child: Index, sentinel: Index = .none, @@ -78,7 +77,7 @@ pub const Enum = struct { }; }; -pub const Fn = struct { +pub const Function = struct { calling_convention: std.builtin.CallingConvention = .Unspecified, alignment: u16 = 0, is_generic: bool = false, @@ -114,7 +113,6 @@ pub const Tuple = struct { }; pub const Vector = packed struct { - // TODO support big int len: u32, child: Index, }; @@ -145,7 +143,7 @@ pub const Key = union(enum) { error_union_type: ErrorUnion, error_set_type: ErrorSet, enum_type: Enum, - function_type: Fn, + function_type: Function, union_type: Union, tuple_type: Tuple, vector_type: Vector, @@ -539,7 +537,6 @@ pub const Key = union(enum) { error_union_info.payload_type.fmtType(ip), }); }, - // .error_type => @panic("TODO"), .error_set_type => |error_set_info| { const names = error_set_info.names; try writer.writeAll("error{"); @@ -559,7 +556,10 @@ pub const Key = union(enum) { if (arg.is_comptime) { try writer.writeAll("comptime "); } - // TODO noalias + if (arg.is_noalias) { + try writer.writeAll("noalias "); + } + try printType(arg.arg_type, ip, writer); } @@ -616,9 +616,7 @@ pub const Key = union(enum) { .float_128_value, => unreachable, - // .type_value, .bytes, - // .one_pointer, .aggregate, .union_value, => unreachable, @@ -709,9 +707,7 @@ pub const Key = union(enum) { .float_80_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), .float_128_value => |float| try writer.print("{d}", .{@floatCast(f64, float)}), - // .type_value => |tty| tty.fmtType(ip), .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.len == struct_info.fields.len); @@ -808,7 +804,7 @@ pub const Tag = enum(u8) { /// data is payload to Enum. type_enum, /// An function type. - /// data is payload to Fn. + /// data is payload to Function. type_function, /// An union type. /// data is payload to Union. @@ -911,9 +907,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.map.deinit(gpa); ip.items.deinit(gpa); ip.extra.deinit(gpa); - - // TODO deinit fields } + pub fn indexToKey(ip: InternPool, index: Index) Key { const item = ip.items.get(@enumToInt(index)); const data = item.data; @@ -936,7 +931,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .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(Fn, 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) }, @@ -997,6 +992,8 @@ fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 comptime if (@sizeOf(@TypeOf(extra)) <= 4) { @compileError(@typeName(@TypeOf(extra)) ++ " fits into a u32! Consider directly storing this extra in Item's data field"); }; + + // TODO this doesn't correctly store types with slices like `Struct` `Aggregate` const result = @intCast(u32, ip.extra.items.len); try ip.extra.appendSlice(gpa, &std.mem.toBytes(extra)); return result; @@ -1690,7 +1687,7 @@ const InMemoryCoercionResult = union(enum) { }; const Sentinel = struct { - // unreachable_value indicates no sentinel + // Index.none indicates no sentinel actual: Index, // value wanted: Index, // value ty: Index, @@ -2077,8 +2074,8 @@ fn coerceInMemoryAllowedFns( ip: *InternPool, gpa: std.mem.Allocator, arena: std.mem.Allocator, - dest_info: Fn, - src_info: Fn, + dest_info: Function, + src_info: Function, target: std.Target, ) !InMemoryCoercionResult { if (dest_info.is_var_args != src_info.is_var_args) { @@ -2463,14 +2460,14 @@ test "error set type" { .names = &.{ "foo", "bar", "baz" }, } }); - // const foo: [3]u8 = "foo".*; + const foo: [3]u8 = "foo".*; - // const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{ - // .names = &.{&foo, "bar", "baz"}, - // } }); + const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{ + .names = &.{ &foo, "bar", "baz" }, + } }); std.debug.assert(empty_error_set != error_set_0); - // std.debug.assert(error_set_0 == error_set_1); + std.debug.assert(error_set_0 == error_set_1); try testExpectFmtType(&ip, empty_error_set, "error{}"); try testExpectFmtType(&ip, error_set_0, "error{foo,bar,baz}"); @@ -2570,14 +2567,14 @@ test "function type" { const bool_type = try ip.get(gpa, .{ .simple = .bool }); const type_type = try ip.get(gpa, .{ .simple = .type }); - var param1 = Fn.Param{ .arg_type = i32_type }; + var param1 = Function.Param{ .arg_type = i32_type }; const @"fn(i32) bool" = try ip.get(gpa, .{ .function_type = .{ .return_type = bool_type, .args = &.{param1}, } }); - var param2 = Fn.Param{ .is_comptime = true, .arg_type = type_type }; - var param3 = Fn.Param{ .arg_type = i32_type }; + var param2 = Function.Param{ .is_comptime = true, .arg_type = type_type }; + var param3 = Function.Param{ .arg_type = i32_type }; const @"fn(comptime type, i32) type" = try ip.get(gpa, .{ .function_type = .{ .return_type = type_type, .args = &.{ param2, param3 }, From 969efdfba09870e5daadcec7d52c3317f24d4e3a Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 21:27:55 +0100 Subject: [PATCH 17/78] more tests --- src/InternPool.zig | 177 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 25 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 02c03f0..ba94187 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -166,6 +166,16 @@ pub const Key = union(enum) { // error // error union + pub fn eql(a: Key, b: Key) bool { + return deepEql(a, b); + } + + pub fn hash(a: Key) u32 { + var hasher = std.hash.Wyhash.init(0); + deepHash(&hasher, a); + return @truncate(u32, hasher.final()); + } + pub fn tag(key: Key) Tag { return switch (key) { .simple => .simple, @@ -309,13 +319,13 @@ pub const Key = union(enum) { .int_type => |int_info| return int_info, .enum_type => @panic("TODO"), .struct_type => |struct_info| { - std.debug.assert(struct_info.layout == .Packed); + assert(struct_info.layout == .Packed); key = ip.indexToKey(struct_info.backing_int_ty); }, // TODO revisit this when error sets support custom int types (comment taken from zig codebase) .error_set_type => return .{ .signedness = .unsigned, .bits = 16 }, .vector_type => |vector_info| { - std.debug.assert(vector_info.len == 1); + assert(vector_info.len == 1); key = ip.indexToKey(vector_info.child); }, else => unreachable, @@ -392,10 +402,7 @@ pub const Key = union(enum) { return switch (ty) { .pointer_type => |pointer_info| pointer_info.sentinel, .array_type => |array_info| array_info.sentinel, - .struct_type, - .tuple_type, - .vector_type, - => Index.none, + .vector_type => Index.none, else => unreachable, }; } @@ -710,7 +717,7 @@ pub const Key = union(enum) { .bytes => |bytes| try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}), .aggregate => |aggregate| { const struct_info = ip.indexToKey(ty).struct_type; - std.debug.assert(aggregate.len == struct_info.fields.len); + assert(aggregate.len == struct_info.fields.len); try writer.writeAll(".{"); var i: u32 = 0; @@ -1010,15 +1017,12 @@ const KeyAdapter = struct { 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))); + return a.eql(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()); + return a.hash(); } }; @@ -1027,9 +1031,9 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool { switch (@typeInfo(T)) { .Struct => |info| { - if (info.layout == .Packed) { - return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b)); - } + // 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; } @@ -1065,7 +1069,11 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool { } return true; }, - .Enum, .Int => return a == b, + .Bool, + .Int, + .Float, + .Enum, + => return a == b, else => unreachable, } } @@ -2072,8 +2080,8 @@ fn coerceInMemoryAllowed( fn coerceInMemoryAllowedFns( ip: *InternPool, - gpa: std.mem.Allocator, - arena: std.mem.Allocator, + gpa: Allocator, + arena: Allocator, dest_info: Function, src_info: Function, target: std.Target, @@ -2150,8 +2158,8 @@ fn coerceInMemoryAllowedFns( fn coerceInMemoryAllowedPtrs( ip: *InternPool, - gpa: std.mem.Allocator, - arena: std.mem.Allocator, + gpa: Allocator, + arena: Allocator, dest_ty: Index, src_ty: Index, dest_ptr_info: Key, @@ -2359,6 +2367,36 @@ test "int value" { try testExpectFmtValue(&ip, i64_max_value, undefined, "9223372036854775807"); } +test "float type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const f16_type = try ip.get(gpa, .{ .simple = .f16 }); + const f32_type = try ip.get(gpa, .{ .simple = .f32 }); + const f64_type = try ip.get(gpa, .{ .simple = .f64 }); + const f80_type = try ip.get(gpa, .{ .simple = .f80 }); + const f128_type = try ip.get(gpa, .{ .simple = .f128 }); + + const another_f32_type = try ip.get(gpa, .{ .simple = .f32 }); + const another_f64_type = try ip.get(gpa, .{ .simple = .f64 }); + + try std.testing.expect(f16_type != f32_type); + try std.testing.expect(f32_type != f64_type); + try std.testing.expect(f64_type != f80_type); + try std.testing.expect(f80_type != f128_type); + + try std.testing.expect(f32_type == another_f32_type); + try std.testing.expect(f64_type == another_f64_type); + + try testExpectFmtType(&ip, f16_type, "f16"); + try testExpectFmtType(&ip, f32_type, "f32"); + try testExpectFmtType(&ip, f64_type, "f64"); + try testExpectFmtType(&ip, f80_type, "f80"); + try testExpectFmtType(&ip, f128_type, "f128"); +} + test "pointer type" { const gpa = std.testing.allocator; @@ -2466,8 +2504,8 @@ test "error set type" { .names = &.{ &foo, "bar", "baz" }, } }); - std.debug.assert(empty_error_set != error_set_0); - std.debug.assert(error_set_0 == error_set_1); + try std.testing.expect(empty_error_set != error_set_0); + try std.testing.expect(error_set_0 == error_set_1); try testExpectFmtType(&ip, empty_error_set, "error{}"); try testExpectFmtType(&ip, error_set_0, "error{foo,bar,baz}"); @@ -2554,7 +2592,7 @@ test "struct type" { .backing_int_ty = .none, }, }); - std.debug.assert(struct_type_0 == struct_type_1); + try std.testing.expect(struct_type_0 == struct_type_1); } test "function type" { @@ -2596,10 +2634,36 @@ test "anyframe 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 std.testing.expect(@"anyframe->i32" != @"anyframe->bool"); + try testExpectFmtType(&ip, @"anyframe->i32", "anyframe->i32"); try testExpectFmtType(&ip, @"anyframe->bool", "anyframe->bool"); } +test "vector 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 bool_type = try ip.get(gpa, .{ .simple = .bool }); + + const @"@Vector(2,u32)" = try ip.get(gpa, .{ .vector_type = .{ + .len = 2, + .child = u32_type, + } }); + const @"@Vector(2,bool)" = try ip.get(gpa, .{ .vector_type = .{ + .len = 2, + .child = bool_type, + } }); + + try std.testing.expect(@"@Vector(2,u32)" != @"@Vector(2,bool)"); + + try testExpectFmtType(&ip, @"@Vector(2,u32)", "@Vector(2,u32)"); + try testExpectFmtType(&ip, @"@Vector(2,bool)", "@Vector(2,bool)"); +} + test "resolvePeerTypes" { const gpa = std.testing.allocator; @@ -2611,6 +2675,9 @@ test "resolvePeerTypes" { const noreturn_type = try ip.get(gpa, .{ .simple = .noreturn }); const undefined_type = try ip.get(gpa, .{ .simple = .undefined_type }); + try std.testing.expect(noreturn_type == try ip.resolvePeerTypes(std.testing.allocator, &.{}, builtin.target)); + try std.testing.expect(type_type == try ip.resolvePeerTypes(std.testing.allocator, &.{type_type}, builtin.target)); + try ip.testResolvePeerTypes(Index.none, Index.none, Index.none); try ip.testResolvePeerTypes(bool_type, bool_type, bool_type); try ip.testResolvePeerTypes(bool_type, noreturn_type, bool_type); @@ -2724,6 +2791,23 @@ test "resolvePeerTypes integers and floats" { try ip.testResolvePeerTypes(bool_type, f32_type, Index.none); } +test "resolvePeerTypes optionals" { + 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 null_type = try ip.get(gpa, .{ .simple = .null_type }); + const bool_type = try ip.get(gpa, .{ .simple = .bool }); + + const @"?u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = u32_type } }); + const @"?bool" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = bool_type } }); + + try ip.testResolvePeerTypes(u32_type, null_type, @"?u32"); + try ip.testResolvePeerTypes(bool_type, null_type, @"?bool"); +} + test "resolvePeerTypes pointers" { const gpa = std.testing.allocator; @@ -2735,6 +2819,8 @@ test "resolvePeerTypes pointers" { const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); + const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C } }); const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ @@ -2759,9 +2845,34 @@ test "resolvePeerTypes pointers" { .is_const = true, } }); - const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many } }); + const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Many, + } }); + const @"[*:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Many, + .sentinel = zero_value, + } }); + const @"?[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]u32" } }); - const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice } }); + const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Slice, + } }); + const @"[]const u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Slice, + .is_const = true, + } }); + const @"[:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Slice, + .sentinel = zero_value, + } }); + + const @"?[]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]u32" } }); + const @"?[]const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]const u32" } }); try ip.testResolvePeerTypes(@"[*c]u32", comptime_int_type, @"[*c]u32"); try ip.testResolvePeerTypes(@"[*c]u32", u32_type, @"[*c]u32"); @@ -2770,12 +2881,28 @@ test "resolvePeerTypes pointers" { try ip.testResolvePeerTypes(@"[*]u32", @"*[2]u32", @"[*]u32"); try ip.testResolvePeerTypes(@"[]u32", @"*[2]u32", @"[]u32"); + try ip.testResolvePeerTypes(@"?[*]u32", @"*[2]u32", @"?[*]u32"); + try ip.testResolvePeerTypes(@"?[]u32", @"*[2]u32", @"?[]u32"); + try ip.testResolvePeerTypes(@"?[]u32", @"?[]const u32", @"?[]const u32"); try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); try ip.testResolvePeerTypes(@"*u32", @"*const u32", @"*const u32"); try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32"); + + try testExpectFmtType(&ip, @"[*c]u32", "[*c]u32"); + try testExpectFmtType(&ip, @"*[1]u32", "*[1]u32"); + try testExpectFmtType(&ip, @"*[2]u32", "*[2]u32"); + try testExpectFmtType(&ip, @"*u32", "*u32"); + try testExpectFmtType(&ip, @"*const u32", "*const u32"); + try testExpectFmtType(&ip, @"[*]u32", "[*]u32"); + try testExpectFmtType(&ip, @"[*:0]u32", "[*:0]u32"); + try testExpectFmtType(&ip, @"[]u32", "[]u32"); + try testExpectFmtType(&ip, @"[]const u32", "[]const u32"); + try testExpectFmtType(&ip, @"[:0]u32", "[:0]u32"); + try testExpectFmtType(&ip, @"?[]u32", "?[]u32"); + try testExpectFmtType(&ip, @"?[]const u32", "?[]const u32"); } fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { From d49979d002161515289cf212abf821590a53e0d5 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 21:30:52 +0100 Subject: [PATCH 18/78] remove arena from ComptimeInterpreter --- src/ComptimeInterpreter.zig | 8 ++------ src/DocumentStore.zig | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index ad56b17..92aefba 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -19,10 +19,7 @@ pub const ComptimeInterpreter = @This(); const log = std.log.scoped(.comptime_interpreter); -// TODO: Investigate arena - allocator: std.mem.Allocator, -arena: std.heap.ArenaAllocator, ip: InternPool = .{}, document_store: *DocumentStore, uri: DocumentStore.Uri, @@ -64,8 +61,6 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void { } interpreter.namespaces.deinit(interpreter.allocator); interpreter.decls.deinit(interpreter.allocator); - - interpreter.arena.deinit(); } pub const Type = struct { @@ -241,6 +236,7 @@ pub fn interpret( const container_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); var fields = std.ArrayListUnmanaged(InternPool.Struct.Field){}; + defer fields.deinit(interpreter.allocator); var buffer: [2]Ast.Node.Index = undefined; const members = ast.declMembers(tree, node_idx, &buffer); @@ -274,7 +270,7 @@ pub fn interpret( .is_comptime = false, // TODO }; - try fields.append(interpreter.arena.allocator(), field); + try fields.append(interpreter.allocator, field); } const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{ diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index f8d4a15..31257ee 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -958,7 +958,6 @@ pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void { var int = try self.allocator.create(ComptimeInterpreter); int.* = ComptimeInterpreter{ .allocator = self.allocator, - .arena = std.heap.ArenaAllocator.init(self.allocator), .document_store = self, .uri = uri, }; From bdf207a8214378e93562ab36331f032776c4164e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 21:49:27 +0100 Subject: [PATCH 19/78] small refactor on ComptimeInterpreter --- src/ComptimeInterpreter.zig | 48 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 92aefba..567748a 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -155,7 +155,7 @@ pub fn huntItDown( namespace: NamespaceIndex, decl_name: []const u8, options: InterpretOptions, -) InterpretError!Decl { +) error{IdentifierNotFound}!Decl { _ = options; var current_namespace = namespace; @@ -168,7 +168,6 @@ pub fn huntItDown( } } - log.err("Identifier not found: {s}", .{decl_name}); return error.IdentifierNotFound; } @@ -431,13 +430,15 @@ pub fn interpret( // TODO: Floats // Logic to find identifiers in accessible scopes - const decl = interpreter.huntItDown(namespace, value, options) catch |err| { - if (err == error.IdentifierNotFound) try interpreter.recordError( - node_idx, - "undeclared_identifier", - try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{value}), - ); - return err; + const decl = interpreter.huntItDown(namespace, value, options) catch |err| switch (err) { + error.IdentifierNotFound => |e| { + try interpreter.recordError( + node_idx, + "undeclared_identifier", + try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{value}), + ); + return e; + }, }; return InterpretResult{ .value = Value{ @@ -456,13 +457,15 @@ pub fn interpret( const lhs_namespace = interpreter.ip.indexToKey(irv.val).getNamespace(); - var scope_sub_decl = irv.interpreter.huntItDown(lhs_namespace, rhs_str, options) catch |err| { - if (err == error.IdentifierNotFound) try interpreter.recordError( - node_idx, - "undeclared_identifier", - try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{rhs_str}), - ); - return err; + var scope_sub_decl = irv.interpreter.huntItDown(lhs_namespace, rhs_str, options) catch |err| switch (err) { + error.IdentifierNotFound => |e| { + try interpreter.recordError( + node_idx, + "undeclared_identifier", + try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{rhs_str}), + ); + return e; + }, }; return InterpretResult{ .value = Value{ @@ -760,13 +763,9 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = string_literal_type, - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), // TODO + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), }; - // TODO: Add type casting, sentinel - // TODO: Should this be a `*const [len:0]u8`? - // try val.value_data.slice_ptr.append(interpreter.allocator, .{ .unsigned_int = 0 }); - return InterpretResult{ .value = val }; }, // TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8) @@ -959,9 +958,9 @@ pub fn call( // TODO: type check args const tree = interpreter.getHandle().tree; - const tags = tree.nodes.items(.tag); - if (tags[func_node_idx] != .fn_decl) return error.CriticalAstFailure; + var buf: [1]Ast.Node.Index = undefined; + var proto = ast.fnProto(tree, func_node_idx, &buf) orelse return error.CriticalAstFailure; // TODO: Make argument namespace to evaluate arguments in try interpreter.namespaces.append(interpreter.allocator, .{ @@ -973,9 +972,6 @@ pub fn call( const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); - var buf: [1]Ast.Node.Index = undefined; - var proto = ast.fnProto(tree, func_node_idx, &buf).?; - var arg_it = proto.iterate(&tree); var arg_index: usize = 0; while (ast.nextFnParam(&arg_it)) |param| { From f5188c44079482f8c5f0544b560077372a68af1e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 14 Jan 2023 21:54:45 +0100 Subject: [PATCH 20/78] update test --- tests/language_features/comptime_interpreter.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index e04e843..79dc509 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -119,7 +119,6 @@ fn testCall(source: []const u8, arguments: []const Value) !CallResult { var interpreter = ComptimeInterpreter{ .allocator = allocator, - .arena = std.heap.ArenaAllocator.init(allocator), .document_store = &doc_store, .uri = handle.uri, }; From 45f03ca23965ab87448169ed06a0cb48fa92347d Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 15 Jan 2023 15:41:54 +0100 Subject: [PATCH 21/78] add bit_offset & host_size to Pointer --- src/InternPool.zig | 53 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index ba94187..89b7068 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -20,6 +20,8 @@ pub const Pointer = packed struct { sentinel: Index = .none, alignment: u16 = 0, size: std.builtin.Type.Pointer.Size, + bit_offset: u16 = 0, + host_size: u16 = 0, is_const: bool = false, is_volatile: bool = false, is_allowzero: bool = false, @@ -510,8 +512,10 @@ pub const Key = union(enum) { if (pointer_info.alignment != 0) { try writer.print("align({d}", .{pointer_info.alignment}); - // TODO bit offset - // TODO host size + if (pointer_info.bit_offset != 0 or pointer_info.host_size != 0) { + try writer.print(":{d}:{d}", .{ pointer_info.bit_offset, pointer_info.host_size }); + } + try writer.writeAll(") "); } @@ -2219,16 +2223,16 @@ fn coerceInMemoryAllowedPtrs( } }; } - // if (src_info.host_size != dest_info.host_size or - // src_info.bit_offset != dest_info.bit_offset) - // { - // return InMemoryCoercionResult{ .ptr_bit_range = .{ - // .actual_host = src_info.host_size, - // .wanted_host = dest_info.host_size, - // .actual_offset = src_info.bit_offset, - // .wanted_offset = dest_info.bit_offset, - // } }; - // } + if (src_info.host_size != dest_info.host_size or + src_info.bit_offset != dest_info.bit_offset) + { + return InMemoryCoercionResult{ .ptr_bit_range = .{ + .actual_host = src_info.host_size, + .wanted_host = dest_info.host_size, + .actual_offset = src_info.bit_offset, + .wanted_offset = dest_info.bit_offset, + } }; + } const ok_sent = dest_info.sentinel == .none or src_info.size == .C or dest_info.sentinel == src_info.sentinel; // is this enough for a value equality check? if (!ok_sent) { @@ -2441,22 +2445,39 @@ test "pointer type" { .size = .One, .alignment = 4, } }); - const @"[*c]const volatile u32" = try ip.get(gpa, .{ .pointer_type = Pointer{ + const @"*align(4:2:3) u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .One, + .alignment = 4, + .bit_offset = 2, + .host_size = 3, + } }); + const @"[*c]const volatile u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C, .is_const = true, .is_volatile = true, } }); + const @"*addrspace(.shared) const u32" = try ip.get(gpa, .{ .pointer_type = Pointer{ + .elem_type = u32_type, + .size = .One, + .is_const = true, + .address_space = .shared, + } }); - try std.testing.expect(@"*const u32" != u32_pointer_type); - try std.testing.expect(u32_pointer_type != @"*align(4) u32"); - try std.testing.expect(@"*align(4) u32" != @"[*c]const volatile u32"); + try std.testing.expect(u32_pointer_type != @"*const u32"); + try std.testing.expect(@"*const u32" != @"*align(4) u32"); + try std.testing.expect(@"*align(4) u32" != @"*align(4:2:3) u32"); + try std.testing.expect(@"*align(4:2:3) u32" != @"[*c]const volatile u32"); + try std.testing.expect(@"[*c]const volatile u32" != @"*addrspace(.shared) const u32"); try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); try testExpectFmtType(&ip, u32_pointer_type, "*u32"); try testExpectFmtType(&ip, @"*const u32", "*const u32"); try testExpectFmtType(&ip, @"*align(4) u32", "*align(4) u32"); + try testExpectFmtType(&ip, @"*align(4:2:3) u32", "*align(4:2:3) u32"); try testExpectFmtType(&ip, @"[*c]const volatile u32", "[*c]const volatile u32"); + try testExpectFmtType(&ip, @"*addrspace(.shared) const u32", "*addrspace(.shared) const u32"); } test "optional type" { From 82e3ab9f2e85c6c746b93682c69816ba27c19287 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 15 Jan 2023 18:25:00 +0100 Subject: [PATCH 22/78] more tests --- src/InternPool.zig | 131 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 5 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 89b7068..e076c2f 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -583,7 +583,7 @@ pub const Key = union(enum) { try writer.writeAll(") "); if (function_info.calling_convention != .Unspecified) { - try writer.print("callconv(.{s})", .{@tagName(function_info.calling_convention)}); + try writer.print("callconv(.{s}) ", .{@tagName(function_info.calling_convention)}); } if (function_info.alignment != 0) { try writer.print("align({d}) ", .{function_info.alignment}); @@ -956,7 +956,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .limbs = ip.extraData([]const std.math.big.Limb, data), } }, .int_big_negative => .{ .int_big_value = .{ - .positive = true, + .positive = false, .limbs = ip.extraData([]const std.math.big.Limb, data), } }, .float_f16 => .{ .float_16_value = @bitCast(f16, @intCast(u16, data)) }, @@ -2319,6 +2319,34 @@ fn testExpectFmtValue(ip: *const InternPool, val: Index, ty: Index, expected: [] try std.testing.expectFmt(expected, "{}", .{val.fmtValue(ty, ip)}); } +test "simple types" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const null_type = try ip.get(gpa, .{ .simple = .null_type }); + const undefined_type = try ip.get(gpa, .{ .simple = .undefined_type }); + const enum_literal_type = try ip.get(gpa, .{ .simple = .enum_literal_type }); + const undefined_value = try ip.get(gpa, .{ .simple = .undefined_value }); + const void_value = try ip.get(gpa, .{ .simple = .void_value }); + const unreachable_value = try ip.get(gpa, .{ .simple = .unreachable_value }); + const null_value = try ip.get(gpa, .{ .simple = .null_value }); + const bool_true = try ip.get(gpa, .{ .simple = .bool_true }); + const bool_false = try ip.get(gpa, .{ .simple = .bool_false }); + + try testExpectFmtType(&ip, null_type, "@TypeOf(null)"); + try testExpectFmtType(&ip, undefined_type, "@TypeOf(undefined)"); + try testExpectFmtType(&ip, enum_literal_type, "@TypeOf(.enum_literal)"); + + try testExpectFmtValue(&ip, undefined_value, undefined, "@Type(.Undefined)"); + try testExpectFmtValue(&ip, void_value, undefined, "void"); + try testExpectFmtValue(&ip, unreachable_value, undefined, "unreachable"); + try testExpectFmtValue(&ip, null_value, undefined, "null"); + try testExpectFmtValue(&ip, bool_true, undefined, "true"); + try testExpectFmtValue(&ip, bool_false, undefined, "false"); +} + test "int type" { const gpa = std.testing.allocator; @@ -2371,6 +2399,26 @@ test "int value" { try testExpectFmtValue(&ip, i64_max_value, undefined, "9223372036854775807"); } +test "big int value" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + var result = try std.math.big.int.Managed.init(gpa); + defer result.deinit(); + var a = try std.math.big.int.Managed.initSet(gpa, 2); + defer a.deinit(); + + try result.pow(&a, 128); + + const positive_big_int_value = try ip.get(gpa, .{ .int_big_value = result.toConst() }); + const negative_big_int_value = try ip.get(gpa, .{ .int_big_value = result.toConst().negate() }); + + try testExpectFmtValue(&ip, positive_big_int_value, undefined, "340282366920938463463374607431768211456"); + try testExpectFmtValue(&ip, negative_big_int_value, undefined, "-340282366920938463463374607431768211456"); +} + test "float type" { const gpa = std.testing.allocator; @@ -2401,6 +2449,30 @@ test "float type" { try testExpectFmtType(&ip, f128_type, "f128"); } +test "float value" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 }); + const f32_value = try ip.get(gpa, .{ .float_32_value = 0.5 }); + const f64_value = try ip.get(gpa, .{ .float_64_value = 1.0 }); + const f80_value = try ip.get(gpa, .{ .float_80_value = 2.0 }); + const f128_value = try ip.get(gpa, .{ .float_128_value = 2.75 }); + + try std.testing.expect(f16_value != f32_value); + try std.testing.expect(f32_value != f64_value); + try std.testing.expect(f64_value != f80_value); + try std.testing.expect(f80_value != f128_value); + + try testExpectFmtValue(&ip, f16_value, undefined, "0.25"); + try testExpectFmtValue(&ip, f32_value, undefined, "0.5"); + try testExpectFmtValue(&ip, f64_value, undefined, "1"); + try testExpectFmtValue(&ip, f80_value, undefined, "2"); + try testExpectFmtValue(&ip, f128_value, undefined, "2.75"); +} + test "pointer type" { const gpa = std.testing.allocator; @@ -2633,14 +2705,29 @@ test "function type" { } }); var param2 = Function.Param{ .is_comptime = true, .arg_type = type_type }; - var param3 = Function.Param{ .arg_type = i32_type }; - const @"fn(comptime type, i32) type" = try ip.get(gpa, .{ .function_type = .{ + var param3 = Function.Param{ .is_noalias = true, .arg_type = i32_type }; + const @"fn(comptime type, noalias i32) type" = try ip.get(gpa, .{ .function_type = .{ .return_type = type_type, .args = &.{ param2, param3 }, } }); + const @"fn(i32, ...) type" = try ip.get(gpa, .{ .function_type = .{ + .is_var_args = true, + .return_type = type_type, + .args = &.{param1}, + } }); + + const @"fn() callconv(.C) align(4) type" = try ip.get(gpa, .{ .function_type = .{ + .calling_convention = .C, + .alignment = 4, + .return_type = type_type, + .args = &.{}, + } }); + try testExpectFmtType(&ip, @"fn(i32) bool", "fn(i32) bool"); - try testExpectFmtType(&ip, @"fn(comptime type, i32) type", "fn(comptime type, i32) type"); + try testExpectFmtType(&ip, @"fn(comptime type, noalias i32) type", "fn(comptime type, noalias i32) type"); + try testExpectFmtType(&ip, @"fn(i32, ...) type", "fn(i32, ...) type"); + try testExpectFmtType(&ip, @"fn() callconv(.C) align(4) type", "fn() callconv(.C) align(4) type"); } test "anyframe type" { @@ -2685,6 +2772,40 @@ test "vector type" { try testExpectFmtType(&ip, @"@Vector(2,bool)", "@Vector(2,bool)"); } +test "bytes value" { + if (true) return error.SkipZigTest; // TODO + + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + var str1: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*; + + const bytes_value1 = try ip.get(gpa, .{ .bytes = &str1 }); + @memset(&str1, 0, str1.len); + + var str2: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*; + @memset(&str2, 0, str2.len); + + const bytes_value2 = try ip.get(gpa, .{ .bytes = &str2 }); + + var str3: [26]u8 = "https://www.duckduckgo.com".*; + const bytes_value3 = try ip.get(gpa, .{ .bytes = &str3 }); + @memset(&str3, 0, str3.len); + + try std.testing.expect(bytes_value1 == bytes_value2); + try std.testing.expect(bytes_value2 != bytes_value3); + + try std.testing.expect(str1 != ip.indexToKey(bytes_value1).bytes); + try std.testing.expect(str2 != ip.indexToKey(bytes_value2).bytes); + try std.testing.expect(str3 != ip.indexToKey(bytes_value3).bytes); + + try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value1).bytes); + try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value2).bytes); + try std.testing.expectEqualStrings("https://www.duckduckgo.com", ip.indexToKey(bytes_value3).bytes); +} + test "resolvePeerTypes" { const gpa = std.testing.allocator; From 739bd08b7beb1f75e033181d5eb221513a576a2f Mon Sep 17 00:00:00 2001 From: Techarix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 16 Jan 2023 12:38:35 +0100 Subject: [PATCH 23/78] fix storage of negative signed integer values --- src/InternPool.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index e076c2f..5425722 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -200,7 +200,7 @@ pub const Key = union(enum) { .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, + .int_i64_value => |int| if (std.math.minInt(i32) <= int and int <= std.math.maxInt(i32)) .int_i32 else .int_i64, .int_big_value => |big_int| if (big_int.positive) .int_big_positive else .int_big_negative, .float_16_value => .float_f16, .float_32_value => .float_f32, @@ -2382,6 +2382,14 @@ test "int value" { const u64_max_value = try ip.get(gpa, .{ .int_u64_value = std.math.maxInt(u64) }); const i64_max_value = try ip.get(gpa, .{ .int_i64_value = std.math.maxInt(i64) }); + const i64_min_value = try ip.get(gpa, .{ .int_i64_value = std.math.minInt(i64) }); + + const tags = ip.items.items(.tag); + try std.testing.expect(tags[@enumToInt(unsigned_one_value)] == .int_u32); + try std.testing.expect(tags[@enumToInt(signed_one_value)] == .int_i32); + try std.testing.expect(tags[@enumToInt(u64_max_value)] == .int_u64); + try std.testing.expect(tags[@enumToInt(i64_max_value)] == .int_i64); + try std.testing.expect(tags[@enumToInt(i64_min_value)] == .int_i64); try std.testing.expect(unsigned_zero_value != unsigned_one_value); try std.testing.expect(unsigned_one_value != signed_zero_value); @@ -2389,6 +2397,7 @@ test "int value" { try std.testing.expect(signed_one_value != u64_max_value); try std.testing.expect(u64_max_value != i64_max_value); + try std.testing.expect(i64_max_value != i64_min_value); try testExpectFmtValue(&ip, unsigned_zero_value, undefined, "0"); try testExpectFmtValue(&ip, unsigned_one_value, undefined, "1"); @@ -2397,6 +2406,7 @@ test "int value" { try testExpectFmtValue(&ip, u64_max_value, undefined, "18446744073709551615"); try testExpectFmtValue(&ip, i64_max_value, undefined, "9223372036854775807"); + try testExpectFmtValue(&ip, i64_min_value, undefined, "-9223372036854775808"); } test "big int value" { From 407f921ef8c519c86f9718ea7694a28c6f2b967c Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 17 Jan 2023 20:23:27 +0100 Subject: [PATCH 24/78] refactor type printing --- src/ComptimeInterpreter.zig | 6 +-- src/InternPool.zig | 99 +++++++++++++++++++++++-------------- src/Server.zig | 6 +-- src/analysis.zig | 2 +- 4 files changed, 69 insertions(+), 44 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 567748a..26ce420 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -256,7 +256,7 @@ pub fn interpret( try interpreter.recordError( container_field.ast.type_expr, "expected_type", - try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(&interpreter.ip)}), + try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(interpreter.ip)}), ); continue; } @@ -623,7 +623,7 @@ pub fn interpret( try writer.writeAll("indeterminate"); continue; }; - try writer.print("@as({}, {})", .{ value.ty.fmtType(&interpreter.ip), value.val.fmtValue(value.ty, &interpreter.ip) }); + try writer.print("@as({}, {})", .{ value.ty.fmtType(interpreter.ip), value.val.fmtValue(value.ty, interpreter.ip) }); if (index != params.len - 1) try writer.writeAll(", "); } @@ -981,7 +981,7 @@ pub fn call( try interpreter.recordError( param.type_expr, "expected_type", - std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{tex.ty.fmtType(&interpreter.ip)}) catch return error.CriticalAstFailure, + std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{tex.ty.fmtType(interpreter.ip)}) catch return error.CriticalAstFailure, ); return error.InvalidCast; } diff --git a/src/InternPool.zig b/src/InternPool.zig index 5425722..7cef585 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -419,16 +419,17 @@ pub const Key = union(enum) { } pub const TypeFormatContext = struct { - ty: Index, + ty: Key, options: FormatOptions = .{}, - ip: *const InternPool, + ip: InternPool, }; pub const ValueFormatContext = struct { - value: Index, + value: Key, + /// for most values the type is not needed which is why we use an index ty: Index, options: FormatOptions = .{}, - ip: *const InternPool, + ip: InternPool, }; // TODO implement options @@ -437,20 +438,30 @@ pub const Key = union(enum) { include_declarations: bool = true, }; - pub fn formatType( + fn formatType( ctx: TypeFormatContext, - comptime unused_format_string: []const u8, + comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - comptime assert(unused_format_string.len == 0); _ = options; - return printType(ctx.ty, ctx.ip, writer); + if (fmt.len != 0) std.fmt.invalidFmtError(fmt, Key); + try printTypeKey(ctx.ty, ctx.ip, writer); } - pub fn printType(ty: Index, ip: *const InternPool, writer: anytype) @TypeOf(writer).Error!void { - const key: Key = ip.indexToKey(ty); - switch (key) { + fn printType(ty: Index, ip: InternPool, writer: anytype) @TypeOf(writer).Error!void { + try printTypeKey(ip.indexToKey(ty), ip, writer); + } + + fn printTypeKey(ty: Key, ip: InternPool, writer: anytype) @TypeOf(writer).Error!void { + var key = ty; + while (try printTypeInternal(key, ip, writer)) |index| { + key = ip.indexToKey(index); + } + } + + fn printTypeInternal(ty: Key, ip: InternPool, writer: anytype) @TypeOf(writer).Error!?Index { + switch (ty) { .simple => |simple| switch (simple) { .f16, .f32, @@ -527,7 +538,7 @@ pub const Key = union(enum) { if (pointer_info.is_volatile) try writer.writeAll("volatile "); if (pointer_info.is_allowzero and pointer_info.size != .C) try writer.writeAll("allowzero "); - try printType(pointer_info.elem_type, ip, writer); + return pointer_info.elem_type; }, .array_type => |array_info| { try writer.print("[{d}", .{array_info.len}); @@ -535,18 +546,18 @@ pub const Key = union(enum) { try writer.print(":{}", .{array_info.sentinel.fmtValue(array_info.child, ip)}); } try writer.writeByte(']'); - try printType(array_info.child, ip, writer); + + return array_info.child; }, .struct_type => @panic("TODO"), .optional_type => |optional_info| { try writer.writeByte('?'); - try printType(optional_info.payload_type, ip, writer); + return optional_info.payload_type; }, .error_union_type => |error_union_info| { - try writer.print("{}!{}", .{ - error_union_info.error_set_type.fmtType(ip), - error_union_info.payload_type.fmtType(ip), - }); + try printType(error_union_info.error_set_type, ip, writer); + try writer.writeByte('!'); + return error_union_info.payload_type; }, .error_set_type => |error_set_info| { const names = error_set_info.names; @@ -585,10 +596,8 @@ pub const Key = union(enum) { if (function_info.calling_convention != .Unspecified) { try writer.print("callconv(.{s}) ", .{@tagName(function_info.calling_convention)}); } - if (function_info.alignment != 0) { - try writer.print("align({d}) ", .{function_info.alignment}); - } - try printType(function_info.return_type, ip, writer); + + return function_info.return_type; }, .union_type => @panic("TODO"), .tuple_type => |tuple_info| { @@ -614,7 +623,7 @@ pub const Key = union(enum) { }, .anyframe_type => |anyframe_info| { try writer.writeAll("anyframe->"); - try printType(anyframe_info.child, ip, writer); + return anyframe_info.child; }, .int_u64_value, @@ -632,27 +641,28 @@ pub const Key = union(enum) { .union_value, => unreachable, } + return null; } - pub fn formatValue( + fn formatValue( ctx: ValueFormatContext, - comptime unused_format_string: []const u8, + comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - comptime assert(unused_format_string.len == 0); + comptime assert(fmt.len == 0); _ = options; + if (fmt.len != 0) std.fmt.invalidFmtError(fmt, Key); return printValue(ctx.value, ctx.ty, ctx.ip, writer); } - pub fn printValue( - value: Index, + fn printValue( + value: Key, ty: Index, - ip: *const InternPool, + ip: InternPool, writer: anytype, ) @TypeOf(writer).Error!void { - const value_key: Key = ip.indexToKey(value); - switch (value_key) { + switch (value) { .simple => |simple| switch (simple) { .f16, .f32, @@ -729,7 +739,7 @@ pub const Key = union(enum) { if (i != 0) try writer.writeAll(", "); try writer.print(".{s} = ", .{struct_info.fields[i].name}); - try printValue(aggregate[i], struct_info.fields[i].ty, ip, writer); + try printValue(ip.indexToKey(aggregate[i]), struct_info.fields[i].ty, ip, writer); } try writer.writeByte('}'); }, @@ -737,13 +747,28 @@ pub const Key = union(enum) { const union_info = ip.indexToKey(ty).union_type; try writer.writeAll(".{ "); - try printValue(union_info.tag_type, union_value.tag, ip, writer); + try printValue(ip.indexToKey(union_info.tag_type), union_value.tag, ip, writer); try writer.writeAll(" = "); try printValue(union_value.val, @panic("TODO"), ip, writer); try writer.writeAll(" }"); }, } } + + pub fn fmtType(ty: Key, ip: InternPool) std.fmt.Formatter(formatType) { + return .{ .data = .{ + .ty = ty, + .ip = ip, + } }; + } + + pub fn fmtValue(value: Key, ty: Index, ip: InternPool) std.fmt.Formatter(formatValue) { + return .{ .data = .{ + .value = value, + .ty = ty, + .ip = ip, + } }; + } }; pub const Item = struct { @@ -761,16 +786,16 @@ pub const Index = enum(u32) { none = std.math.maxInt(u32), _, - pub fn fmtType(ty: Index, ip: *const InternPool) std.fmt.Formatter(Key.formatType) { + pub fn fmtType(ty: Index, ip: InternPool) std.fmt.Formatter(Key.formatType) { return .{ .data = .{ - .ty = ty, + .ty = ip.indexToKey(ty), .ip = ip, } }; } - pub fn fmtValue(value_index: Index, type_index: Index, ip: *const InternPool) std.fmt.Formatter(Key.formatValue) { + pub fn fmtValue(value_index: Index, type_index: Index, ip: InternPool) std.fmt.Formatter(Key.formatValue) { return .{ .data = .{ - .value = value_index, + .value = ip.indexToKey(value_index), .ty = type_index, .ip = ip, } }; diff --git a/src/Server.zig b/src/Server.zig index 4c2fc63..e1fc2a6 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -583,7 +583,7 @@ fn typeToCompletion( .primitive, .array_index => {}, .@"comptime" => |co| { const key = co.interpreter.ip.indexToKey(co.type.ty); - + switch (key) { .struct_type => |struct_info| { for (struct_info.fields) |field| { @@ -594,7 +594,7 @@ fn typeToCompletion( .insertTextFormat = .PlainText, }); } - + // TODO declaration completion }, else => {}, @@ -916,7 +916,7 @@ fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutO const resolved_type_str = if (resolved_type) |rt| if (rt.type.is_type_val) switch (rt.type.data) { - .@"comptime" => |*co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.type.ty.fmtType(&co.interpreter.ip)}), + .@"comptime" => |*co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.type.ty.fmtType(co.interpreter.ip)}), else => "type", } else switch (rt.type.data) { // TODO: Investigate random weird numbers like 897 that cause index of bounds .pointer, diff --git a/src/analysis.zig b/src/analysis.zig index 7720eca..c7e77ac 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -793,7 +793,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.IPKey{ .simple = .type }); if (val.ty != type_type) { - log.err("Not a type: {}", .{val.ty.fmtType(&interpreter.ip)}); + log.err("Not a type: {}", .{val.ty.fmtType(interpreter.ip)}); return null; } From fb3a4238d79262bf075071805af124075e1f05be Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 17 Jan 2023 20:32:49 +0100 Subject: [PATCH 25/78] update tests --- src/InternPool.zig | 156 +++++++++++++++++++++++---------------------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 7cef585..8c2bb03 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -593,6 +593,9 @@ pub const Key = union(enum) { } try writer.writeAll(") "); + if (function_info.alignment != 0) { + try writer.print("align({d}) ", .{function_info.alignment}); + } if (function_info.calling_convention != .Unspecified) { try writer.print("callconv(.{s}) ", .{@tagName(function_info.calling_convention)}); } @@ -2336,11 +2339,11 @@ fn optionalPtrTy( // TESTS // --------------------------------------------- -fn testExpectFmtType(ip: *const InternPool, index: Index, expected: []const u8) !void { +fn testExpectFmtType(ip: InternPool, index: Index, expected: []const u8) !void { try std.testing.expectFmt(expected, "{}", .{index.fmtType(ip)}); } -fn testExpectFmtValue(ip: *const InternPool, val: Index, ty: Index, expected: []const u8) !void { +fn testExpectFmtValue(ip: InternPool, val: Index, ty: Index, expected: []const u8) !void { try std.testing.expectFmt(expected, "{}", .{val.fmtValue(ty, ip)}); } @@ -2360,16 +2363,16 @@ test "simple types" { const bool_true = try ip.get(gpa, .{ .simple = .bool_true }); const bool_false = try ip.get(gpa, .{ .simple = .bool_false }); - try testExpectFmtType(&ip, null_type, "@TypeOf(null)"); - try testExpectFmtType(&ip, undefined_type, "@TypeOf(undefined)"); - try testExpectFmtType(&ip, enum_literal_type, "@TypeOf(.enum_literal)"); + try testExpectFmtType(ip, null_type, "@TypeOf(null)"); + try testExpectFmtType(ip, undefined_type, "@TypeOf(undefined)"); + try testExpectFmtType(ip, enum_literal_type, "@TypeOf(.enum_literal)"); - try testExpectFmtValue(&ip, undefined_value, undefined, "@Type(.Undefined)"); - try testExpectFmtValue(&ip, void_value, undefined, "void"); - try testExpectFmtValue(&ip, unreachable_value, undefined, "unreachable"); - try testExpectFmtValue(&ip, null_value, undefined, "null"); - try testExpectFmtValue(&ip, bool_true, undefined, "true"); - try testExpectFmtValue(&ip, bool_false, undefined, "false"); + try testExpectFmtValue(ip, undefined_value, Index.none, "@Type(.Undefined)"); + try testExpectFmtValue(ip, void_value, Index.none, "void"); + try testExpectFmtValue(ip, unreachable_value, Index.none, "unreachable"); + try testExpectFmtValue(ip, null_value, Index.none, "null"); + try testExpectFmtValue(ip, bool_true, Index.none, "true"); + try testExpectFmtValue(ip, bool_false, Index.none, "false"); } test "int type" { @@ -2389,9 +2392,9 @@ test "int type" { try std.testing.expect(i16_type != another_i32_type); try std.testing.expect(i16_type != u7_type); - try testExpectFmtType(&ip, i32_type, "i32"); - try testExpectFmtType(&ip, i16_type, "i16"); - try testExpectFmtType(&ip, u7_type, "u7"); + try testExpectFmtType(ip, i32_type, "i32"); + try testExpectFmtType(ip, i16_type, "i16"); + try testExpectFmtType(ip, u7_type, "u7"); } test "int value" { @@ -2424,14 +2427,14 @@ test "int value" { try std.testing.expect(u64_max_value != i64_max_value); try std.testing.expect(i64_max_value != i64_min_value); - try testExpectFmtValue(&ip, unsigned_zero_value, undefined, "0"); - try testExpectFmtValue(&ip, unsigned_one_value, undefined, "1"); - try testExpectFmtValue(&ip, signed_zero_value, undefined, "0"); - try testExpectFmtValue(&ip, signed_one_value, undefined, "1"); + try testExpectFmtValue(ip, unsigned_zero_value, undefined, "0"); + try testExpectFmtValue(ip, unsigned_one_value, undefined, "1"); + try testExpectFmtValue(ip, signed_zero_value, undefined, "0"); + try testExpectFmtValue(ip, signed_one_value, undefined, "1"); - try testExpectFmtValue(&ip, u64_max_value, undefined, "18446744073709551615"); - try testExpectFmtValue(&ip, i64_max_value, undefined, "9223372036854775807"); - try testExpectFmtValue(&ip, i64_min_value, undefined, "-9223372036854775808"); + try testExpectFmtValue(ip, u64_max_value, undefined, "18446744073709551615"); + try testExpectFmtValue(ip, i64_max_value, undefined, "9223372036854775807"); + try testExpectFmtValue(ip, i64_min_value, undefined, "-9223372036854775808"); } test "big int value" { @@ -2450,8 +2453,8 @@ test "big int value" { const positive_big_int_value = try ip.get(gpa, .{ .int_big_value = result.toConst() }); const negative_big_int_value = try ip.get(gpa, .{ .int_big_value = result.toConst().negate() }); - try testExpectFmtValue(&ip, positive_big_int_value, undefined, "340282366920938463463374607431768211456"); - try testExpectFmtValue(&ip, negative_big_int_value, undefined, "-340282366920938463463374607431768211456"); + try testExpectFmtValue(ip, positive_big_int_value, Index.none, "340282366920938463463374607431768211456"); + try testExpectFmtValue(ip, negative_big_int_value, Index.none, "-340282366920938463463374607431768211456"); } test "float type" { @@ -2477,11 +2480,11 @@ test "float type" { try std.testing.expect(f32_type == another_f32_type); try std.testing.expect(f64_type == another_f64_type); - try testExpectFmtType(&ip, f16_type, "f16"); - try testExpectFmtType(&ip, f32_type, "f32"); - try testExpectFmtType(&ip, f64_type, "f64"); - try testExpectFmtType(&ip, f80_type, "f80"); - try testExpectFmtType(&ip, f128_type, "f128"); + try testExpectFmtType(ip, f16_type, "f16"); + try testExpectFmtType(ip, f32_type, "f32"); + try testExpectFmtType(ip, f64_type, "f64"); + try testExpectFmtType(ip, f80_type, "f80"); + try testExpectFmtType(ip, f128_type, "f128"); } test "float value" { @@ -2501,11 +2504,11 @@ test "float value" { try std.testing.expect(f64_value != f80_value); try std.testing.expect(f80_value != f128_value); - try testExpectFmtValue(&ip, f16_value, undefined, "0.25"); - try testExpectFmtValue(&ip, f32_value, undefined, "0.5"); - try testExpectFmtValue(&ip, f64_value, undefined, "1"); - try testExpectFmtValue(&ip, f80_value, undefined, "2"); - try testExpectFmtValue(&ip, f128_value, undefined, "2.75"); + try testExpectFmtValue(ip, f16_value, undefined, "0.25"); + try testExpectFmtValue(ip, f32_value, undefined, "0.5"); + try testExpectFmtValue(ip, f64_value, undefined, "1"); + try testExpectFmtValue(ip, f80_value, undefined, "2"); + try testExpectFmtValue(ip, f128_value, undefined, "2.75"); } test "pointer type" { @@ -2565,7 +2568,7 @@ test "pointer type" { .is_const = true, .is_volatile = true, } }); - const @"*addrspace(.shared) const u32" = try ip.get(gpa, .{ .pointer_type = Pointer{ + const @"*addrspace(.shared) const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true, @@ -2578,13 +2581,13 @@ test "pointer type" { try std.testing.expect(@"*align(4:2:3) u32" != @"[*c]const volatile u32"); try std.testing.expect(@"[*c]const volatile u32" != @"*addrspace(.shared) const u32"); - try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); - try testExpectFmtType(&ip, u32_pointer_type, "*u32"); - try testExpectFmtType(&ip, @"*const u32", "*const u32"); - try testExpectFmtType(&ip, @"*align(4) u32", "*align(4) u32"); - try testExpectFmtType(&ip, @"*align(4:2:3) u32", "*align(4:2:3) u32"); - try testExpectFmtType(&ip, @"[*c]const volatile u32", "[*c]const volatile u32"); - try testExpectFmtType(&ip, @"*addrspace(.shared) const u32", "*addrspace(.shared) const u32"); + try testExpectFmtType(ip, i32_pointer_type_0, "*i32"); + try testExpectFmtType(ip, u32_pointer_type, "*u32"); + try testExpectFmtType(ip, @"*const u32", "*const u32"); + try testExpectFmtType(ip, @"*align(4) u32", "*align(4) u32"); + try testExpectFmtType(ip, @"*align(4:2:3) u32", "*align(4:2:3) u32"); + try testExpectFmtType(ip, @"[*c]const volatile u32", "[*c]const volatile u32"); + try testExpectFmtType(ip, @"*addrspace(.shared) const u32", "*addrspace(.shared) const u32"); } test "optional type" { @@ -2607,11 +2610,11 @@ test "optional type" { try std.testing.expect(i32_optional_type_0 == i32_optional_type_1); try std.testing.expect(i32_optional_type_0 != u32_optional_type); - try testExpectFmtType(&ip, i32_optional_type_0, "?i32"); - try testExpectFmtType(&ip, u32_optional_type, "?u32"); + try testExpectFmtType(ip, i32_optional_type_0, "?i32"); + try testExpectFmtType(ip, u32_optional_type, "?u32"); - try testExpectFmtValue(&ip, null_value, u32_optional_type, "null"); - try testExpectFmtValue(&ip, u64_42_value, u32_optional_type, "42"); + try testExpectFmtValue(ip, null_value, u32_optional_type, "null"); + try testExpectFmtValue(ip, u64_42_value, u32_optional_type, "42"); } test "error set type" { @@ -2635,8 +2638,8 @@ test "error set type" { try std.testing.expect(empty_error_set != error_set_0); try std.testing.expect(error_set_0 == error_set_1); - try testExpectFmtType(&ip, empty_error_set, "error{}"); - try testExpectFmtType(&ip, error_set_0, "error{foo,bar,baz}"); + try testExpectFmtType(ip, empty_error_set, "error{}"); + try testExpectFmtType(ip, error_set_0, "error{foo,bar,baz}"); } test "error union type" { @@ -2644,15 +2647,16 @@ test "error union type" { var ip: InternPool = .{}; defer ip.deinit(gpa); + const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const @"error{}!bool" = try ip.get(gpa, .{ .error_union_type = ErrorUnion{ + const @"error{}!bool" = try ip.get(gpa, .{ .error_union_type = .{ .error_set_type = empty_error_set, .payload_type = bool_type, } }); - try testExpectFmtType(&ip, @"error{}!bool", "error{}!bool"); + try testExpectFmtType(ip, @"error{}!bool", "error{}!bool"); } test "array type" { @@ -2683,8 +2687,8 @@ test "array type" { try std.testing.expect(i32_3_array_type_0 == i32_3_array_type_1); try std.testing.expect(i32_3_array_type_1 != u32_0_0_array_type); - try testExpectFmtType(&ip, i32_3_array_type_0, "[3]i32"); - try testExpectFmtType(&ip, u32_0_0_array_type, "[3:0]u32"); + try testExpectFmtType(ip, i32_3_array_type_0, "[3]i32"); + try testExpectFmtType(ip, u32_0_0_array_type, "[3:0]u32"); } test "struct type" { @@ -2752,17 +2756,17 @@ test "function type" { .args = &.{param1}, } }); - const @"fn() callconv(.C) align(4) type" = try ip.get(gpa, .{ .function_type = .{ + const @"fn() align(4) callconv(.C) type" = try ip.get(gpa, .{ .function_type = .{ .calling_convention = .C, .alignment = 4, .return_type = type_type, .args = &.{}, } }); - try testExpectFmtType(&ip, @"fn(i32) bool", "fn(i32) bool"); - try testExpectFmtType(&ip, @"fn(comptime type, noalias i32) type", "fn(comptime type, noalias i32) type"); - try testExpectFmtType(&ip, @"fn(i32, ...) type", "fn(i32, ...) type"); - try testExpectFmtType(&ip, @"fn() callconv(.C) align(4) type", "fn() callconv(.C) align(4) type"); + try testExpectFmtType(ip, @"fn(i32) bool", "fn(i32) bool"); + try testExpectFmtType(ip, @"fn(comptime type, noalias i32) type", "fn(comptime type, noalias i32) type"); + try testExpectFmtType(ip, @"fn(i32, ...) type", "fn(i32, ...) type"); + try testExpectFmtType(ip, @"fn() align(4) callconv(.C) type", "fn() align(4) callconv(.C) type"); } test "anyframe type" { @@ -2774,13 +2778,13 @@ 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_type = .{ .child = i32_type } }); - const @"anyframe->bool" = try ip.get(gpa, Key{ .anyframe_type = .{ .child = bool_type } }); + const @"anyframe->i32" = try ip.get(gpa, .{ .anyframe_type = .{ .child = i32_type } }); + const @"anyframe->bool" = try ip.get(gpa, .{ .anyframe_type = .{ .child = bool_type } }); try std.testing.expect(@"anyframe->i32" != @"anyframe->bool"); - try testExpectFmtType(&ip, @"anyframe->i32", "anyframe->i32"); - try testExpectFmtType(&ip, @"anyframe->bool", "anyframe->bool"); + try testExpectFmtType(ip, @"anyframe->i32", "anyframe->i32"); + try testExpectFmtType(ip, @"anyframe->bool", "anyframe->bool"); } test "vector type" { @@ -2803,8 +2807,8 @@ test "vector type" { try std.testing.expect(@"@Vector(2,u32)" != @"@Vector(2,bool)"); - try testExpectFmtType(&ip, @"@Vector(2,u32)", "@Vector(2,u32)"); - try testExpectFmtType(&ip, @"@Vector(2,bool)", "@Vector(2,bool)"); + try testExpectFmtType(ip, @"@Vector(2,u32)", "@Vector(2,u32)"); + try testExpectFmtType(ip, @"@Vector(2,bool)", "@Vector(2,bool)"); } test "bytes value" { @@ -3003,12 +3007,12 @@ test "resolvePeerTypes pointers" { const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 1, .child = u32_type, - .sentinel = Index.none, + .sentinel = .none, } }); const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 2, .child = u32_type, - .sentinel = Index.none, + .sentinel = .none, } }); const @"*[1]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[1]u32", .size = .One } }); @@ -3068,18 +3072,18 @@ test "resolvePeerTypes pointers" { try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32"); - try testExpectFmtType(&ip, @"[*c]u32", "[*c]u32"); - try testExpectFmtType(&ip, @"*[1]u32", "*[1]u32"); - try testExpectFmtType(&ip, @"*[2]u32", "*[2]u32"); - try testExpectFmtType(&ip, @"*u32", "*u32"); - try testExpectFmtType(&ip, @"*const u32", "*const u32"); - try testExpectFmtType(&ip, @"[*]u32", "[*]u32"); - try testExpectFmtType(&ip, @"[*:0]u32", "[*:0]u32"); - try testExpectFmtType(&ip, @"[]u32", "[]u32"); - try testExpectFmtType(&ip, @"[]const u32", "[]const u32"); - try testExpectFmtType(&ip, @"[:0]u32", "[:0]u32"); - try testExpectFmtType(&ip, @"?[]u32", "?[]u32"); - try testExpectFmtType(&ip, @"?[]const u32", "?[]const u32"); + try testExpectFmtType(ip, @"[*c]u32", "[*c]u32"); + try testExpectFmtType(ip, @"*[1]u32", "*[1]u32"); + try testExpectFmtType(ip, @"*[2]u32", "*[2]u32"); + try testExpectFmtType(ip, @"*u32", "*u32"); + try testExpectFmtType(ip, @"*const u32", "*const u32"); + try testExpectFmtType(ip, @"[*]u32", "[*]u32"); + try testExpectFmtType(ip, @"[*:0]u32", "[*:0]u32"); + try testExpectFmtType(ip, @"[]u32", "[]u32"); + try testExpectFmtType(ip, @"[]const u32", "[]const u32"); + try testExpectFmtType(ip, @"[:0]u32", "[:0]u32"); + try testExpectFmtType(ip, @"?[]u32", "?[]u32"); + try testExpectFmtType(ip, @"?[]const u32", "?[]const u32"); } fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { From 2ea97a050b6600182aed11e75eb5189e00992899 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 17 Jan 2023 20:35:42 +0100 Subject: [PATCH 26/78] improve Function memory layout --- src/InternPool.zig | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 8c2bb03..ed33f73 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -80,19 +80,15 @@ pub const Enum = struct { }; pub const Function = struct { - calling_convention: std.builtin.CallingConvention = .Unspecified, + args: []const Index, + args_is_comptime: std.StaticBitSet(32) = std.StaticBitSet(32).initEmpty(), + args_is_generic: std.StaticBitSet(32) = std.StaticBitSet(32).initEmpty(), + args_is_noalias: std.StaticBitSet(32) = std.StaticBitSet(32).initEmpty(), + return_type: Index, alignment: u16 = 0, + calling_convention: std.builtin.CallingConvention = .Unspecified, is_generic: bool = false, is_var_args: bool = false, - return_type: Index, - args: []const Param, - - pub const Param = packed struct { - arg_type: Index, - is_comptime: bool = false, - is_generic: bool = false, - is_noalias: bool = false, - }; }; pub const Union = struct { @@ -572,17 +568,19 @@ pub const Key = union(enum) { .function_type => |function_info| { try writer.writeAll("fn("); - for (function_info.args) |arg, i| { + for (function_info.args) |arg_ty, i| { if (i != 0) try writer.writeAll(", "); - if (arg.is_comptime) { - try writer.writeAll("comptime "); - } - if (arg.is_noalias) { - try writer.writeAll("noalias "); + if (i < 32) { + if (function_info.args_is_comptime.isSet(i)) { + try writer.writeAll("comptime "); + } + if (function_info.args_is_noalias.isSet(i)) { + try writer.writeAll("noalias "); + } } - try printType(arg.arg_type, ip, writer); + try printType(arg_ty, ip, writer); } if (function_info.is_var_args) { @@ -2737,30 +2735,34 @@ test "function type" { const bool_type = try ip.get(gpa, .{ .simple = .bool }); const type_type = try ip.get(gpa, .{ .simple = .type }); - var param1 = Function.Param{ .arg_type = i32_type }; const @"fn(i32) bool" = try ip.get(gpa, .{ .function_type = .{ + .args = &.{i32_type}, .return_type = bool_type, - .args = &.{param1}, } }); - var param2 = Function.Param{ .is_comptime = true, .arg_type = type_type }; - var param3 = Function.Param{ .is_noalias = true, .arg_type = i32_type }; + var args_is_comptime = std.StaticBitSet(32).initEmpty(); + args_is_comptime.set(0); + var args_is_noalias = std.StaticBitSet(32).initEmpty(); + args_is_noalias.set(1); + const @"fn(comptime type, noalias i32) type" = try ip.get(gpa, .{ .function_type = .{ + .args = &.{ type_type, i32_type }, + .args_is_comptime = args_is_comptime, + .args_is_noalias = args_is_noalias, .return_type = type_type, - .args = &.{ param2, param3 }, } }); const @"fn(i32, ...) type" = try ip.get(gpa, .{ .function_type = .{ - .is_var_args = true, + .args = &.{i32_type}, .return_type = type_type, - .args = &.{param1}, + .is_var_args = true, } }); const @"fn() align(4) callconv(.C) type" = try ip.get(gpa, .{ .function_type = .{ - .calling_convention = .C, - .alignment = 4, - .return_type = type_type, .args = &.{}, + .return_type = type_type, + .alignment = 4, + .calling_convention = .C, } }); try testExpectFmtType(ip, @"fn(i32) bool", "fn(i32) bool"); From 30f919d8545b092b10998b565aa451843502b743 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 17 Jan 2023 20:36:33 +0100 Subject: [PATCH 27/78] optimize hashing and equality checks on structs --- src/InternPool.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index ed33f73..fb29bfe 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1061,9 +1061,9 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool { switch (@typeInfo(T)) { .Struct => |info| { - // if (info.layout == .Packed) { - // return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b)); - // } + if (comptime std.meta.trait.hasUniqueRepresentation(T)) { + 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; } @@ -1147,7 +1147,7 @@ fn deepHash(hasher: anytype, key: anytype) void { }, .Struct => |info| { - if (info.layout == .Packed) { + if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) { hasher.update(std.mem.asBytes(&key)); } else { inline for (info.fields) |field| { From 985cfb6d065082957490fb7cf41b7e04a1452c9e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 17 Jan 2023 20:37:34 +0100 Subject: [PATCH 28/78] implement union values --- src/InternPool.zig | 152 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 25 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index fb29bfe..0d6b587 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -75,7 +75,7 @@ pub const Enum = struct { pub const Field = struct { name: []const u8, - ty: Index, + val: Index, }; }; @@ -126,7 +126,7 @@ pub const Bytes = []const u8; pub const Aggregate = []const Index; pub const UnionValue = packed struct { - tag: Index, + field_index: u32, val: Index, }; @@ -651,7 +651,6 @@ pub const Key = union(enum) { options: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - comptime assert(fmt.len == 0); _ = options; if (fmt.len != 0) std.fmt.invalidFmtError(fmt, Key); return printValue(ctx.value, ctx.ty, ctx.ip, writer); @@ -747,11 +746,11 @@ pub const Key = union(enum) { .union_value => |union_value| { const union_info = ip.indexToKey(ty).union_type; - try writer.writeAll(".{ "); - try printValue(ip.indexToKey(union_info.tag_type), union_value.tag, ip, writer); - try writer.writeAll(" = "); - try printValue(union_value.val, @panic("TODO"), ip, writer); - try writer.writeAll(" }"); + try writer.print(".{{ .{} = {} }}", .{ + std.zig.fmtId(union_info.fields[union_value.field_index].name), + // union_value.tag.fmtValue(union_info.tag_type, ip), + union_value.val.fmtValue(union_info.fields[union_value.field_index].ty, ip), + }); }, } } @@ -2703,28 +2702,64 @@ test "struct type" { const field2 = Struct.Field{ .name = "bar", .ty = i32_type }; const field3 = Struct.Field{ .name = "baz", .ty = bool_type }; - const struct_type_0 = try ip.get(gpa, Key{ - .struct_type = Struct{ - .fields = &.{ field1, field2, field3 }, - .namespace = .none, - .layout = .Auto, - .backing_int_ty = .none, - }, - }); + const struct_type_0 = try ip.get(gpa, .{ .struct_type = .{ + .fields = &.{ field1, field2, field3 }, + .namespace = .none, + .layout = .Auto, + .backing_int_ty = .none, + } }); - _ = try ip.get(gpa, .{ .simple = .unreachable_value }); + const struct_type_1 = 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, Key{ - .struct_type = Struct{ - .fields = &.{ field1, field2, field3 }, - .namespace = .none, - .layout = .Auto, - .backing_int_ty = .none, - }, - }); try std.testing.expect(struct_type_0 == struct_type_1); } +test "enum type" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + 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", .val = .none }; + const field2 = Enum.Field{ .name = "cpp", .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; @@ -2771,6 +2806,73 @@ 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 }); + + var field1 = Union.Field{ .name = "Ok", .ty = u32_type, .alignment = 0 }; + var field2 = Union.Field{ .name = "Err", .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; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + const f16_type = try ip.get(gpa, .{ .simple = .f16 }); + + const int_value = try ip.get(gpa, .{ .int_u64_value = 1 }); + const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 }); + + var field1 = Union.Field{ .name = "int", .ty = u32_type, .alignment = 0 }; + var field2 = Union.Field{ .name = "float", .ty = f16_type, .alignment = 0 }; + + const union_type = try ip.get(gpa, .{ + .union_type = .{ + .tag_type = .none, + .fields = &.{ field1, field2 }, + .namespace = .none, + }, + }); + + const union_value1 = try ip.get(gpa, .{ .union_value = .{ + .field_index = 0, + .val = int_value, + } }); + const union_value2 = try ip.get(gpa, .{ .union_value = .{ + .field_index = 1, + .val = f16_value, + } }); + + try testExpectFmtValue(ip, union_value1, union_type, ".{ .int = 1 }"); + try testExpectFmtValue(ip, union_value2, union_type, ".{ .float = 0.25 }"); +} + test "anyframe type" { const gpa = std.testing.allocator; From 0c24f8e2a9d53f145add34b0c1e2d1cdac3f4e72 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 17 Jan 2023 20:49:11 +0100 Subject: [PATCH 29/78] update pointer type tests --- src/InternPool.zig | 115 +++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 67 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 0d6b587..0437b18 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2514,43 +2514,24 @@ test "pointer type" { var ip: InternPool = .{}; defer ip.deinit(gpa); - const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - try std.testing.expect(i32_type_0 == i32_type_1); - try std.testing.expect(i32_type_0 != u32_type); + const zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); - const i32_pointer_type_0 = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = i32_type_0, + const @"*i32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = i32_type, .size = .One, } }); - const i32_pointer_type_1 = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = i32_type_0, - .size = .One, - } }); - const i32_pointer_type_2 = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = i32_type_1, - .size = .One, - } }); - const u32_pointer_type = try ip.get(gpa, .{ .pointer_type = .{ + const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, } }); - - try std.testing.expect(i32_pointer_type_0 == i32_pointer_type_1); - try std.testing.expect(i32_pointer_type_1 == i32_pointer_type_2); - try std.testing.expect(i32_pointer_type_0 != u32_pointer_type); - - const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ + const @"*const volatile u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true, - } }); - const @"*align(4) u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .One, - .alignment = 4, + .is_volatile = true, } }); const @"*align(4:2:3) u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, @@ -2559,12 +2540,6 @@ test "pointer type" { .bit_offset = 2, .host_size = 3, } }); - const @"[*c]const volatile u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .C, - .is_const = true, - .is_volatile = true, - } }); const @"*addrspace(.shared) const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, @@ -2572,19 +2547,50 @@ test "pointer type" { .address_space = .shared, } }); - try std.testing.expect(u32_pointer_type != @"*const u32"); - try std.testing.expect(@"*const u32" != @"*align(4) u32"); - try std.testing.expect(@"*align(4) u32" != @"*align(4:2:3) u32"); - try std.testing.expect(@"*align(4:2:3) u32" != @"[*c]const volatile u32"); - try std.testing.expect(@"[*c]const volatile u32" != @"*addrspace(.shared) const u32"); + const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Many, + } }); + const @"[*:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Many, + .sentinel = zero_value, + } }); + const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Slice, + } }); + const @"[:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .Slice, + .sentinel = zero_value, + } }); + const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ + .elem_type = u32_type, + .size = .C, + } }); - try testExpectFmtType(ip, i32_pointer_type_0, "*i32"); - try testExpectFmtType(ip, u32_pointer_type, "*u32"); - try testExpectFmtType(ip, @"*const u32", "*const u32"); - try testExpectFmtType(ip, @"*align(4) u32", "*align(4) u32"); + try std.testing.expect(@"*i32" != @"*u32"); + try std.testing.expect(@"*u32" != @"*const volatile u32"); + try std.testing.expect(@"*const volatile u32" != @"*align(4:2:3) u32"); + try std.testing.expect(@"*align(4:2:3) u32" != @"*addrspace(.shared) const u32"); + + try std.testing.expect(@"[*]u32" != @"[*:0]u32"); + try std.testing.expect(@"[*:0]u32" != @"[]u32"); + try std.testing.expect(@"[*:0]u32" != @"[:0]u32"); + try std.testing.expect(@"[:0]u32" != @"[*c]u32"); + + try testExpectFmtType(ip, @"*i32", "*i32"); + try testExpectFmtType(ip, @"*u32", "*u32"); + try testExpectFmtType(ip, @"*const volatile u32", "*const volatile u32"); try testExpectFmtType(ip, @"*align(4:2:3) u32", "*align(4:2:3) u32"); - try testExpectFmtType(ip, @"[*c]const volatile u32", "[*c]const volatile u32"); try testExpectFmtType(ip, @"*addrspace(.shared) const u32", "*addrspace(.shared) const u32"); + + try testExpectFmtType(ip, @"[*]u32", "[*c]u32"); + try testExpectFmtType(ip, @"[*:0]u32", "[*c]u32"); + try testExpectFmtType(ip, @"[]u32", "[*c]u32"); + try testExpectFmtType(ip, @"[:0]u32", "[*c]u32"); + try testExpectFmtType(ip, @"[*c]u32", "[*c]u32"); } test "optional type" { @@ -3104,8 +3110,6 @@ test "resolvePeerTypes pointers" { const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); - const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C } }); const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ @@ -3134,11 +3138,6 @@ test "resolvePeerTypes pointers" { .elem_type = u32_type, .size = .Many, } }); - const @"[*:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .Many, - .sentinel = zero_value, - } }); const @"?[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]u32" } }); const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ @@ -3150,11 +3149,6 @@ test "resolvePeerTypes pointers" { .size = .Slice, .is_const = true, } }); - const @"[:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .Slice, - .sentinel = zero_value, - } }); const @"?[]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]u32" } }); const @"?[]const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]const u32" } }); @@ -3175,19 +3169,6 @@ test "resolvePeerTypes pointers" { try ip.testResolvePeerTypes(@"*u32", @"*const u32", @"*const u32"); try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32"); - - try testExpectFmtType(ip, @"[*c]u32", "[*c]u32"); - try testExpectFmtType(ip, @"*[1]u32", "*[1]u32"); - try testExpectFmtType(ip, @"*[2]u32", "*[2]u32"); - try testExpectFmtType(ip, @"*u32", "*u32"); - try testExpectFmtType(ip, @"*const u32", "*const u32"); - try testExpectFmtType(ip, @"[*]u32", "[*]u32"); - try testExpectFmtType(ip, @"[*:0]u32", "[*:0]u32"); - try testExpectFmtType(ip, @"[]u32", "[]u32"); - try testExpectFmtType(ip, @"[]const u32", "[]const u32"); - try testExpectFmtType(ip, @"[:0]u32", "[:0]u32"); - try testExpectFmtType(ip, @"?[]u32", "?[]u32"); - try testExpectFmtType(ip, @"?[]const u32", "?[]const u32"); } fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { From 1e3d9579ca92d7c98dc1642228c776db1ee0cefd Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:15:52 +0100 Subject: [PATCH 30/78] improve peer type resolution test failure message --- src/InternPool.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 0437b18..0d6fae5 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -446,6 +446,10 @@ pub const Key = union(enum) { } fn printType(ty: Index, ip: InternPool, writer: anytype) @TypeOf(writer).Error!void { + if (builtin.is_test and ty == .none) { + try writer.writeAll(@tagName(Index.none)); + return; + } try printTypeKey(ip.indexToKey(ty), ip, writer); } @@ -3178,5 +3182,8 @@ fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !v fn testResolvePeerTypesInOrder(ip: *InternPool, lhs: Index, rhs: Index, expected: Index) !void { const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{ lhs, rhs }, builtin.target); - try std.testing.expectEqual(expected, actual); + if (expected != actual) { + std.debug.print("expected {}, found {}\n", .{ expected.fmtType(ip.*), actual.fmtType(ip.*) }); + return error.TestExpectedEqual; + } } From ea608a47eabcd91a3a668a837c06f76646ecac78 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:16:19 +0100 Subject: [PATCH 31/78] peer type resolution improvements --- src/InternPool.zig | 103 +++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 54 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 0d6fae5..fdd94b2 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1195,11 +1195,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t var arena = arena_allocator.allocator(); var chosen = types[0]; - // // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(). - // // * ErrorSet: this is an override - // // * ErrorUnion: this is an override of the error set only - // // * other: at the end we make an ErrorUnion with the other thing and this - // var err_set_ty: Index = Index.none; + // If this is non-null then it does the following thing, depending on the chosen zigTypeTag(). + // * ErrorSet: this is an override + // * ErrorUnion: this is an override of the error set only + // * other: at the end we make an ErrorUnion with the other thing and this + var err_set_ty: Index = Index.none; var any_are_null = false; var seen_const = false; var convert_to_slice = false; @@ -1610,12 +1610,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info }); const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty; - return opt_ptr_ty; - // const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ptr_ty; - // return try ip.get(gpa, .{ .error_union_type = .{ - // .error_set_type = set_ty, - // .payload_type = opt_ptr_ty, - // } }); + const set_ty = if (err_set_ty != .none) err_set_ty else return opt_ptr_ty; + return try ip.get(gpa, .{ .error_union_type = .{ + .error_set_type = set_ty, + .payload_type = opt_ptr_ty, + } }); } if (seen_const) { @@ -1627,8 +1626,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info }); const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty; - // const set_ty = if(err_set_ty != .none) err_set_ty else error_union_info.error_set_type; - const set_ty = error_union_info.error_set_type; + const set_ty = if (err_set_ty != .none) err_set_ty else error_union_info.error_set_type; return try ip.get(gpa, .{ .error_union_type = .{ .error_set_type = set_ty, .payload_type = opt_ptr_ty, @@ -1640,12 +1638,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t const new_ptr_ty = try ip.get(gpa, .{ .pointer_type = info }); const opt_ptr_ty = if (any_are_null) try ip.get(gpa, .{ .optional_type = .{ .payload_type = new_ptr_ty } }) else new_ptr_ty; - return opt_ptr_ty; - // const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ptr_ty; - // return try ip.get(gpa, .{ .error_union_type = .{ - // .error_set_type = set_ty, - // .payload_type = opt_ptr_ty, - // } }); + const set_ty = if (err_set_ty != .none) err_set_ty else return opt_ptr_ty; + return try ip.get(gpa, .{ .error_union_type = .{ + .error_set_type = set_ty, + .payload_type = opt_ptr_ty, + } }); }, else => return chosen, } @@ -1660,12 +1657,11 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .optional_type => chosen, else => try ip.get(gpa, .{ .optional_type = .{ .payload_type = chosen } }), }; - return opt_ty; - // const set_ty = if(err_set_ty != .none) err_set_ty else return opt_ty; - // return try ip.get(gpa, .{ .error_union_type = .{ - // .error_set_type = set_ty, - // .payload_type = opt_ty, - // } }); + const set_ty = if (err_set_ty != .none) err_set_ty else return opt_ty; + return try ip.get(gpa, .{ .error_union_type = .{ + .error_set_type = set_ty, + .payload_type = opt_ty, + } }); } return chosen; @@ -2155,36 +2151,35 @@ fn coerceInMemoryAllowedFns( } }; } - // TODO + if (!dest_info.args_is_noalias.eql(src_info.args_is_noalias)) { + return InMemoryCoercionResult{ .fn_param_noalias = .{ + .actual = src_info.args_is_noalias.mask, + .wanted = dest_info.args_is_noalias.mask, + } }; + } - // if (dest_info.noalias_bits != src_info.noalias_bits) { - // return InMemoryCoercionResult{ .fn_param_noalias = .{ - // .actual = src_info.noalias_bits, - // .wanted = dest_info.noalias_bits, - // } }; - // } + for (dest_info.args) |dest_param_ty, i| { + const src_param_ty = src_info.args[i]; - // for (dest_info.param_types) |dest_param_ty, i| { - // const src_param_ty = src_info.param_types[i]; + // TODO move outside of loop + if (i < 32 and dest_info.args_is_comptime.isSet(i) != src_info.args_is_comptime.isSet(i)) { + return InMemoryCoercionResult{ .fn_param_comptime = .{ + .index = i, + .wanted = dest_info.args_is_comptime.isSet(i), + } }; + } - // if (dest_info.comptime_params[i] != src_info.comptime_params[i]) { - // return InMemoryCoercionResult{ .fn_param_comptime = .{ - // .index = i, - // .wanted = dest_info.comptime_params[i], - // } }; - // } - - // // Note: Cast direction is reversed here. - // const param = try ip.coerceInMemoryAllowed(gpa, src_param_ty, dest_param_ty, true, target); - // if (param != .ok) { - // return InMemoryCoercionResult{ .fn_param = .{ - // .child = try param.dupe(arena), - // .actual = src_param_ty, - // .wanted = dest_param_ty, - // .index = i, - // } }; - // } - // } + // Note: Cast direction is reversed here. + const param = try ip.coerceInMemoryAllowed(gpa, arena, src_param_ty, dest_param_ty, true, target); + if (param != .ok) { + return InMemoryCoercionResult{ .fn_param = .{ + .child = try param.dupe(arena), + .actual = src_param_ty, + .wanted = dest_param_ty, + .index = i, + } }; + } + } return .ok; } @@ -2266,8 +2261,8 @@ fn coerceInMemoryAllowedPtrs( const ok_sent = dest_info.sentinel == .none or src_info.size == .C or dest_info.sentinel == src_info.sentinel; // is this enough for a value equality check? if (!ok_sent) { return InMemoryCoercionResult{ .ptr_sentinel = .{ - .actual = if (src_info.sentinel != .none) src_info.sentinel else try ip.get(gpa, .{ .simple = .unreachable_value }), - .wanted = if (dest_info.sentinel != .none) dest_info.sentinel else try ip.get(gpa, .{ .simple = .unreachable_value }), + .actual = src_info.sentinel, + .wanted = dest_info.sentinel, .ty = dest_info.elem_type, } }; } From eca9fc2f20b81edff436ccdc7a0082e57523d1db Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:49:46 +0100 Subject: [PATCH 32/78] add expectEqualTypes --- src/InternPool.zig | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index fdd94b2..6531135 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -446,10 +446,6 @@ pub const Key = union(enum) { } fn printType(ty: Index, ip: InternPool, writer: anytype) @TypeOf(writer).Error!void { - if (builtin.is_test and ty == .none) { - try writer.writeAll(@tagName(Index.none)); - return; - } try printTypeKey(ip.indexToKey(ty), ip, writer); } @@ -3177,8 +3173,18 @@ fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !v fn testResolvePeerTypesInOrder(ip: *InternPool, lhs: Index, rhs: Index, expected: Index) !void { const actual = try resolvePeerTypes(ip, std.testing.allocator, &.{ lhs, rhs }, builtin.target); - if (expected != actual) { - std.debug.print("expected {}, found {}\n", .{ expected.fmtType(ip.*), actual.fmtType(ip.*) }); - return error.TestExpectedEqual; - } + try expectEqualTypes(ip, expected, actual); +} + +fn expectEqualTypes(ip: *InternPool, expected: Index, actual: Index) !void { + if (expected == actual) return; + const allocator = std.testing.allocator; + + const expected_type = if (expected == .none) @tagName(Index.none) else try std.fmt.allocPrint(allocator, "{}", .{expected.fmtType(ip.*)}); + defer if (expected != .none) allocator.free(expected_type); + const actual_type = if (actual == .none) @tagName(Index.none) else try std.fmt.allocPrint(allocator, "{}", .{actual.fmtType(ip.*)}); + defer if (actual != .none) allocator.free(actual_type); + + std.debug.print("expected `{s}`, found `{s}`\n", .{ expected_type, actual_type }); + return error.TestExpectedEqual; } From 5c20650143d77f24b3f7a8869ffe722d2974d15a Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:50:19 +0100 Subject: [PATCH 33/78] refactor pointer peer type tests --- src/InternPool.zig | 118 +++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 46 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 6531135..d22a83d 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2581,10 +2581,10 @@ test "pointer type" { try testExpectFmtType(ip, @"*align(4:2:3) u32", "*align(4:2:3) u32"); try testExpectFmtType(ip, @"*addrspace(.shared) const u32", "*addrspace(.shared) const u32"); - try testExpectFmtType(ip, @"[*]u32", "[*c]u32"); - try testExpectFmtType(ip, @"[*:0]u32", "[*c]u32"); - try testExpectFmtType(ip, @"[]u32", "[*c]u32"); - try testExpectFmtType(ip, @"[:0]u32", "[*c]u32"); + try testExpectFmtType(ip, @"[*]u32", "[*]u32"); + try testExpectFmtType(ip, @"[*:0]u32", "[*:0]u32"); + try testExpectFmtType(ip, @"[]u32", "[]u32"); + try testExpectFmtType(ip, @"[:0]u32", "[:0]u32"); try testExpectFmtType(ip, @"[*c]u32", "[*c]u32"); } @@ -3105,65 +3105,91 @@ test "resolvePeerTypes pointers" { const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); + const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many } }); + const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice } }); const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C } }); - const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ - .len = 1, - .child = u32_type, - .sentinel = .none, - } }); - const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{ - .len = 2, - .child = u32_type, - .sentinel = .none, - } }); + const @"?*u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*u32" } }); + const @"?[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]u32" } }); + const @"?[]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]u32" } }); + + const @"**u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"*u32", .size = .One } }); + const @"*[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[*]u32", .size = .One } }); + const @"*[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[]u32", .size = .One } }); + const @"*[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[*c]u32", .size = .One } }); + + const @"?*[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[*]u32" } }); + const @"?*[]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[]u32" } }); + + const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 1, .child = u32_type, .sentinel = .none } }); + const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 2, .child = u32_type, .sentinel = .none } }); const @"*[1]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[1]u32", .size = .One } }); const @"*[2]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[2]u32", .size = .One } }); - const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); + const @"?*[1]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[1]u32" } }); + const @"?*[2]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[2]u32" } }); - const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .One, - .is_const = true, - } }); + const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true } }); + const @"[*]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many, .is_const = true } }); + const @"[]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice, .is_const = true } }); + const @"[*c]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C, .is_const = true } }); - const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .Many, - } }); - const @"?[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]u32" } }); - - const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .Slice, - } }); - const @"[]const u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, - .size = .Slice, - .is_const = true, - } }); - - const @"?[]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]u32" } }); + const @"?*const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*const u32" } }); + const @"?[*]const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]const u32" } }); const @"?[]const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[]const u32" } }); + _ = @"**u32"; + _ = @"*[*c]u32"; + _ = @"?*[]u32"; + _ = @"?*[2]u32"; + _ = @"?[*]const u32"; + _ = @"?[]const u32"; + + // gain const + try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); + try ip.testResolvePeerTypes(@"*u32", @"*const u32", @"*const u32"); + try ip.testResolvePeerTypes(@"[*]u32", @"[*]const u32", @"[*]const u32"); + try ip.testResolvePeerTypes(@"[]u32", @"[]const u32", @"[]const u32"); + try ip.testResolvePeerTypes(@"[*c]u32", @"[*c]const u32", @"[*c]const u32"); + + // array to slice + try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32"); + try ip.testResolvePeerTypes(@"[]u32", @"*[1]u32", @"[]u32"); + + // pointer like optionals + try ip.testResolvePeerTypes(@"*u32", @"?*u32", @"?*u32"); + try ip.testResolvePeerTypesInOrder(@"*u32", @"?[*]u32", @"?[*]u32"); + try ip.testResolvePeerTypesInOrder(@"[*]u32", @"?*u32", @"?*u32"); + try ip.testResolvePeerTypes(@"[*c]u32", comptime_int_type, @"[*c]u32"); try ip.testResolvePeerTypes(@"[*c]u32", u32_type, @"[*c]u32"); try ip.testResolvePeerTypes(@"[*c]u32", comptime_float_type, Index.none); try ip.testResolvePeerTypes(@"[*c]u32", bool_type, Index.none); - try ip.testResolvePeerTypes(@"[*]u32", @"*[2]u32", @"[*]u32"); - try ip.testResolvePeerTypes(@"[]u32", @"*[2]u32", @"[]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", @"*u32", @"[*c]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", @"[*]u32", @"[*c]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", @"[]u32", @"[*c]u32"); + + try ip.testResolvePeerTypes(@"[*c]u32", @"*[1]u32", Index.none); + try ip.testResolvePeerTypesInOrder(@"[*c]u32", @"?*[1]u32", @"?*[1]u32"); + try ip.testResolvePeerTypesInOrder(@"?*[1]u32", @"[*c]u32", Index.none); + try ip.testResolvePeerTypes(@"[*c]u32", @"*[*]u32", Index.none); + try ip.testResolvePeerTypesInOrder(@"[*c]u32", @"?*[*]u32", @"?*[*]u32"); + try ip.testResolvePeerTypesInOrder(@"?*[*]u32", @"[*c]u32", Index.none); + try ip.testResolvePeerTypes(@"[*c]u32", @"[]u32", @"[*c]u32"); + // TODO try ip.testResolvePeerTypesInOrder(@"[*c]u32", @"?[]u32", @"?[]u32"); + // TODO try ip.testResolvePeerTypesInOrder(@"?[]u32", @"[*c]u32", Index.none); + + // TODO try ip.testResolvePeerTypesInOrder(@"*u32", @"?[*]const u32", @"?[*]const u32"); + try ip.testResolvePeerTypesInOrder(@"*const u32", @"?[*]u32", @"?[*]u32"); + try ip.testResolvePeerTypesInOrder(@"[*]const u32", @"?*u32", @"?*u32"); + try ip.testResolvePeerTypesInOrder(@"[*]u32", @"?*const u32", @"?*const u32"); + try ip.testResolvePeerTypes(@"?[*]u32", @"*[2]u32", @"?[*]u32"); try ip.testResolvePeerTypes(@"?[]u32", @"*[2]u32", @"?[]u32"); - try ip.testResolvePeerTypes(@"?[]u32", @"?[]const u32", @"?[]const u32"); - - try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); - try ip.testResolvePeerTypes(@"*u32", @"*u32", @"*u32"); - try ip.testResolvePeerTypes(@"*u32", @"*const u32", @"*const u32"); - - try ip.testResolvePeerTypes(@"*[1]u32", @"*[2]u32", @"[]u32"); + try ip.testResolvePeerTypes(@"[*]u32", @"*[2]u32", @"[*]u32"); } fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { From 666e3b0e7cb8b6cdb0c13191c623012a244885d4 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Thu, 19 Jan 2023 21:08:04 +0100 Subject: [PATCH 34/78] add function pointer peer type tests --- src/InternPool.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/InternPool.zig b/src/InternPool.zig index d22a83d..be8ccae 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -3026,6 +3026,9 @@ test "resolvePeerTypes integers and floats" { try ip.testResolvePeerTypes(u16_type, usize_type, usize_type); try ip.testResolvePeerTypes(u16_type, isize_type, isize_type); + try ip.testResolvePeerTypes(c_short_type, usize_type, usize_type); + try ip.testResolvePeerTypes(c_short_type, isize_type, isize_type); + try ip.testResolvePeerTypes(i16_type, c_long_type, c_long_type); try ip.testResolvePeerTypes(i16_type, c_long_type, c_long_type); try ip.testResolvePeerTypes(u16_type, c_long_type, c_long_type); @@ -3192,6 +3195,31 @@ test "resolvePeerTypes pointers" { try ip.testResolvePeerTypes(@"[*]u32", @"*[2]u32", @"[*]u32"); } +test "resolvePeerTypes function pointers" { + const gpa = std.testing.allocator; + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const void_type = try ip.get(gpa, .{ .simple = .void }); + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); + const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true } }); + + const @"fn(*u32) void" = try ip.get(gpa, .{ .function_type = .{ + .args = &.{@"*u32"}, + .return_type = void_type, + } }); + + const @"fn(*const u32) void" = try ip.get(gpa, .{ .function_type = .{ + .args = &.{@"*const u32"}, + .return_type = void_type, + } }); + + try ip.testResolvePeerTypes(@"fn(*u32) void", @"fn(*u32) void", @"fn(*u32) void"); + try ip.testResolvePeerTypes(@"fn(*u32) void", @"fn(*const u32) void", @"fn(*u32) void"); +} + fn testResolvePeerTypes(ip: *InternPool, a: Index, b: Index, expected: Index) !void { try ip.testResolvePeerTypesInOrder(a, b, expected); try ip.testResolvePeerTypesInOrder(b, a, expected); From bdf143eaa6ac9da6da68605a8b0e5acf79f071f6 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:08:08 +0100 Subject: [PATCH 35/78] implement onePossibleValue --- src/InternPool.zig | 121 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index be8ccae..ee10475 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -414,6 +414,121 @@ pub const Key = union(enum) { }; } + pub fn onePossibleValue(ty: Key, ip: InternPool) ?Key { + switch (ty) { + .simple => |simple| switch (simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .anyopaque, + .bool, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .@"anyframe", + .enum_literal_type, + => return null, + + .void => return Key{ .simple = .void_value }, + .noreturn => return Key{ .simple = .unreachable_value }, + .null_type => return Key{ .simple = .null_value }, + .undefined_type => return Key{ .simple = .undefined_value }, + + // values + .undefined_value, + .void_value, + .unreachable_value, + .null_value, + .bool_true, + .bool_false, + => unreachable, + }, + .int_type => |int_info| { + if (int_info.bits == 0) { + switch (int_info.signedness) { + .unsigned => return Key{ .int_u64_value = 0 }, + .signed => return Key{ .int_i64_value = 0 }, + } + } + return null; + }, + .pointer_type => return null, + .array_type => |array_info| { + if (array_info.len == 0) { + @panic("TODO return empty array value"); + } else if (ip.indexToKey(array_info.child).onePossibleValue(ip)) |value| { + return value; + } + 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; + return null; + } + @panic("TODO return empty struct value"); + }, + .optional_type => |optional_info| { + const child = ip.indexToKey(optional_info.payload_type); + if (child == .simple and child.simple == .noreturn) { + return Key{ .simple = .null_value }; + } + return null; + }, + .error_union_type => return null, + .error_set_type => return null, + .enum_type => |enum_info| { + switch (enum_info.fields.len) { + 0 => return Key{ .simple = .unreachable_value }, + 1 => return ip.indexToKey(enum_info.fields[0].val), + else => return null, + } + }, + .function_type => return null, + .union_type => @panic("TODO"), + .tuple_type => @panic("TODO"), + .vector_type => |vector_info| { + if (vector_info.len == 0) { + @panic("TODO return empty array value"); + } else if (ip.indexToKey(vector_info.child).onePossibleValue(ip)) |value| { + return value; + } + return null; + }, + .anyframe_type => return null, + + .int_u64_value, + .int_i64_value, + .int_big_value, + .float_16_value, + .float_32_value, + .float_64_value, + .float_80_value, + .float_128_value, + => unreachable, + + .bytes, + .aggregate, + .union_value, + => unreachable, + } + } + pub const TypeFormatContext = struct { ty: Key, options: FormatOptions = .{}, @@ -2314,10 +2429,8 @@ fn optionalPtrTy( .Many, .One => { if (child_ptr_key.is_allowzero) return Index.none; - // TODO optionals of zero sized types behave like bools, not pointers - // if ((try sema.typeHasOnePossibleValue(child_type)) != null) { - // return null; - // } + // optionals of zero sized types behave like bools, not pointers + if (child_key.onePossibleValue(ip) != null) return Index.none; return child_type; }, From 3d95b203f26662a95100017ed874fa84f3ed0ec9 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:08:52 +0100 Subject: [PATCH 36/78] coerce in memory improvements --- src/InternPool.zig | 69 +++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index ee10475..9378f3e 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1889,14 +1889,12 @@ const InMemoryCoercionResult = union(enum) { } }; -/// If pointers have the same representation in runtime memory -/// * `const` attribute can be gained -/// * `volatile` attribute can be gained -/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if dest_is_const -/// * alignment can be decreased -/// * bit offset attributes must match exactly -/// * `*`/`[*]` must match exactly, but `[*c]` matches either one -/// * sentinel-terminated pointers can coerce into `[*]` +/// If types have the same representation in runtime memory +/// * int/float: same number of bits +/// * pointer: see `coerceInMemoryAllowedPtrs` +/// * error union: coerceable error set and payload +/// * error set: sub-set to super-set +/// * array: same shape and coerceable child fn coerceInMemoryAllowed( ip: *InternPool, gpa: Allocator, @@ -1928,12 +1926,19 @@ fn coerceInMemoryAllowed( if (dest_info.signedness == src_info.signedness and dest_info.bits == src_info.bits) return .ok; - return InMemoryCoercionResult{ .int_not_coercible = .{ - .actual_signedness = src_info.signedness, - .wanted_signedness = dest_info.signedness, - .actual_bits = src_info.bits, - .wanted_bits = dest_info.bits, - } }; + if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or + // small enough unsigned ints can get casted to large enough signed ints + (dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or + (dest_info.signedness == .unsigned and src_info.signedness == .signed)) + { + return InMemoryCoercionResult{ .int_not_coercible = .{ + .actual_signedness = src_info.signedness, + .wanted_signedness = dest_info.signedness, + .actual_bits = src_info.bits, + .wanted_bits = dest_info.bits, + } }; + } + return .ok; }, .Float => { const dest_bits = dest_key.floatBits(target); @@ -2225,7 +2230,7 @@ fn coerceInMemoryAllowedFns( dest_info: Function, src_info: Function, target: std.Target, -) !InMemoryCoercionResult { +) error{OutOfMemory}!InMemoryCoercionResult { if (dest_info.is_var_args != src_info.is_var_args) { return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; } @@ -2269,24 +2274,24 @@ fn coerceInMemoryAllowedFns( } }; } - for (dest_info.args) |dest_param_ty, i| { - const src_param_ty = src_info.args[i]; + if (!dest_info.args_is_comptime.eql(src_info.args_is_comptime)) { + const index = dest_info.args_is_comptime.xorWith(src_info.args_is_comptime).findFirstSet().?; + return InMemoryCoercionResult{ .fn_param_comptime = .{ + .index = index, + .wanted = dest_info.args_is_comptime.isSet(index), + } }; + } - // TODO move outside of loop - if (i < 32 and dest_info.args_is_comptime.isSet(i) != src_info.args_is_comptime.isSet(i)) { - return InMemoryCoercionResult{ .fn_param_comptime = .{ - .index = i, - .wanted = dest_info.args_is_comptime.isSet(i), - } }; - } + for (dest_info.args) |dest_arg_ty, i| { + const src_arg_ty = src_info.args[i]; // Note: Cast direction is reversed here. - const param = try ip.coerceInMemoryAllowed(gpa, arena, src_param_ty, dest_param_ty, true, target); + const param = try ip.coerceInMemoryAllowed(gpa, arena, src_arg_ty, dest_arg_ty, true, target); if (param != .ok) { return InMemoryCoercionResult{ .fn_param = .{ .child = try param.dupe(arena), - .actual = src_param_ty, - .wanted = dest_param_ty, + .actual = src_arg_ty, + .wanted = dest_arg_ty, .index = i, } }; } @@ -2295,6 +2300,14 @@ fn coerceInMemoryAllowedFns( return .ok; } +/// If pointers have the same representation in runtime memory +/// * `const` attribute can be gained +/// * `volatile` attribute can be gained +/// * `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) but only if dest_is_const +/// * alignment can be decreased +/// * bit offset attributes must match exactly +/// * `*`/`[*]` must match exactly, but `[*c]` matches either one +/// * sentinel-terminated pointers can coerce into `[*]` fn coerceInMemoryAllowedPtrs( ip: *InternPool, gpa: Allocator, @@ -2305,7 +2318,7 @@ fn coerceInMemoryAllowedPtrs( src_ptr_info: Key, dest_is_const: bool, target: std.Target, -) !InMemoryCoercionResult { +) error{OutOfMemory}!InMemoryCoercionResult { const dest_info = dest_ptr_info.pointer_type; const src_info = src_ptr_info.pointer_type; From a145dbb6163474c1e99c14cbda699b637f7cd42a Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:26:00 +0100 Subject: [PATCH 37/78] remove panics from release builds --- src/InternPool.zig | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index 9378f3e..550130d 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -315,7 +315,7 @@ pub const Key = union(enum) { else => unreachable, }, .int_type => |int_info| return int_info, - .enum_type => @panic("TODO"), + .enum_type => return panicOrElse("TODO", .{ .signedness = .unsigned, .bits = 0 }), .struct_type => |struct_info| { assert(struct_info.layout == .Packed); key = ip.indexToKey(struct_info.backing_int_ty); @@ -383,7 +383,7 @@ pub const Key = union(enum) { pub fn elemType2(ty: Key) Index { return switch (ty) { .simple => |simple| switch (simple) { - .@"anyframe" => @panic("TODO: return void type"), + .@"anyframe" => panicOrElse("TODO: return void type", Index.none), else => unreachable, }, .pointer_type => |pointer_info| pointer_info.elem_type, @@ -469,7 +469,7 @@ pub const Key = union(enum) { .pointer_type => return null, .array_type => |array_info| { if (array_info.len == 0) { - @panic("TODO return empty array value"); + return panicOrElse("TODO return empty array value", null); } else if (ip.indexToKey(array_info.child).onePossibleValue(ip)) |value| { return value; } @@ -481,7 +481,7 @@ pub const Key = union(enum) { if (ip.indexToKey(field.ty).onePossibleValue(ip) != null) continue; return null; } - @panic("TODO return empty struct value"); + return panicOrElse("TODO return empty struct value", null); }, .optional_type => |optional_info| { const child = ip.indexToKey(optional_info.payload_type); @@ -500,11 +500,11 @@ pub const Key = union(enum) { } }, .function_type => return null, - .union_type => @panic("TODO"), - .tuple_type => @panic("TODO"), + .union_type => return panicOrElse("TODO", null), + .tuple_type => return panicOrElse("TODO", null), .vector_type => |vector_info| { if (vector_info.len == 0) { - @panic("TODO return empty array value"); + return panicOrElse("TODO return empty array value", null); } else if (ip.indexToKey(vector_info.child).onePossibleValue(ip)) |value| { return value; } @@ -660,7 +660,7 @@ pub const Key = union(enum) { return array_info.child; }, - .struct_type => @panic("TODO"), + .struct_type => panicOrElse("TODO", null), .optional_type => |optional_info| { try writer.writeByte('?'); return optional_info.payload_type; @@ -679,7 +679,7 @@ pub const Key = union(enum) { } try writer.writeByte('}'); }, - .enum_type => @panic("TODO"), + .enum_type => panicOrElse("TODO", null), .function_type => |function_info| { try writer.writeAll("fn("); @@ -715,7 +715,7 @@ pub const Key = union(enum) { return function_info.return_type; }, - .union_type => @panic("TODO"), + .union_type => panicOrElse("TODO", null), .tuple_type => |tuple_info| { try writer.writeAll("tuple{"); for (tuple_info.types) |field_ty, i| { @@ -2453,6 +2453,14 @@ fn optionalPtrTy( } } +/// will panic in debug mode or in tests else will return `value` +inline fn panicOrElse(message: []const u8, value: anytype) @TypeOf(value) { + if (builtin.is_test or builtin.mode == .Debug) { + @panic(message); + } + return null; +} + // --------------------------------------------- // TESTS // --------------------------------------------- From 4a8fe90d23eaee0f6d7474bf1fca3b93e0fe90a2 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:34:49 +0100 Subject: [PATCH 38/78] add coerceInMemoryAllowed integer & float tests --- src/InternPool.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/InternPool.zig b/src/InternPool.zig index 550130d..2b97a9d 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -3084,6 +3084,38 @@ test "bytes value" { try std.testing.expectEqualStrings("https://www.duckduckgo.com", ip.indexToKey(bytes_value3).bytes); } +test "coerceInMemoryAllowed integers and floats" { + const gpa = std.testing.allocator; + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + var ip: InternPool = .{}; + defer ip.deinit(gpa); + + const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); + const u16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 16 } }); + const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); + const i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); + + const f32_type = try ip.get(gpa, .{ .simple = .f32 }); + const f64_type = try ip.get(gpa, .{ .simple = .f64 }); + + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, u32_type, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, u16_type, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u16_type, u32_type, true, builtin.target) == .int_not_coercible); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, i32_type, u32_type, true, builtin.target) == .int_not_coercible); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, i32_type, true, builtin.target) == .int_not_coercible); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, i16_type, true, builtin.target) == .int_not_coercible); + + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f32_type, f32_type, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f64_type, f32_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f32_type, f64_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, f32_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f32_type, u32_type, true, builtin.target) == .no_match); +} + test "resolvePeerTypes" { const gpa = std.testing.allocator; From 06fcfcb3db90fc2d25fe105b614b7c3e580e9a25 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 17:04:59 +0100 Subject: [PATCH 39/78] add internpool to test steps --- build.zig | 7 ++++++- src/zls.zig | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index d96dd1f..fcdfc9c 100644 --- a/build.zig +++ b/build.zig @@ -49,7 +49,7 @@ pub fn build(b: *std.build.Builder) !void { "enable_tracy_callstack", b.option(bool, "enable_tracy_callstack", "Enable callstack graphs.") orelse false, ); - + exe_options.addOption( bool, "enable_failing_allocator", @@ -171,4 +171,9 @@ pub fn build(b: *std.build.Builder) !void { tests.setBuildMode(.Debug); tests.setTarget(target); test_step.dependOn(&tests.step); + + var src_tests = b.addTest("src/zls.zig"); + src_tests.setBuildMode(.Debug); + src_tests.setTarget(target); + test_step.dependOn(&src_tests.step); } diff --git a/src/zls.zig b/src/zls.zig index ffb943e..f3a5e5c 100644 --- a/src/zls.zig +++ b/src/zls.zig @@ -13,3 +13,8 @@ pub const URI = @import("uri.zig"); pub const DocumentStore = @import("DocumentStore.zig"); pub const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); pub const InternPool = @import("InternPool.zig"); + +comptime { + const std = @import("std"); + std.testing.refAllDecls(@This()); +} From 34b2643b3337b279d3632a252f77796c6dc2d557 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 17:06:16 +0100 Subject: [PATCH 40/78] add basic comptime interpreter tests --- src/ComptimeInterpreter.zig | 14 +- src/InternPool.zig | 2 +- .../comptime_interpreter.zig | 136 ++++++++++++++++-- 3 files changed, 132 insertions(+), 20 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 26ce420..0db230a 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -727,12 +727,14 @@ pub fn interpret( if (as_type.ty != type_type) return error.InvalidBuiltin; - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = type_type, - .val = try interpreter.cast(node_idx, as_type.val, value.val), - } }; + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = as_type.val, + .val = value.val, // TODO port Sema.coerceExtra to InternPool + }, + }; } log.err("Builtin not implemented: {s}", .{call_name}); diff --git a/src/InternPool.zig b/src/InternPool.zig index 2b97a9d..919ee51 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -63,7 +63,7 @@ pub const ErrorSet = struct { names: []const []const u8, pub fn sort(self: *ErrorSet) void { - std.sort.sort([][]const u8, self.names, u8, std.mem.lessThan); + std.sort.sort([]const []const u8, self.names, u8, std.mem.lessThan); } }; diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 79dc509..5aef2fa 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -11,6 +11,40 @@ const Key = InternPool.Key; const allocator: std.mem.Allocator = std.testing.allocator; +test "ComptimeInterpreter - primitive types" { + try testExprCheck("true", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExprCheck("false", .{ .simple = .bool }, .{ .simple = .bool_false }); + try testExprCheck("5", .{ .simple = .comptime_int }, .{ .int_u64_value = 5 }); + // TODO try testExprCheck("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 }); + try testExprCheck("3.0", .{ .simple = .comptime_float }, null); + + if (true) return error.SkipZigTest; // TODO + try testExprCheck("null", .{ .simple = .null_type }, .{ .simple = .null_value }); + try testExprCheck("void", .{ .simple = .void }, .{ .simple = .void_value }); + try testExprCheck("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value }); + try testExprCheck("noreturn", .{ .simple = .noreturn }, .{ .simple = .unreachable_value }); +} + +test "ComptimeInterpreter - expressions" { + if (true) return error.SkipZigTest; // TODO + try testExprCheck("5 + 3", .{ .simple = .comptime_int }, .{ .int_u64_value = 8 }); + try testExprCheck("5.2 + 4.2", .{ .simple = .comptime_float }, null); + + try testExprCheck("3 == 3", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExprCheck("5.2 == 2.1", .{ .simple = .bool }, .{ .simple = .bool_false }); + + try testExprCheck("@as(?bool, null) orelse true", .{ .simple = .bool }, .{ .simple = .bool_true }); +} + +test "ComptimeInterpreter - builtins" { + if (true) return error.SkipZigTest; // TODO + try testExprCheck("@as(bool, true)", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExprCheck("@as(u32, 3)", .{ .int_type = .{ + .signedness = .unsigned, + .bits = 32, + } }, .{ .int_u64_value = 3 }); +} + test "ComptimeInterpreter - call return primitive type" { try testCallCheck( \\pub fn Foo() type { @@ -48,7 +82,9 @@ test "ComptimeInterpreter - call return struct" { \\} , &.{}); defer result.deinit(); - const struct_info = result.key.struct_type; + try std.testing.expect(result.ty == .simple); + try std.testing.expect(result.ty.simple == .type); + const struct_info = 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); try std.testing.expectEqual(@as(usize, 1), struct_info.fields.len); @@ -72,7 +108,9 @@ test "ComptimeInterpreter - call comptime argument" { }, }); defer result1.deinit(); - try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.key); + try std.testing.expect(result1.ty == .simple); + try std.testing.expect(result1.ty.simple == .type); + try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val); var result2 = try testCall(source, &.{ Value{ @@ -81,18 +119,21 @@ test "ComptimeInterpreter - call comptime argument" { }, }); defer result2.deinit(); - try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.key); + try std.testing.expect(result2.ty == .simple); + try std.testing.expect(result2.ty.simple == .type); + try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val); } // // Helper functions // -const CallResult = struct { +const Result = struct { interpreter: ComptimeInterpreter, - key: Key, + ty: Key, + val: Key, - pub fn deinit(self: *CallResult) void { + pub fn deinit(self: *Result) void { self.interpreter.deinit(); } }; @@ -102,7 +143,7 @@ const Value = struct { val: Key, }; -fn testCall(source: []const u8, arguments: []const Value) !CallResult { +fn testCall(source: []const u8, arguments: []const Value) !Result { var config = zls.Config{}; var doc_store = zls.DocumentStore{ .allocator = allocator, @@ -144,20 +185,89 @@ fn testCall(source: []const u8, arguments: []const Value) !CallResult { const call_result = try interpreter.call(.none, func_node, args, .{}); - try std.testing.expectEqual(Key{ .simple = .type }, interpreter.ip.indexToKey(call_result.result.value.ty)); - - return CallResult{ + return Result{ .interpreter = interpreter, - .key = interpreter.ip.indexToKey(call_result.result.value.val), + .ty = interpreter.ip.indexToKey(call_result.result.value.ty), + .val = interpreter.ip.indexToKey(call_result.result.value.val), }; } fn testCallCheck( source: []const u8, arguments: []const Value, - expected: Key, + expected_ty: Key, ) !void { var result = try testCall(source, arguments); defer result.deinit(); - try std.testing.expectEqual(expected, result.key); + try std.testing.expect(result.ty == .simple); + try std.testing.expect(result.ty.simple == .type); + if (!expected_ty.eql(result.val)) { + std.debug.print("expected type `{}`, found `{}`\n", .{ expected_ty.fmtType(result.interpreter.ip), result.val.fmtType(result.interpreter.ip) }); + return error.TestExpectedEqual; + } +} + +fn testInterpret(source: []const u8, node_idx: Ast.Node.Index) !Result { + var config = zls.Config{}; + var doc_store = zls.DocumentStore{ + .allocator = allocator, + .config = &config, + }; + defer doc_store.deinit(); + + const test_uri: []const u8 = switch (builtin.os.tag) { + .windows => "file:///C:\\test.zig", + else => "file:///test.zig", + }; + + const handle = try doc_store.openDocument(test_uri, source); + + var interpreter = ComptimeInterpreter{ + .allocator = allocator, + .document_store = &doc_store, + .uri = handle.uri, + }; + errdefer interpreter.deinit(); + + _ = try interpreter.interpret(0, .none, .{}); + + const result = try interpreter.interpret(node_idx, .none, .{}); + + return Result{ + .interpreter = interpreter, + .ty = interpreter.ip.indexToKey(result.value.ty), + .val = interpreter.ip.indexToKey(result.value.val), + }; +} + +fn testExprCheck( + expr: []const u8, + expected_ty: Key, + expected_val: ?Key, +) !void { + const source = try std.fmt.allocPrint(allocator, + \\const foobarbaz = {s}; + , .{expr}); + defer allocator.free(source); + + var result = try testInterpret(source, 1); + defer result.deinit(); + var ip: *InternPool = &result.interpreter.ip; + + if (!expected_ty.eql(result.ty)) { + std.debug.print("expected type `{}`, found `{}`\n", .{ expected_ty.fmtType(ip.*), result.ty.fmtType(ip.*) }); + return error.TestExpectedEqual; + } + + if (expected_val) |expected_value| { + if (!expected_value.eql(result.val)) { + const expected_ty_index = try ip.get(allocator, expected_ty); + const actual_ty_index = try ip.get(allocator, result.ty); + std.debug.print("expected value `{}`, found `{}`\n", .{ + expected_value.fmtValue(expected_ty_index, ip.*), + result.val.fmtValue(actual_ty_index, ip.*), + }); + return error.TestExpectedEqual; + } + } } From b4ac6142cfcc8ab5314bae00f34cbd99e8bca9f8 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:20:42 +0100 Subject: [PATCH 41/78] correctly resolve primitive types --- src/ComptimeInterpreter.zig | 76 ++++++++++--------- src/InternPool.zig | 42 ++++++++++ .../comptime_interpreter.zig | 5 +- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 0db230a..83b3495 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -378,57 +378,59 @@ pub fn interpret( .identifier => { const value = offsets.nodeToSlice(tree, node_idx); - if (std.mem.eql(u8, "bool", value)) { + const simples = std.ComptimeStringMap(InternPool.Simple, .{ + .{ "anyerror", .anyerror }, + .{ "anyframe", .@"anyframe" }, + .{ "anyopaque", .anyopaque }, + .{ "bool", .bool }, + .{ "c_int", .c_int }, + .{ "c_long", .c_long }, + .{ "c_longdouble", .c_longdouble }, + .{ "c_longlong", .c_longlong }, + .{ "c_short", .c_short }, + .{ "c_uint", .c_uint }, + .{ "c_ulong", .c_ulong }, + .{ "c_ulonglong", .c_ulonglong }, + .{ "c_ushort", .c_ushort }, + .{ "comptime_float", .comptime_float }, + .{ "comptime_int", .comptime_int }, + .{ "f128", .f128 }, + .{ "f16", .f16 }, + .{ "f32", .f32 }, + .{ "f64", .f64 }, + .{ "f80", .f80 }, + .{ "false", .bool_false }, + .{ "isize", .isize }, + .{ "noreturn", .noreturn }, + .{ "null", .null_value }, + .{ "true", .bool_true }, + .{ "type", .type }, + .{ "undefined", .undefined_value }, + .{ "usize", .usize }, + .{ "void", .void }, + }); + + if (simples.get(value)) |simple| { return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), + .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = simple.toType() }), + .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = simple }), } }; - } else if (std.mem.eql(u8, "true", value)) { - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }), - } }; - } else if (std.mem.eql(u8, "false", value)) { - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }), - } }; - } else if (std.mem.eql(u8, "usize", value) or std.mem.eql(u8, "isize", value)) { - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ - .simple = if (value[0] == 'u') .usize else .isize, - }), - } }; - } else if (std.mem.eql(u8, "type", value)) { - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - } }; - } else if (value.len >= 2 and (value[0] == 'u' or value[0] == 'i')) int: { + } + + if (value.len >= 2 and (value[0] == 'u' or value[0] == 'i')) blk: { return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{ .signedness = if (value[0] == 'u') .unsigned else .signed, - .bits = std.fmt.parseInt(u16, value[1..], 10) catch break :int, + .bits = std.fmt.parseInt(u16, value[1..], 10) catch break :blk, } }), } }; } - // TODO: Floats - // Logic to find identifiers in accessible scopes const decl = interpreter.huntItDown(namespace, value, options) catch |err| switch (err) { error.IdentifierNotFound => |e| { diff --git a/src/InternPool.zig b/src/InternPool.zig index 919ee51..88feede 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1052,6 +1052,48 @@ pub const Simple = enum(u32) { null_value, bool_true, bool_false, + + pub fn toType(self: Simple) Simple { + return switch (self) { + .f16, + .f32, + .f64, + .f80, + .f128, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .c_longdouble, + .anyopaque, + .bool, + .void, + .type, + .anyerror, + .comptime_int, + .comptime_float, + .noreturn, + .@"anyframe", + .null_type, + .undefined_type, + .enum_literal_type, + => .type, + + // values + .undefined_value => .undefined_type, + .void_value => .void, + .unreachable_value => .noreturn, + .null_value => .null_type, + .bool_true => .bool, + .bool_false => .bool, + }; + } }; pub fn deinit(ip: *InternPool, gpa: Allocator) void { diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 5aef2fa..da17b57 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -18,11 +18,10 @@ test "ComptimeInterpreter - primitive types" { // TODO try testExprCheck("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 }); try testExprCheck("3.0", .{ .simple = .comptime_float }, null); - if (true) return error.SkipZigTest; // TODO try testExprCheck("null", .{ .simple = .null_type }, .{ .simple = .null_value }); - try testExprCheck("void", .{ .simple = .void }, .{ .simple = .void_value }); + try testExprCheck("void", .{ .simple = .type }, .{ .simple = .void }); try testExprCheck("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value }); - try testExprCheck("noreturn", .{ .simple = .noreturn }, .{ .simple = .unreachable_value }); + try testExprCheck("noreturn", .{ .simple = .type }, .{ .simple = .noreturn }); } test "ComptimeInterpreter - expressions" { From 1e73ac91e53b190282f7a20105668be9ad1a7a64 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:28:25 +0100 Subject: [PATCH 42/78] small comptime interpreter refactor --- src/ComptimeInterpreter.zig | 74 ++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 83b3495..1469ae4 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -171,17 +171,6 @@ pub fn huntItDown( return error.IdentifierNotFound; } -pub fn cast( - interpreter: *ComptimeInterpreter, - node_idx: Ast.Node.Index, - destination_ty: IPIndex, - source_ty: IPIndex, -) error{ OutOfMemory, InvalidCast }!IPIndex { - _ = node_idx; - // TODO return errors - return try interpreter.ip.cast(interpreter.allocator, destination_ty, source_ty, builtin.target); -} - // Might be useful in the future pub const InterpretOptions = struct {}; @@ -223,7 +212,6 @@ pub fn interpret( // .tagged_union_enum_tag, // .tagged_union_enum_tag_trailing, .root, - // .error_set_decl, // TODO => { const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); @@ -289,6 +277,10 @@ pub fn interpret( .val = struct_type, } }; }, + .error_set_decl => { + // TODO + return InterpretResult{ .nothing = {} }; + }, .global_var_decl, .local_var_decl, .aligned_var_decl, @@ -496,10 +488,10 @@ pub fn interpret( .@"if", .if_simple, => { - const iff = ast.ifFull(tree, node_idx); + const if_info = ast.ifFull(tree, node_idx); // TODO: Don't evaluate runtime ifs // if (options.observe_values) { - const ir = try interpreter.interpret(iff.ast.cond_expr, namespace, options); + const ir = try interpreter.interpret(if_info.ast.cond_expr, namespace, options); const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); @@ -507,10 +499,10 @@ pub fn interpret( const condition = (try ir.getValue()).val; std.debug.assert(condition == false_value or condition == true_value); if (condition == true_value) { - return try interpreter.interpret(iff.ast.then_expr, namespace, options); + return try interpreter.interpret(if_info.ast.then_expr, namespace, options); } else { - if (iff.ast.else_expr != 0) { - return try interpreter.interpret(iff.ast.else_expr, namespace, options); + if (if_info.ast.else_expr != 0) { + return try interpreter.interpret(if_info.ast.else_expr, namespace, options); } else return InterpretResult{ .nothing = {} }; } }, @@ -525,8 +517,6 @@ pub fn interpret( .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (a.value.val == b.value.val) .bool_true else .bool_false }), // TODO eql function required? }, }; - - // a.getValue().eql(b.getValue()) }, .number_literal => { const s = tree.getNodeSource(node_idx); @@ -534,7 +524,7 @@ pub fn interpret( if (nl == .failure) return error.CriticalAstFailure; - const comptime_int_type = try interpreter.ip.get(interpreter.allocator, IPKey{ + const number_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (nl == .float) .comptime_float else .comptime_int, }); @@ -542,7 +532,7 @@ pub fn interpret( interpreter.allocator, switch (nl) { .float => IPKey{ - .float_64_value = try std.fmt.parseFloat(f64, s), // shouldn't this be f128? + .float_128_value = try std.fmt.parseFloat(f128, s), }, .int => if (s[0] == '-') IPKey{ .int_i64_value = try std.fmt.parseInt(i64, s, 0), @@ -557,7 +547,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = comptime_int_type, + .ty = number_type, .val = value, } }; }, @@ -587,7 +577,8 @@ pub fn interpret( var to_value = try ir.getValue(); var from_value = (try (try interpreter.interpret(data[node_idx].rhs, namespace, options)).getValue()); - _ = try interpreter.cast(undefined, to_value.ty, from_value.ty); + // TODO report error + _ = try interpreter.ip.cast(interpreter.allocator, to_value.ty, from_value.ty, builtin.target); return InterpretResult{ .nothing = {} }; }, @@ -658,7 +649,7 @@ pub fn interpret( .fields = &.{}, .namespace = .none, .layout = .Auto, - .backing_int_ty = IPIndex.none, + .backing_int_ty = .none, } }), .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .undefined_value }), } }; @@ -675,7 +666,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = undefined, // TODO + .val = .none, // TODO }, }; } @@ -763,14 +754,12 @@ pub fn interpret( .address_space = .generic, } }); - var val = Value{ + return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, .ty = string_literal_type, .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), - }; - - return InterpretResult{ .value = val }; + } }; }, // TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8) .@"comptime" => { @@ -874,23 +863,22 @@ pub fn interpret( const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const bool_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }); const value = try result.getValue(); - if (value.ty == bool_type) { - const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); - const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); - const not_value = if (value.val == false_value) true_value else if (value.val == true_value) false_value else return error.InvalidOperation; - return InterpretResult{ - .value = .{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = bool_type, - .val = not_value, - }, - }; - } else { - // TODO + if (value.ty != bool_type) { + try interpreter.recordError(node_idx, "invalid_deref", try std.fmt.allocPrint(interpreter.allocator, "expected type `bool` but got `{}`", .{value.ty.fmtType(interpreter.ip)})); return error.InvalidOperation; } + + const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); + const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); + + std.debug.assert(value.val == false_value or value.val == true_value); + return InterpretResult{ .value = .{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = bool_type, + .val = if (value.val == false_value) true_value else false_value, + } }; }, .address_of => { // TODO: Make const pointers if we're drawing from a const; From ea7fc74483730f33588aad60aa570f5cab7eea17 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:29:03 +0100 Subject: [PATCH 43/78] add more comptime interpreter tests --- .../comptime_interpreter.zig | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index da17b57..8f9c252 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -44,6 +44,43 @@ test "ComptimeInterpreter - builtins" { } }, .{ .int_u64_value = 3 }); } +test "ComptimeInterpreter - string literal" { + if (true) return error.SkipZigTest; // TODO + const source = + \\const foobarbaz = "hello world!"; + \\ + ; + + var result = try testInterpret(source, 1); + defer result.deinit(); + + try std.testing.expect(result.ty == .pointer_type); + try std.testing.expect(result.val == .bytes); + + try std.testing.expectEqualStrings("hello world!", result.val.bytes); +} + +test "ComptimeInterpreter - labeled block" { + try testExprCheck( + \\blk: { + \\ break :blk true; + \\} + , .{ .simple = .bool }, .{ .simple = .bool_true }); +} + +test "ComptimeInterpreter - if" { + try testExprCheck( + \\blk: { + \\ break :blk if (true) true else false; + \\} + , .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExprCheck( + \\blk: { + \\ break :blk if (false) true else false; + \\} + , .{ .simple = .bool }, .{ .simple = .bool_false }); +} + test "ComptimeInterpreter - call return primitive type" { try testCallCheck( \\pub fn Foo() type { @@ -232,6 +269,9 @@ fn testInterpret(source: []const u8, node_idx: Ast.Node.Index) !Result { const result = try interpreter.interpret(node_idx, .none, .{}); + try std.testing.expect(result.value.ty != .none); + try std.testing.expect(result.value.val != .none); + return Result{ .interpreter = interpreter, .ty = interpreter.ip.indexToKey(result.value.ty), From 861e2ffb059a03f88956f36e07203bace7913f23 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:55:26 +0100 Subject: [PATCH 44/78] add formatting to recordError --- src/ComptimeInterpreter.zig | 38 ++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 1469ae4..196ddb1 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -40,10 +40,16 @@ pub const InterpreterError = struct { }; /// `message` must be allocated with interpreter allocator -pub fn recordError(interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, code: []const u8, message: []const u8) error{OutOfMemory}!void { +pub fn recordError( + interpreter: *ComptimeInterpreter, + node_idx: Ast.Node.Index, + code: []const u8, + comptime fmt: []const u8, + args: anytype, +) error{OutOfMemory}!void { try interpreter.errors.put(interpreter.allocator, node_idx, .{ .code = code, - .message = message, + .message = try std.fmt.allocPrint(interpreter.allocator, fmt, args), }); } @@ -244,7 +250,8 @@ pub fn interpret( try interpreter.recordError( container_field.ast.type_expr, "expected_type", - try std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{init_type_value.ty.fmtType(interpreter.ip)}), + "expected type 'type', found '{}'", + .{init_type_value.ty.fmtType(interpreter.ip)}, ); continue; } @@ -429,7 +436,8 @@ pub fn interpret( try interpreter.recordError( node_idx, "undeclared_identifier", - try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{value}), + "use of undeclared identifier '{s}'", + .{value}, ); return e; }, @@ -456,7 +464,8 @@ pub fn interpret( try interpreter.recordError( node_idx, "undeclared_identifier", - try std.fmt.allocPrint(interpreter.allocator, "use of undeclared identifier '{s}'", .{rhs_str}), + "`{}` has no member '{s}'", + .{ irv.ty.fmtType(interpreter.ip), rhs_str }, ); return e; }, @@ -620,14 +629,15 @@ pub fn interpret( if (index != params.len - 1) try writer.writeAll(", "); } - try interpreter.recordError(node_idx, "compile_log", try final.toOwnedSlice()); + try interpreter.recordError(node_idx, "compile_log", "{s}", .{try final.toOwnedSlice()}); return InterpretResult{ .nothing = {} }; } if (std.mem.eql(u8, call_name, "@compileError")) { - // TODO: Add message - try interpreter.recordError(node_idx, "compile_error", try std.fmt.allocPrint(interpreter.allocator, "compile error", .{})); + if (params.len != 0) return error.InvalidBuiltin; + const message = offsets.nodeToSlice(tree, params[0]); + try interpreter.recordError(node_idx, "compile_error", "{s}", .{message}); return InterpretResult{ .@"return" = {} }; } @@ -865,7 +875,12 @@ pub fn interpret( const value = try result.getValue(); if (value.ty != bool_type) { - try interpreter.recordError(node_idx, "invalid_deref", try std.fmt.allocPrint(interpreter.allocator, "expected type `bool` but got `{}`", .{value.ty.fmtType(interpreter.ip)})); + try interpreter.recordError( + node_idx, + "invalid_deref", + "expected type `bool` but got `{}`", + .{value.ty.fmtType(interpreter.ip)}, + ); return error.InvalidOperation; } @@ -912,7 +927,7 @@ pub fn interpret( const type_key = interpreter.ip.indexToKey(value.ty); if (type_key != .pointer_type) { - try interpreter.recordError(node_idx, "invalid_deref", try std.fmt.allocPrint(interpreter.allocator, "cannot deference non-pointer", .{})); + try interpreter.recordError(node_idx, "invalid_deref", "cannot deference non-pointer", .{}); return error.InvalidOperation; } @@ -973,7 +988,8 @@ pub fn call( try interpreter.recordError( param.type_expr, "expected_type", - std.fmt.allocPrint(interpreter.allocator, "expected type 'type', found '{}'", .{tex.ty.fmtType(interpreter.ip)}) catch return error.CriticalAstFailure, + "expected type 'type', found '{}'", + .{tex.ty.fmtType(interpreter.ip)}, ); return error.InvalidCast; } From bcd2b7f1aee8f52c997561759427b5c320ccd57c Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:55:53 +0100 Subject: [PATCH 45/78] report interpreter errors in tests --- .../comptime_interpreter.zig | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 8f9c252..ae66b36 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -8,6 +8,8 @@ const ComptimeInterpreter = zls.ComptimeInterpreter; const InternPool = zls.InternPool; const Index = InternPool.Index; const Key = InternPool.Key; +const ast = zls.ast; +const offsets = zls.offsets; const allocator: std.mem.Allocator = std.testing.allocator; @@ -201,7 +203,7 @@ fn testCall(source: []const u8, arguments: []const Value) !Result { }; errdefer interpreter.deinit(); - _ = try interpreter.interpret(0, .none, .{}); + _ = try interpretReportErrors(&interpreter, 0, .none); var args = try allocator.alloc(ComptimeInterpreter.Value, arguments.len); defer allocator.free(args); @@ -265,7 +267,7 @@ fn testInterpret(source: []const u8, node_idx: Ast.Node.Index) !Result { }; errdefer interpreter.deinit(); - _ = try interpreter.interpret(0, .none, .{}); + _ = try interpretReportErrors(&interpreter, 0, .none); const result = try interpreter.interpret(node_idx, .none, .{}); @@ -310,3 +312,24 @@ fn testExprCheck( } } } + +fn interpretReportErrors( + interpreter: *ComptimeInterpreter, + node_idx: Ast.Node.Index, + namespace: InternPool.NamespaceIndex, +) !ComptimeInterpreter.InterpretResult { + const result = interpreter.interpret(node_idx, namespace, .{}); + + // TODO use ErrorBuilder + var err_it = interpreter.errors.iterator(); + if (interpreter.errors.count() != 0) { + const handle = interpreter.getHandle(); + std.debug.print("\n{s}\n", .{handle.text}); + while (err_it.next()) |entry| { + const token = handle.tree.firstToken(entry.key_ptr.*); + const position = offsets.tokenToPosition(handle.tree, token, .@"utf-8"); + std.debug.print("{d}:{d}: {s}\n", .{ position.line, position.character, entry.value_ptr.message }); + } + } + return result; +} From 6d12c640ee856f197b3e39e662f2fbc2864b5d94 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 22:24:39 +0100 Subject: [PATCH 46/78] add initial completions on InternPool --- src/analyser/completions.zig | 145 +++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/analyser/completions.zig diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig new file mode 100644 index 0000000..8bee981 --- /dev/null +++ b/src/analyser/completions.zig @@ -0,0 +1,145 @@ +const std = @import("std"); +const InternPool = @import("InternPool.zig"); +const types = @import("lsp.zig"); + +const Ast = std.zig.Ast; + +pub fn dotCompletions( + arena: std.mem.Allocator, + ip: *InternPool, + ty: InternPool.Index, + /// used for extracting doc comments + node: ?Ast.Node.Index, +) error{OutOfMemory}![]types.CompletionItem { + return try dotCompletionsInternal(arena, ip, ty, node); +} + +pub fn dotCompletionsInternal( + arena: std.mem.Allocator, + ip: *InternPool, + ty: InternPool.Index, + node: ?Ast.Node.Index, + follow_one_pointer: bool, +) error{OutOfMemory}![]types.CompletionItem { + _ = node; + + var completions = std.ArrayListUnmanaged(types.CompletionItem){}; + switch (ip.indexToKey(ty)) { + .simple => {}, + + .int_type => {}, + .pointer_type => |pointer_info| { + switch (pointer_info.size) { + .One => { + try completions.append(arena, .{ + .label = "*", + .kind = .Operator, + .detail = std.fmt.allocPrint(arena, "{}", .{pointer_info.elem_type.fmtType(ip)}), + }); + if (follow_one_pointer) { + try dotCompletionsInternal(arena, ip, pointer_info.elem_type, false); + } + }, + .Slice => { + var many_ptr_info = InternPool.Key{ .pointer_type = pointer_info }; + many_ptr_info.pointer_info.size = .Many; + + try completions.append(arena, .{ + .label = "ptr", + .kind = .Field, + .detail = std.fmt.allocPrint(arena, "{}", .{many_ptr_info.fmtType(ip)}), + }); + try completions.append(arena, .{ + .label = "len", + .kind = .Field, + .detail = "usize", + }); + }, + .Many, + .C, + => {}, + } + }, + .array_type => |array_info| { + try completions.append(arena, types.CompletionItem{ + .label = "len", + .labelDetails = std.fmt.allocPrint(arena, "{d}", .{array_info.len}), + .kind = .Field, + .detail = "usize", + }); + }, + .struct_type => |struct_info| { + for (struct_info.fields) |field| { + try completions.append(arena, types.CompletionItem{ + .label = field.name, + .kind = .Field, + // TODO include alignment and comptime + .detail = std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), + }); + } + // TODO namespace + }, + .optional_type => |optional_info| { + try completions.append(arena, .{ + .label = "?", + .kind = .Operator, + .detail = std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmtType(ip)}), + }); + }, + .error_union_type => {}, + .error_set_type => |error_set_info| { + for (error_set_info.names) |name| { + try completions.append(arena, .{ + .label = name, + .kind = .Constant, + .detail = std.fmt.allocPrint(arena, "error.{s}", .{name}), + }); + } + }, + .enum_type => |enum_info| { + for (enum_info.fields) |field| { + try completions.append(arena, .{ + .label = field.name, + .kind = .Field, + .detail = std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), + }); + } + // TODO namespace + }, + .function_type => {}, + .union_type => |union_info| { + for (union_info.fields) |field| { + try completions.append(arena, .{ + .label = field.name, + .kind = .Field, + .detail = if (field.alignment != 0) + std.fmt.allocPrint(arena, "align({d}) {}", .{ field.alignment, field.ty.fmtType(ip) }) + else + std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), + }); + } + // TODO namespace + }, + .tuple_type => { + // TODO + }, + .vector_type => {}, + .anyframe_type => {}, + + .int_u64_value, + .int_i64_value, + .int_big_value, + .float_16_value, + .float_32_value, + .float_64_value, + .float_80_value, + .float_128_value, + => {}, + + .bytes, + .aggregate, + .union_value, + => {}, + } + return try completions.toOwnedSlice(arena); +} From 89ac9d00f54c55796e00bf7282b50a0bea5eccbf Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 22:25:19 +0100 Subject: [PATCH 47/78] add more interpreter tests --- .../comptime_interpreter.zig | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index ae66b36..a110e9a 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -83,6 +83,39 @@ test "ComptimeInterpreter - if" { , .{ .simple = .bool }, .{ .simple = .bool_false }); } +test "ComptimeInterpreter - field access" { + if (true) return error.SkipZigTest; // TODO + try testExprCheck( + \\blk: { + \\ const foo = struct {alpha: u32, beta: bool} = undefined; + \\ break :blk foo.beta; + \\} + , .{ .simple = .bool }, null); +} + +test "ComptimeInterpreter - pointer operations" { + if (true) return error.SkipZigTest; // TODO + try testExprCheck( + \\blk: { + \\ const foo: []const u8 = ""; + \\ break :blk foo.len; + \\} + , .{ .simple = .usize }, .{ .bytes = "" }); + try testExprCheck( + \\blk: { + \\ const foo = true; + \\ break :blk &foo; + \\} + , @panic("TODO"), .{ .simple = .bool_true }); + try testExprCheck( + \\blk: { + \\ const foo = true; + \\ const bar = &foo; + \\ break :blk bar.*; + \\} + , @panic("TODO"), .{ .simple = .bool_true }); +} + test "ComptimeInterpreter - call return primitive type" { try testCallCheck( \\pub fn Foo() type { From 3085c3e1538baebf04d1e84d6e7d763cf474d9bb Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 20 Jan 2023 22:39:19 +0100 Subject: [PATCH 48/78] implement big int --- src/ComptimeInterpreter.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 196ddb1..ba620cd 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -548,7 +548,13 @@ pub fn interpret( } else IPKey{ .int_u64_value = try std.fmt.parseInt(u64, s, 0), }, - .big_int => @panic("TODO: implement big int"), + .big_int => |base| blk: { + var big_int = try std.math.big.int.Managed.init(interpreter.allocator); + defer big_int.deinit(); + const prefix_length: usize = if (base != .decimal) 2 else 0; + try big_int.setString(@enumToInt(base), s[prefix_length..]); + break :blk IPKey{ .int_big_value = big_int.toConst() }; + }, .failure => return error.CriticalAstFailure, }, ); From db97a192330b320ba66b94fe8394dbc8a69962e7 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 22 Jan 2023 21:57:40 +0100 Subject: [PATCH 49/78] add test filter to src tests --- build.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 40ae7d0..5e9d480 100644 --- a/build.zig +++ b/build.zig @@ -160,12 +160,13 @@ pub fn build(b: *std.build.Builder) !void { const test_step = b.step("test", "Run all the tests"); test_step.dependOn(b.getInstallStep()); - var tests = b.addTest("tests/tests.zig"); - tests.setFilter(b.option( + const test_filter = b.option( []const u8, "test-filter", "Skip tests that do not match filter", - )); + ); + var tests = b.addTest("tests/tests.zig"); + tests.setFilter(test_filter); if (coverage) { const src_dir = b.pathJoin(&.{ b.build_root, "src" }); @@ -186,6 +187,7 @@ pub fn build(b: *std.build.Builder) !void { test_step.dependOn(&tests.step); var src_tests = b.addTest("src/zls.zig"); + src_tests.setFilter(test_filter); src_tests.setBuildMode(.Debug); src_tests.setTarget(target); test_step.dependOn(&src_tests.step); From 0817d6008b0e6b2c128ea284e358617875e734b6 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 22 Jan 2023 21:59:30 +0100 Subject: [PATCH 50/78] redesign InternPool encoding --- src/ComptimeInterpreter.zig | 6 +- src/InternPool.zig | 173 +++++++++++++++-------------- src/Server.zig | 5 +- src/encoding.zig | 210 ++++++++++++++++++++++++++++++++++++ 4 files changed, 311 insertions(+), 83 deletions(-) create mode 100644 src/encoding.zig diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index ba620cd..3a7037b 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -255,9 +255,11 @@ 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 = tree.tokenSlice(container_field.ast.main_token), + .name = field_name, .ty = init_type_value.val, .default_value = default_value, .alignment = 0, // TODO, diff --git a/src/InternPool.zig b/src/InternPool.zig index 88feede..f9e79e3 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -10,6 +10,8 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const encoding = @import("encoding.zig"); + pub const Int = packed struct { signedness: std.builtin.Signedness, bits: u16, @@ -40,8 +42,8 @@ pub const Struct = struct { layout: std.builtin.Type.ContainerLayout = .Auto, backing_int_ty: Index, - pub const Field = struct { - name: []const u8, + pub const Field = packed struct { + name: Index, ty: Index, default_value: Index = .none, alignment: u16 = 0, @@ -59,12 +61,7 @@ pub const ErrorUnion = packed struct { }; pub const ErrorSet = struct { - /// must be sorted - names: []const []const u8, - - pub fn sort(self: *ErrorSet) void { - std.sort.sort([]const []const u8, self.names, u8, std.mem.lessThan); - } + names: []const Index, }; pub const Enum = struct { @@ -73,8 +70,8 @@ pub const Enum = struct { namespace: NamespaceIndex, tag_type_infered: bool, - pub const Field = struct { - name: []const u8, + pub const Field = packed struct { + name: Index, val: Index, }; }; @@ -97,8 +94,8 @@ pub const Union = struct { namespace: NamespaceIndex, layout: std.builtin.Type.ContainerLayout = .Auto, - pub const Field = struct { - name: []const u8, + pub const Field = packed struct { + name: Index, ty: Index, alignment: u16, }; @@ -675,7 +672,7 @@ pub const Key = union(enum) { try writer.writeAll("error{"); for (names) |name, i| { if (i != 0) try writer.writeByte(','); - try writer.writeAll(name); + try writer.writeAll(ip.indexToKey(name).bytes); } try writer.writeByte('}'); }, @@ -853,7 +850,8 @@ pub const Key = union(enum) { while (i < aggregate.len) : (i += 1) { if (i != 0) try writer.writeAll(", "); - try writer.print(".{s} = ", .{struct_info.fields[i].name}); + const field_name = ip.indexToKey(struct_info.fields[i].name).bytes; + try writer.print(".{s} = ", .{field_name}); try printValue(ip.indexToKey(aggregate[i]), struct_info.fields[i].ty, ip, writer); } try writer.writeByte('}'); @@ -861,9 +859,9 @@ pub const Key = union(enum) { .union_value => |union_value| { const union_info = ip.indexToKey(ty).union_type; + const name = ip.indexToKey(union_info.fields[union_value.field_index].name).bytes; try writer.print(".{{ .{} = {} }}", .{ - std.zig.fmtId(union_info.fields[union_value.field_index].name), - // union_value.tag.fmtValue(union_info.tag_type, ip), + std.zig.fmtId(name), union_value.val.fmtValue(union_info.fields[union_value.field_index].ty, ip), }); }, @@ -1182,20 +1180,21 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { } fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { - comptime if (@sizeOf(@TypeOf(extra)) <= 4) { - @compileError(@typeName(@TypeOf(extra)) ++ " fits into a u32! Consider directly storing this extra in Item's data field"); + const T = @TypeOf(extra); + comptime if (@sizeOf(T) <= 4) { + @compileError(@typeName(T) ++ " fits into a u32! Consider directly storing this extra in Item's data field"); }; - // TODO this doesn't correctly store types with slices like `Struct` `Aggregate` const result = @intCast(u32, ip.extra.items.len); - try ip.extra.appendSlice(gpa, &std.mem.toBytes(extra)); + var managed = ip.extra.toManaged(gpa); + defer ip.extra = managed.moveToUnmanaged(); + try encoding.encode(&managed, T, extra); return result; } fn extraData(ip: InternPool, comptime T: type, index: usize) T { - const size = @sizeOf(T); - const bytes = @ptrCast(*const [size]u8, ip.extra.items.ptr + index); - return std.mem.bytesToValue(T, bytes); + var bytes: []const u8 = ip.extra.items[index..]; + return encoding.decode(&bytes, T); } const KeyAdapter = struct { @@ -1217,7 +1216,7 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool { switch (@typeInfo(T)) { .Struct => |info| { - if (comptime std.meta.trait.hasUniqueRepresentation(T)) { + if (info.layout == .Packed and comptime std.meta.trait.hasUniqueRepresentation(T)) { return std.mem.eql(u8, std.mem.asBytes(&a), std.mem.asBytes(&b)); } inline for (info.fields) |field_info| { @@ -1239,40 +1238,39 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool { } return false; }, - .Pointer => |info| { - if (info.size != .Slice) { - @compileError("cannot compare non slice pointer type " ++ @typeName(T)); - } + .Pointer => |info| switch (info.size) { + .One => return deepEql(a.*, b.*), + .Slice => { + if (a.len != b.len) return false; - 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; + var i: usize = 0; + while (i < a.len) : (i += 1) { + if (!deepEql(a[i], b[i])) return false; + } + return true; + }, + .Many, + .C, + => @compileError("Unable to equality compare pointer " ++ @typeName(T)), }, .Bool, .Int, .Float, .Enum, => return a == b, - else => unreachable, + else => @compileError("Unable to equality compare type " ++ @typeName(T)), } } fn deepHash(hasher: anytype, key: anytype) void { - const Inner = @TypeOf(key); + const T = @TypeOf(key); - switch (@typeInfo(Inner)) { + switch (@typeInfo(T)) { .Int => { - if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) { + if (comptime std.meta.trait.hasUniqueRepresentation(Tuple)) { hasher.update(std.mem.asBytes(&key)); } else { - const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(Inner), 8) catch unreachable; + const byte_size = comptime std.math.divCeil(comptime_int, @bitSizeOf(T), 8) catch unreachable; hasher.update(std.mem.asBytes(&key)[0..byte_size]); } }, @@ -1288,22 +1286,25 @@ fn deepHash(hasher: anytype, key: anytype) void { 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); + .Pointer => |info| switch (info.size) { + .One => { + deepHash(hasher, key.*); + }, + .Slice => { + if (info.child == u8) { + hasher.update(key); + } else { + for (key) |item| { + deepHash(hasher, item); + } } - } + }, + .Many, + .C, + => @compileError("Unable to hash pointer " ++ @typeName(T)), }, - .Struct => |info| { - if (comptime std.meta.trait.hasUniqueRepresentation(Inner)) { + if (info.layout == .Packed and comptime std.meta.trait.hasUniqueRepresentation(T)) { hasher.update(std.mem.asBytes(&key)); } else { inline for (info.fields) |field| { @@ -1324,7 +1325,7 @@ fn deepHash(hasher: anytype, key: anytype) void { } } }, - else => unreachable, + else => @compileError("Unable to hash type " ++ @typeName(T)), } } @@ -2797,23 +2798,26 @@ test "error set type" { var ip: InternPool = .{}; defer ip.deinit(gpa); + 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 empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); const error_set_0 = try ip.get(gpa, .{ .error_set_type = .{ - .names = &.{ "foo", "bar", "baz" }, + .names = &.{ foo_name, bar_name, baz_name }, } }); - const foo: [3]u8 = "foo".*; - const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{ - .names = &.{ &foo, "bar", "baz" }, + .names = &.{ foo_name, bar_name }, } }); try std.testing.expect(empty_error_set != error_set_0); - try std.testing.expect(error_set_0 == error_set_1); + try std.testing.expect(error_set_0 != error_set_1); try testExpectFmtType(ip, empty_error_set, "error{}"); try testExpectFmtType(ip, error_set_0, "error{foo,bar,baz}"); + try testExpectFmtType(ip, error_set_1, "error{foo,bar}"); } test "error union type" { @@ -2875,9 +2879,13 @@ test "struct type" { const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } }); const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const field1 = Struct.Field{ .name = "foo", .ty = u64_type }; - const field2 = Struct.Field{ .name = "bar", .ty = i32_type }; - const field3 = Struct.Field{ .name = "baz", .ty = bool_type }; + 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 }, @@ -2902,6 +2910,9 @@ test "enum type" { 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 = &.{}, @@ -2916,8 +2927,8 @@ test "enum type" { .tag_type_infered = true, } }); - const field1 = Enum.Field{ .name = "zig", .val = .none }; - const field2 = Enum.Field{ .name = "cpp", .val = .none }; + 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, @@ -2992,8 +3003,11 @@ test "union type" { const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); const void_type = try ip.get(gpa, .{ .simple = .void }); - var field1 = Union.Field{ .name = "Ok", .ty = u32_type, .alignment = 0 }; - var field2 = Union.Field{ .name = "Err", .ty = void_type, .alignment = 0 }; + 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 = .{ @@ -3026,8 +3040,11 @@ 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 }); - var field1 = Union.Field{ .name = "int", .ty = u32_type, .alignment = 0 }; - var field2 = Union.Field{ .name = "float", .ty = f16_type, .alignment = 0 }; + 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 = .{ @@ -3093,22 +3110,18 @@ test "vector type" { } test "bytes value" { - if (true) return error.SkipZigTest; // TODO - const gpa = std.testing.allocator; var ip: InternPool = .{}; defer ip.deinit(gpa); var str1: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*; - const bytes_value1 = try ip.get(gpa, .{ .bytes = &str1 }); @memset(&str1, 0, str1.len); var str2: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*; - @memset(&str2, 0, str2.len); - const bytes_value2 = try ip.get(gpa, .{ .bytes = &str2 }); + @memset(&str2, 0, str2.len); var str3: [26]u8 = "https://www.duckduckgo.com".*; const bytes_value3 = try ip.get(gpa, .{ .bytes = &str3 }); @@ -3117,9 +3130,11 @@ test "bytes value" { try std.testing.expect(bytes_value1 == bytes_value2); try std.testing.expect(bytes_value2 != bytes_value3); - try std.testing.expect(str1 != ip.indexToKey(bytes_value1).bytes); - try std.testing.expect(str2 != ip.indexToKey(bytes_value2).bytes); - try std.testing.expect(str3 != ip.indexToKey(bytes_value3).bytes); + try std.testing.expect(@ptrToInt(&str1) != @ptrToInt(ip.indexToKey(bytes_value1).bytes.ptr)); + try std.testing.expect(@ptrToInt(&str2) != @ptrToInt(ip.indexToKey(bytes_value2).bytes.ptr)); + try std.testing.expect(@ptrToInt(&str3) != @ptrToInt(ip.indexToKey(bytes_value3).bytes.ptr)); + + try std.testing.expectEqual(ip.indexToKey(bytes_value1).bytes.ptr, ip.indexToKey(bytes_value2).bytes.ptr); try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value1).bytes); try std.testing.expectEqualStrings("https://www.youtube.com/watch?v=dQw4w9WgXcQ", ip.indexToKey(bytes_value2).bytes); diff --git a/src/Server.zig b/src/Server.zig index 1710b2b..ca71f60 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -600,10 +600,11 @@ fn typeToCompletion( switch (key) { .struct_type => |struct_info| { for (struct_info.fields) |field| { + const field_name = co.interpreter.ip.indexToKey(field.name).bytes; try list.append(allocator, .{ - .label = field.name, + .label = field_name, .kind = .Field, - .insertText = field.name, + .insertText = field_name, .insertTextFormat = .PlainText, }); } diff --git a/src/encoding.zig b/src/encoding.zig new file mode 100644 index 0000000..79f9709 --- /dev/null +++ b/src/encoding.zig @@ -0,0 +1,210 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const Index = usize; + +pub fn encode(extra: *std.ArrayList(u8), comptime T: type, data: anytype) Allocator.Error!void { + switch (@typeInfo(T)) { + .Type, + .NoReturn, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .ErrorUnion, + .ErrorSet, + .Fn, + .Opaque, + .Frame, + .AnyFrame, + .EnumLiteral, + => @compileError("Unable to encode type " ++ @typeName(T)), + + .Void => {}, + .Bool => try encode(extra, u1, @boolToInt(data)), + .Int => try extra.appendSlice(std.mem.asBytes(&data)), + .Float => |info| switch (info.bits) { + 16 => try encode(extra, u16, @bitCast(u16, data)), + 32 => try encode(extra, u32, @bitCast(u32, data)), + 64 => try encode(extra, u64, @bitCast(u64, data)), + 80 => try encode(extra, u80, @bitCast(u80, data)), + 128 => try encode(extra, u128, @bitCast(u128, data)), + else => @compileError("Unable to encode type " ++ @typeName(T)), + }, + .Pointer => |info| { + switch (info.size) { + .One => { + if (comptime canEncodeAsBytes(info.child)) { + try extra.appendNTimes(undefined, std.mem.alignPointerOffset(extra.items.ptr + extra.items.len, info.alignment).?); + try encode(extra, info.child, data.*); + } else { + @compileError("Encoding " ++ @typeName(T) ++ " would require allocation"); + } + }, + .Slice => { + if (comptime canEncodeAsBytes(info.child)) { + try encode(extra, u32, @intCast(u32, data.len)); + try extra.appendNTimes(undefined, std.mem.alignPointerOffset(extra.items.ptr + extra.items.len, info.alignment).?); + try extra.appendSlice(std.mem.sliceAsBytes(data)); + } else { + @compileError("Encoding " ++ @typeName(T) ++ " would require allocation"); + } + }, + + .Many, + .C, + => @compileError("Unable to encode type " ++ @typeName(T)), + } + }, + .Array => |info| { + for (data) |item| { + try encode(extra, info.child, item); + } + }, + .Struct => |info| { + switch (info.layout) { + .Packed, + .Extern, + => return try extra.appendSlice(std.mem.asBytes(&data)), + .Auto => { + inline for (info.fields) |field| { + try encode(extra, field.type, @field(data, field.name)); + } + }, + } + }, + .Optional => { + try encode(extra, bool, data == null); + if (data) |item| { + try encode(extra, item); + } + }, + .Enum => |info| try encode(extra, info.tag_type, @enumToInt(data)), + .Union => @compileError("TODO"), + .Vector => |info| { + const array: [info.len]info.child = data; + try encode(extra, array); + }, + } +} + +pub fn decode(extra: *[]const u8, comptime T: type) T { + return switch (@typeInfo(T)) { + .Type, + .NoReturn, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .ErrorUnion, + .ErrorSet, + .Fn, + .Opaque, + .Frame, + .AnyFrame, + .EnumLiteral, + => @compileError("Unable to decode type " ++ @typeName(T)), + + .Void => {}, + .Bool => decode(extra, u1) == 1, + .Int => std.mem.bytesToValue(T, readArray(extra, @sizeOf(T))), + .Float => |info| switch (info.bits) { + 16 => @bitCast(T, decode(extra, u16)), + 32 => @bitCast(T, decode(extra, u32)), + 64 => @bitCast(T, decode(extra, u64)), + 80 => @bitCast(T, decode(extra, u80)), + 128 => @bitCast(T, decode(extra, u128)), + else => @compileError("Unable to decode type " ++ @typeName(T)), + }, + .Pointer => |info| { + switch (info.size) { + .One => { + if (comptime canEncodeAsBytes(info.child)) { + extra.* = alignForward(extra.*, info.alignment); + return std.mem.bytesAsValue(T, readArray(extra, @sizeOf(info.child))); + } else { + @compileError("Decoding " ++ @typeName(T) ++ " would require allocation"); + } + }, + .Slice => { + if (comptime canEncodeAsBytes(info.child)) { + const len = decode(extra, u32); + extra.* = alignForward(extra.*, info.alignment); + const bytes = readBytes(extra, len * @sizeOf(info.child)); + return std.mem.bytesAsSlice(info.child, @alignCast(info.alignment, bytes)); + } else { + @compileError("Decoding " ++ @typeName(T) ++ " would require allocation"); + } + }, + + .Many, + .C, + => @compileError("Unable to decode type " ++ @typeName(T)), + } + }, + .Array => |info| blk: { + var array: T = undefined; + var i: usize = 0; + while (i < info.len) { + array[i] = decode(extra, info.child); + } + break :blk array; + }, + .Struct => |info| { + switch (info.layout) { + .Packed, + .Extern, + => return std.mem.bytesToValue(T, readArray(extra, @sizeOf(T))), + .Auto => { + var result: T = undefined; + inline for (info.fields) |field| { + @field(result, field.name) = decode(extra, field.type); + } + return result; + }, + } + }, + .Optional => |info| blk: { + const is_null = decode(extra, bool); + if (is_null) { + break :blk null; + } else { + break :blk decode(extra, info.child); + } + }, + .Enum => |info| @intToEnum(T, decode(extra, info.tag_type)), + .Union => @compileError("TODO"), + .Vector => |info| decode(extra, [info.len]info.child), + }; +} + +pub fn canEncodeAsBytes(comptime T: type) bool { + return switch (@typeInfo(T)) { + .Void, .Bool, .Int, .Float, .Enum, .Vector => true, + .Array => |info| canEncodeAsBytes(info.child), + .Struct => |info| info.layout != .Auto, + .Union => |info| info.layout != .Auto, + else => false, + }; +} + +/// forward aligns `extra` until it has the given alignment +pub fn alignForward(extra: []const u8, alignment: usize) []const u8 { + const unaligned = @ptrToInt(extra.ptr); + const offset = std.mem.alignForward(unaligned, alignment) - unaligned; + const result = extra[offset..]; + std.debug.assert(std.mem.isAligned(@ptrToInt(result.ptr), alignment)); + return result; +} + +pub fn readBytes(extra: *[]const u8, n: usize) []const u8 { + defer extra.* = extra.*[n..]; + return extra.*[0..n]; +} + +pub fn readArray(extra: *[]const u8, comptime n: usize) *const [n]u8 { + defer extra.* = extra.*[n..]; + return extra.*[0..n]; +} From 4e985e252beaf711b9f9779eb30477b77c7adb3b Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 22 Jan 2023 22:10:52 +0100 Subject: [PATCH 51/78] add struct value tests --- src/InternPool.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/InternPool.zig b/src/InternPool.zig index f9e79e3..d394802 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2904,6 +2904,36 @@ test "struct type" { try std.testing.expect(struct_type_0 == struct_type_1); } +test "struct value" { + 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 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 }, + .namespace = .none, + .layout = .Auto, + .backing_int_ty = .none, + } }); + + const one_value = try ip.get(gpa, .{ .int_i64_value = 1 }); + const true_value = try ip.get(gpa, .{ .simple = .bool_true }); + + const aggregate_value = try ip.get(gpa, Key{ .aggregate = &.{ one_value, true_value } }); + + try ip.testExpectFmtValue(aggregate_value, struct_type, ".{.foo = 1, .bar = true}"); +} + test "enum type" { const gpa = std.testing.allocator; From 723592e2ed4d5c26986c9ff39246d44adcce6794 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 22 Jan 2023 22:51:54 +0100 Subject: [PATCH 52/78] enable successful interpreter tests --- tests/language_features/comptime_interpreter.zig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index a110e9a..2d5b445 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -47,7 +47,6 @@ test "ComptimeInterpreter - builtins" { } test "ComptimeInterpreter - string literal" { - if (true) return error.SkipZigTest; // TODO const source = \\const foobarbaz = "hello world!"; \\ @@ -158,9 +157,13 @@ test "ComptimeInterpreter - call return struct" { const struct_info = 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 = result.interpreter.ip.indexToKey(struct_info.fields[0].name).bytes; + const bool_type = try result.interpreter.ip.get(allocator, .{ .simple = .bool }); + try std.testing.expectEqual(@as(usize, 1), struct_info.fields.len); - // try std.testing.expectEqualStrings("slay", struct_info.fields[0].name); - // try std.testing.expect(struct_info.fields[0].ty != .none); // TODO check for bool + try std.testing.expectEqualStrings("slay", field_name); + try std.testing.expect(struct_info.fields[0].ty == bool_type); } test "ComptimeInterpreter - call comptime argument" { From d10837dca0732b52164fc95b4033ad56ab527352 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 21:28:15 +0100 Subject: [PATCH 53/78] expand dot completions --- src/analyser/completions.zig | 150 +++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 67 deletions(-) diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 8bee981..725e1da 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -1,6 +1,6 @@ const std = @import("std"); -const InternPool = @import("InternPool.zig"); -const types = @import("lsp.zig"); +const InternPool = @import("../InternPool.zig"); +const types = @import("../lsp.zig"); const Ast = std.zig.Ast; @@ -8,56 +8,74 @@ pub fn dotCompletions( arena: std.mem.Allocator, ip: *InternPool, ty: InternPool.Index, - /// used for extracting doc comments - node: ?Ast.Node.Index, -) error{OutOfMemory}![]types.CompletionItem { - return try dotCompletionsInternal(arena, ip, ty, node); -} - -pub fn dotCompletionsInternal( - arena: std.mem.Allocator, - ip: *InternPool, - ty: InternPool.Index, - node: ?Ast.Node.Index, - follow_one_pointer: bool, + val: InternPool.Index, + node: ?Ast.Node.Index ) error{OutOfMemory}![]types.CompletionItem { _ = node; var completions = std.ArrayListUnmanaged(types.CompletionItem){}; - switch (ip.indexToKey(ty)) { - .simple => {}, - .int_type => {}, + const key = ip.indexToKey(ty); + const inner_key = switch (key) { + .pointer_type => |info| if (info.size == .One) ip.indexToKey(info.elem_type) else key, + else => key, + }; + + switch (inner_key) { + .simple => |simple| switch (simple) { + .type => { + const ty_key = ip.indexToKey(val); + if (ty_key.getNamespace()) { + // TODO lookup in namespace + } + switch (ty_key) { + .error_set_type => |error_set_info| { + for (error_set_info.names) |name| { + const error_name = ip.indexToKey(name).bytes; + try completions.append(arena, .{ + .label = error_name, + .kind = .Constant, + .detail = std.fmt.allocPrint(arena, "error.{s}", .{std.zig.fmtId(error_name)}), + }); + } + }, + .union_type => {}, // TODO + .enum_type => |enum_info|{ + for (enum_info.fields) |field| { + const field_name = ip.indexToKey(field.name).bytes; + try completions.append(arena, .{ + .label = field_name, + .kind = .Constant, + // include field.val? + }); + } + }, + else => {}, + } + }, + else => false, + }, .pointer_type => |pointer_info| { - switch (pointer_info.size) { - .One => { - try completions.append(arena, .{ - .label = "*", - .kind = .Operator, - .detail = std.fmt.allocPrint(arena, "{}", .{pointer_info.elem_type.fmtType(ip)}), - }); - if (follow_one_pointer) { - try dotCompletionsInternal(arena, ip, pointer_info.elem_type, false); - } - }, - .Slice => { - var many_ptr_info = InternPool.Key{ .pointer_type = pointer_info }; - many_ptr_info.pointer_info.size = .Many; + if (pointer_info == .Slice) { + var many_ptr_info = InternPool.Key{ .pointer_type = pointer_info }; + many_ptr_info.pointer_type.size = .Many; - try completions.append(arena, .{ - .label = "ptr", - .kind = .Field, - .detail = std.fmt.allocPrint(arena, "{}", .{many_ptr_info.fmtType(ip)}), - }); - try completions.append(arena, .{ - .label = "len", - .kind = .Field, - .detail = "usize", - }); - }, - .Many, - .C, - => {}, + try completions.append(arena, .{ + .label = "ptr", + .kind = .Field, + .detail = std.fmt.allocPrint(arena, "{}", .{many_ptr_info.fmtType(ip)}), + }); + try completions.append(arena, .{ + .label = "len", + .kind = .Field, + .detail = "usize", + }); + } else if(ip.indexToKey(pointer_info.elem_type) == .array_type) { + try completions.append(arena, .{ + .label = "len", + .kind = .Field, + .detail = "usize", + }); } }, .array_type => |array_info| { @@ -70,14 +88,14 @@ pub fn dotCompletionsInternal( }, .struct_type => |struct_info| { for (struct_info.fields) |field| { + const field_name = ip.indexToKey(field.name).bytes; try completions.append(arena, types.CompletionItem{ - .label = field.name, + .label = field_name, .kind = .Field, // TODO include alignment and comptime .detail = std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), }); } - // TODO namespace }, .optional_type => |optional_info| { try completions.append(arena, .{ @@ -86,31 +104,21 @@ pub fn dotCompletionsInternal( .detail = std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmtType(ip)}), }); }, - .error_union_type => {}, - .error_set_type => |error_set_info| { - for (error_set_info.names) |name| { - try completions.append(arena, .{ - .label = name, - .kind = .Constant, - .detail = std.fmt.allocPrint(arena, "error.{s}", .{name}), - }); - } - }, .enum_type => |enum_info| { for (enum_info.fields) |field| { + const field_name = ip.indexToKey(field.name).bytes; try completions.append(arena, .{ - .label = field.name, + .label = field_name, .kind = .Field, .detail = std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), }); } - // TODO namespace }, - .function_type => {}, .union_type => |union_info| { for (union_info.fields) |field| { + const field_name = ip.indexToKey(field.name).bytes; try completions.append(arena, .{ - .label = field.name, + .label = field_name, .kind = .Field, .detail = if (field.alignment != 0) std.fmt.allocPrint(arena, "align({d}) {}", .{ field.alignment, field.ty.fmtType(ip) }) @@ -118,12 +126,20 @@ pub fn dotCompletionsInternal( std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), }); } - // TODO namespace }, - .tuple_type => { - // TODO + .tuple_type => |tuple_info| { + for (tuple_info.types) |tuple_ty,i| { + try completions.append(arena, .{ + .label = std.fmt.allocPrint(arena, "{d}", .{i}), + .kind = .Field, + .detail = std.fmt.allocPrint(arena, "{}", .{tuple_ty.fmtType(ip)}), + }); + } }, - .vector_type => {}, + .int_type, + .error_union_type, + .function_type, + .vector_type, .anyframe_type => {}, .int_u64_value, @@ -134,12 +150,12 @@ pub fn dotCompletionsInternal( .float_64_value, .float_80_value, .float_128_value, - => {}, + => unreachable, .bytes, .aggregate, .union_value, - => {}, + => unreachable, } return try completions.toOwnedSlice(arena); } From 9dbae6928854d1292b4ac07277407a2b667d9b3b Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 21:28:40 +0100 Subject: [PATCH 54/78] add helper functions to intern pool --- src/InternPool.zig | 93 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index d394802..ab2968c 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -31,6 +31,7 @@ pub const Pointer = packed struct { }; pub const Array = packed struct { + // TODO change to Index len: u32, child: Index, sentinel: Index = .none, @@ -275,17 +276,91 @@ pub const Key = union(enum) { .float_64_value, .float_80_value, .float_128_value, - // .type_value, => unreachable, .bytes, - // .one_pointer, .aggregate, .union_value, => unreachable, }; } + pub fn isType(key: Key) bool { + return switch (key) { + .simple => |simple| switch (simple) { + .f16, + .f32, + .f64, + .f80, + .f128, + .c_longdouble, + .usize, + .isize, + .c_short, + .c_ushort, + .c_int, + .c_uint, + .c_long, + .c_ulong, + .c_longlong, + .c_ulonglong, + .comptime_int, + .comptime_float, + .anyopaque, + .bool, + .void, + .type, + .anyerror, + .noreturn, + .@"anyframe", + .null_type, + .undefined_type, + .enum_literal_type, => true, + + .undefined_value, + .void_value, + .unreachable_value, + .null_value, + .bool_true, + .bool_false, + => false, + }, + + .int_type, + .pointer_type, + .array_type, + .struct_type, + .optional_type, + .error_union_type, + .error_set_type, + .enum_type, + .function_type, + .union_type, + .tuple_type, + .vector_type, + .anyframe_type, => true, + + .int_u64_value, + .int_i64_value, + .int_big_value, + .float_16_value, + .float_32_value, + .float_64_value, + .float_80_value, + .float_128_value, + => false, + + .bytes, + .aggregate, + .union_value, + => false, + }; + } + + pub fn isValue(key: Key) bool { + return !key.isValue(); + } + /// Asserts the type is an integer, enum, error set, packed struct, or vector of one of them. pub fn intInfo(ty: Key, target: std.Target, ip: *const InternPool) Int { var key: Key = ty; @@ -657,7 +732,7 @@ pub const Key = union(enum) { return array_info.child; }, - .struct_type => panicOrElse("TODO", null), + .struct_type => return panicOrElse("TODO", null), .optional_type => |optional_info| { try writer.writeByte('?'); return optional_info.payload_type; @@ -676,7 +751,7 @@ pub const Key = union(enum) { } try writer.writeByte('}'); }, - .enum_type => panicOrElse("TODO", null), + .enum_type => return panicOrElse("TODO", null), .function_type => |function_info| { try writer.writeAll("fn("); @@ -712,7 +787,7 @@ pub const Key = union(enum) { return function_info.return_type; }, - .union_type => panicOrElse("TODO", null), + .union_type => return panicOrElse("TODO", null), .tuple_type => |tuple_info| { try writer.writeAll("tuple{"); for (tuple_info.types) |field_ty, i| { @@ -1179,6 +1254,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { return @intToEnum(Index, ip.items.len - 1); } +pub fn contains(ip: InternPool, key: Key) ?Index { + const adapter: KeyAdapter = .{ .ip = &ip }; + const index = ip.map.getIndexAdapted(key, adapter) orelse return null; + return @intToEnum(Index, index); +} + fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { const T = @TypeOf(extra); comptime if (@sizeOf(T) <= 4) { @@ -2501,7 +2582,7 @@ inline fn panicOrElse(message: []const u8, value: anytype) @TypeOf(value) { if (builtin.is_test or builtin.mode == .Debug) { @panic(message); } - return null; + return value; } // --------------------------------------------- From 497f1e1b234b54dd80bbf4f2be93afee9facae0e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 21:29:24 +0100 Subject: [PATCH 55/78] implement field access in comptime interpreter --- src/ComptimeInterpreter.zig | 245 +++++++++++++++++++++++++++++------- 1 file changed, 197 insertions(+), 48 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 3a7037b..d5e672e 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -161,7 +161,7 @@ pub fn huntItDown( namespace: NamespaceIndex, decl_name: []const u8, options: InterpretOptions, -) error{IdentifierNotFound}!Decl { +) ?Decl { _ = options; var current_namespace = namespace; @@ -174,7 +174,7 @@ pub fn huntItDown( } } - return error.IdentifierNotFound; + return null; } // Might be useful in the future @@ -303,15 +303,20 @@ pub fn interpret( const decl = ast.varDecl(tree, node_idx).?; - if (decl.ast.init_node == 0) - return InterpretResult{ .nothing = {} }; + const type_value = if (decl.ast.type_node != 0) try ((try interpreter.interpret(decl.ast.type_node, namespace, .{})).getValue()) else null; + const init_value = if (decl.ast.init_node != 0) try ((try interpreter.interpret(decl.ast.init_node, namespace, .{})).getValue()) else null; - const init_type_value = try ((try interpreter.interpret(decl.ast.init_node, namespace, .{})).getValue()); + if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; + + if (type_value) |v| { + const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + if (v.ty != type_type) return InterpretResult{ .nothing = {} }; + } try decls.putNoClobber(interpreter.allocator, name, .{ .name = name, - .ty = init_type_value.ty, - .val = init_type_value.val, + .ty = if (type_value) |v| v.val else init_value.?.ty, + .val = if (init_value) |init| init.val else .none, .alignment = 0, // TODO .address_space = .generic, // TODO .is_pub = true, // TODO @@ -377,7 +382,7 @@ pub fn interpret( return InterpretResult{ .nothing = {} }; }, .identifier => { - const value = offsets.nodeToSlice(tree, node_idx); + const identifier = offsets.nodeToSlice(tree, node_idx); const simples = std.ComptimeStringMap(InternPool.Simple, .{ .{ "anyerror", .anyerror }, @@ -411,7 +416,7 @@ pub fn interpret( .{ "void", .void }, }); - if (simples.get(value)) |simple| { + if (simples.get(identifier)) |simple| { return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -420,65 +425,209 @@ pub fn interpret( } }; } - if (value.len >= 2 and (value[0] == 'u' or value[0] == 'i')) blk: { + if (identifier.len >= 2 and (identifier[0] == 'u' or identifier[0] == 'i')) blk: { return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{ - .signedness = if (value[0] == 'u') .unsigned else .signed, - .bits = std.fmt.parseInt(u16, value[1..], 10) catch break :blk, + .signedness = if (identifier[0] == 'u') .unsigned else .signed, + .bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk, } }), } }; } // Logic to find identifiers in accessible scopes - const decl = interpreter.huntItDown(namespace, value, options) catch |err| switch (err) { - error.IdentifierNotFound => |e| { - try interpreter.recordError( - node_idx, - "undeclared_identifier", - "use of undeclared identifier '{s}'", - .{value}, - ); - return e; - }, - }; + if (interpreter.huntItDown(namespace, identifier, options)) |decl| { + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = decl.ty, + .val = decl.val, + } }; + } - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = node_idx, - .ty = decl.ty, - .val = decl.val, - } }; + try interpreter.recordError( + node_idx, + "undeclared_identifier", + "use of undeclared identifier '{s}'", + .{identifier}, + ); + return error.IdentifierNotFound; }, .field_access => { if (data[node_idx].rhs == 0) return error.CriticalAstFailure; - const rhs_str = tree.tokenSlice(data[node_idx].rhs); + const field_name = tree.tokenSlice(data[node_idx].rhs); var ir = try interpreter.interpret(data[node_idx].lhs, namespace, options); var irv = try ir.getValue(); - const lhs_namespace = interpreter.ip.indexToKey(irv.val).getNamespace(); - - var scope_sub_decl = irv.interpreter.huntItDown(lhs_namespace, rhs_str, options) catch |err| switch (err) { - error.IdentifierNotFound => |e| { - try interpreter.recordError( - node_idx, - "undeclared_identifier", - "`{}` has no member '{s}'", - .{ irv.ty.fmtType(interpreter.ip), rhs_str }, - ); - return e; - }, + const lhs = interpreter.ip.indexToKey(irv.ty); + const inner_lhs = switch (lhs) { + .pointer_type => |info| if (info.size == .One) interpreter.ip.indexToKey(info.elem_type) else lhs, + else => lhs, }; - return InterpretResult{ .value = Value{ - .interpreter = interpreter, - .node_idx = data[node_idx].rhs, - .ty = scope_sub_decl.ty, - .val = scope_sub_decl.val, - } }; + const can_have_fields: bool = switch (inner_lhs) { + .simple => |simple| switch (simple) { + .type => blk: { + const ty_key = interpreter.ip.indexToKey(irv.val); + if (interpreter.huntItDown(ty_key.getNamespace(), field_name, options)) |decl| { + std.debug.print("here {s}: {}\n", .{field_name, decl.ty.fmtType(interpreter.ip)}); + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = node_idx, + .ty = decl.ty, + .val = decl.val, + } }; + } + + switch (ty_key) { + .error_set_type => |error_set_info| { // TODO + _ = error_set_info; + }, + .union_type => {}, // TODO + .enum_type => |enum_info| { // TODO + if (interpreter.ip.contains(IPKey{ .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 + }, + }; + } + } + }, + else => break :blk false, + } + break :blk true; + }, + else => false, + }, + .pointer_type => |pointer_info| blk: { + if (pointer_info.size == .Slice) { + if (std.mem.eql(u8, field_name, "ptr")) { + var many_ptr_info = InternPool.Key{ .pointer_type = pointer_info }; + many_ptr_info.pointer_type.size = .Many; + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = try interpreter.ip.get(interpreter.allocator, many_ptr_info), + .val = .none, // TODO resolve ptr of Slice + }, + }; + } else if (std.mem.eql(u8, field_name, "len")) { + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }), + .val = .none, // TODO resolve length of Slice + }, + }; + } + } else if (interpreter.ip.indexToKey(pointer_info.elem_type) == .array_type) { + if (std.mem.eql(u8, field_name, "len")) { + return InterpretResult{ + .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }), + .val = .none, // TODO resolve length of Slice + }, + }; + } + } + break :blk true; + }, + .array_type => |array_info| blk: { + const len_value = try interpreter.ip.get(interpreter.allocator, .{ .int_u64_value = array_info.len }); + + if (std.mem.eql(u8, field_name, "len")) { + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .comptime_int }), + .val = len_value, + } }; + } + break :blk true; + }, + .optional_type => |optional_info| blk: { + if (!std.mem.eql(u8, field_name, "?")) break :blk false; + const null_value = try interpreter.ip.get(interpreter.allocator, .{ .simple = .null_value }); + if (irv.val == null_value) { + try interpreter.recordError( + node_idx, + "null_unwrap", + "tried to unwrap optional of type `{}` which was null", + .{irv.ty.fmtType(interpreter.ip)}, + ); + return error.InvalidOperation; + } else { + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = optional_info.payload_type, + .val = irv.val, + } }; + } + }, + .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(IPKey{ .bytes = field_name }) orelse break :blk true; + + for (struct_info.fields) |field, i| { + std.debug.print("field {} {}\n", .{field.ty, field.ty.fmtType(interpreter.ip)}); + if (field.name != field_name_index) continue; + 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]; + }; + + return InterpretResult{ .value = Value{ + .interpreter = interpreter, + .node_idx = data[node_idx].rhs, + .ty = field.ty, + .val = val, + } }; + } + break :blk true; + }, + .enum_type => |enum_info| blk: { // TODO + _ = enum_info; + break :blk true; + }, + .union_type => |union_info| blk: { // TODO + _ = union_info; + break :blk true; + }, + else => false, + }; + + if (can_have_fields) { + try interpreter.recordError( + node_idx, + "undeclared_identifier", + "`{}` has no member '{s}'", + .{ irv.ty.fmtType(interpreter.ip), field_name }, + ); + } else { + try interpreter.recordError( + node_idx, + "invalid_field_access", + "`{}` does not support field access", + .{irv.ty.fmtType(interpreter.ip)}, + ); + } + return error.InvalidOperation; }, .grouped_expression => { return try interpreter.interpret(data[node_idx].lhs, namespace, options); From f2b133c4286e9b9aaa7465e37e17052001ecaa5e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 21:29:51 +0100 Subject: [PATCH 56/78] update tests --- .../comptime_interpreter.zig | 386 ++++++++++-------- 1 file changed, 215 insertions(+), 171 deletions(-) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 2d5b445..bc9cfba 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -14,79 +14,92 @@ const offsets = zls.offsets; const allocator: std.mem.Allocator = std.testing.allocator; test "ComptimeInterpreter - primitive types" { - try testExprCheck("true", .{ .simple = .bool }, .{ .simple = .bool_true }); - try testExprCheck("false", .{ .simple = .bool }, .{ .simple = .bool_false }); - try testExprCheck("5", .{ .simple = .comptime_int }, .{ .int_u64_value = 5 }); - // TODO try testExprCheck("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 }); - try testExprCheck("3.0", .{ .simple = .comptime_float }, null); + try testExpr("true", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr("false", .{ .simple = .bool }, .{ .simple = .bool_false }); + try testExpr("5", .{ .simple = .comptime_int }, .{ .int_u64_value = 5 }); + // TODO try testExpr("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 }); + try testExpr("3.0", .{ .simple = .comptime_float }, null); - try testExprCheck("null", .{ .simple = .null_type }, .{ .simple = .null_value }); - try testExprCheck("void", .{ .simple = .type }, .{ .simple = .void }); - try testExprCheck("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value }); - try testExprCheck("noreturn", .{ .simple = .type }, .{ .simple = .noreturn }); + try testExpr("null", .{ .simple = .null_type }, .{ .simple = .null_value }); + try testExpr("void", .{ .simple = .type }, .{ .simple = .void }); + try testExpr("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value }); + try testExpr("noreturn", .{ .simple = .type }, .{ .simple = .noreturn }); } test "ComptimeInterpreter - expressions" { if (true) return error.SkipZigTest; // TODO - try testExprCheck("5 + 3", .{ .simple = .comptime_int }, .{ .int_u64_value = 8 }); - try testExprCheck("5.2 + 4.2", .{ .simple = .comptime_float }, null); + try testExpr("5 + 3", .{ .simple = .comptime_int }, .{ .int_u64_value = 8 }); + try testExpr("5.2 + 4.2", .{ .simple = .comptime_float }, null); - try testExprCheck("3 == 3", .{ .simple = .bool }, .{ .simple = .bool_true }); - try testExprCheck("5.2 == 2.1", .{ .simple = .bool }, .{ .simple = .bool_false }); + try testExpr("3 == 3", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr("5.2 == 2.1", .{ .simple = .bool }, .{ .simple = .bool_false }); - try testExprCheck("@as(?bool, null) orelse true", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr("@as(?bool, null) orelse true", .{ .simple = .bool }, .{ .simple = .bool_true }); } test "ComptimeInterpreter - builtins" { if (true) return error.SkipZigTest; // TODO - try testExprCheck("@as(bool, true)", .{ .simple = .bool }, .{ .simple = .bool_true }); - try testExprCheck("@as(u32, 3)", .{ .int_type = .{ + try testExpr("@as(bool, true)", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr("@as(u32, 3)", .{ .int_type = .{ .signedness = .unsigned, .bits = 32, } }, .{ .int_u64_value = 3 }); } test "ComptimeInterpreter - string literal" { - const source = + var context = try Context.init( \\const foobarbaz = "hello world!"; \\ - ; - - var result = try testInterpret(source, 1); - defer result.deinit(); + ); + defer context.deinit(); + const result = try context.interpret(context.findVar("foobarbaz")); try std.testing.expect(result.ty == .pointer_type); - try std.testing.expect(result.val == .bytes); - try std.testing.expectEqualStrings("hello world!", result.val.bytes); + try std.testing.expectEqualStrings("hello world!", result.val.?.bytes); } test "ComptimeInterpreter - labeled block" { - try testExprCheck( + try testExpr( \\blk: { \\ break :blk true; \\} , .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr( + \\blk: { + \\ break :blk 3; + \\} + , .{ .simple = .comptime_int }, .{ .int_u64_value = 3 }); } test "ComptimeInterpreter - if" { - try testExprCheck( + try testExpr( \\blk: { \\ break :blk if (true) true else false; \\} , .{ .simple = .bool }, .{ .simple = .bool_true }); - try testExprCheck( + try testExpr( \\blk: { \\ break :blk if (false) true else false; \\} , .{ .simple = .bool }, .{ .simple = .bool_false }); } -test "ComptimeInterpreter - field access" { - if (true) return error.SkipZigTest; // TODO - try testExprCheck( +test "ComptimeInterpreter - variable lookup" { + try testExpr( \\blk: { - \\ const foo = struct {alpha: u32, beta: bool} = undefined; + \\ var foo = 1; + \\ var bar = 2; + \\ var baz = 3; + \\ break :blk bar; + \\} + , .{ .simple = .comptime_int }, .{ .int_u64_value = 2 }); +} + +test "ComptimeInterpreter - field access" { + try testExpr( + \\blk: { + \\ const foo: struct {alpha: u64, beta: bool} = undefined; \\ break :blk foo.beta; \\} , .{ .simple = .bool }, null); @@ -94,19 +107,19 @@ test "ComptimeInterpreter - field access" { test "ComptimeInterpreter - pointer operations" { if (true) return error.SkipZigTest; // TODO - try testExprCheck( + try testExpr( \\blk: { \\ const foo: []const u8 = ""; \\ break :blk foo.len; \\} , .{ .simple = .usize }, .{ .bytes = "" }); - try testExprCheck( + try testExpr( \\blk: { \\ const foo = true; \\ break :blk &foo; \\} , @panic("TODO"), .{ .simple = .bool_true }); - try testExprCheck( + try testExpr( \\blk: { \\ const foo = true; \\ const bar = &foo; @@ -116,25 +129,25 @@ test "ComptimeInterpreter - pointer operations" { } test "ComptimeInterpreter - call return primitive type" { - try testCallCheck( + try testCall( \\pub fn Foo() type { \\ return bool; \\} , &.{}, .{ .simple = .bool }); - try testCallCheck( + try testCall( \\pub fn Foo() type { \\ return u32; \\} , &.{}, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - try testCallCheck( + try testCall( \\pub fn Foo() type { \\ return i128; \\} , &.{}, .{ .int_type = .{ .signedness = .signed, .bits = 128 } }); - try testCallCheck( + try testCall( \\pub fn Foo() type { \\ const alpha = i128; \\ return alpha; @@ -143,23 +156,25 @@ test "ComptimeInterpreter - call return primitive type" { } test "ComptimeInterpreter - call return struct" { - var result = try testCall( + var context = try Context.init( \\pub fn Foo() type { \\ return struct { \\ slay: bool, \\ var abc = 123; \\ }; \\} - , &.{}); - defer result.deinit(); + ); + defer context.deinit(); + const result = try context.call(context.findFn("Foo"), &.{}); + 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 = 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 = result.interpreter.ip.indexToKey(struct_info.fields[0].name).bytes; - const bool_type = try result.interpreter.ip.get(allocator, .{ .simple = .bool }); + 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); @@ -167,157 +182,173 @@ test "ComptimeInterpreter - call return struct" { } test "ComptimeInterpreter - call comptime argument" { - const source = + var context = try Context.init( \\pub fn Foo(comptime my_arg: bool) type { \\ var abc = z: {break :z if (!my_arg) 123 else 0;}; \\ if (abc == 123) return u69; \\ return u8; \\} - ; + ); + defer context.deinit(); - var result1 = try testCall(source, &.{ - Value{ - .ty = .{ .simple = .bool }, - .val = .{ .simple = .bool_true }, - }, - }); - defer result1.deinit(); + const result1 = try context.call(context.findFn("Foo"), &.{KV{ + .ty = .{ .simple = .bool }, + .val = .{ .simple = .bool_true }, + }}); try std.testing.expect(result1.ty == .simple); try std.testing.expect(result1.ty.simple == .type); - try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val); + try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val.?); - var result2 = try testCall(source, &.{ - Value{ - .ty = .{ .simple = .bool }, - .val = .{ .simple = .bool_false }, - }, - }); - defer result2.deinit(); + var result2 = try context.call(context.findFn("Foo"), &.{KV{ + .ty = .{ .simple = .bool }, + .val = .{ .simple = .bool_false }, + }}); try std.testing.expect(result2.ty == .simple); try std.testing.expect(result2.ty.simple == .type); - try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val); + try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val.?); } // // Helper functions // -const Result = struct { - interpreter: ComptimeInterpreter, +const KV = struct { ty: Key, - val: Key, - - pub fn deinit(self: *Result) void { - self.interpreter.deinit(); - } + val: ?Key, }; -const Value = struct { - ty: Key, - val: Key, -}; +const Context = struct { + config: *zls.Config, + document_store: *zls.DocumentStore, + interpreter: *ComptimeInterpreter, -fn testCall(source: []const u8, arguments: []const Value) !Result { - var config = zls.Config{}; - var doc_store = zls.DocumentStore{ - .allocator = allocator, - .config = &config, - }; - defer doc_store.deinit(); + pub fn init(source: []const u8) !Context { + var config = try allocator.create(zls.Config); + errdefer allocator.destroy(config); - const test_uri: []const u8 = switch (builtin.os.tag) { - .windows => "file:///C:\\test.zig", - else => "file:///test.zig", - }; + var document_store = try allocator.create(zls.DocumentStore); + errdefer allocator.destroy(document_store); - const handle = try doc_store.openDocument(test_uri, source); + var interpreter = try allocator.create(ComptimeInterpreter); + errdefer allocator.destroy(interpreter); - var interpreter = ComptimeInterpreter{ - .allocator = allocator, - .document_store = &doc_store, - .uri = handle.uri, - }; - errdefer interpreter.deinit(); + config.* = .{}; + document_store.* = .{ + .allocator = allocator, + .config = config, + }; + errdefer document_store.deinit(); - _ = try interpretReportErrors(&interpreter, 0, .none); + const test_uri: []const u8 = switch (builtin.os.tag) { + .windows => "file:///C:\\test.zig", + else => "file:///test.zig", + }; - var args = try allocator.alloc(ComptimeInterpreter.Value, arguments.len); - defer allocator.free(args); + const handle = try document_store.openDocument(test_uri, source); - for (arguments) |argument, i| { - args[i] = .{ - .interpreter = &interpreter, - .node_idx = 0, - .ty = try interpreter.ip.get(interpreter.allocator, argument.ty), - .val = try interpreter.ip.get(interpreter.allocator, argument.val), + interpreter.* = .{ + .allocator = allocator, + .document_store = document_store, + .uri = handle.uri, + }; + errdefer interpreter.deinit(); + + _ = try interpretReportErrors(interpreter, 0, .none); + + return .{ + .config = config, + .document_store = document_store, + .interpreter = interpreter, }; } - const func_node = for (handle.tree.nodes.items(.tag)) |tag, i| { - if (tag == .fn_decl) break @intCast(Ast.Node.Index, i); - } else unreachable; + pub fn deinit(self: *Context) void { + self.interpreter.deinit(); + self.document_store.deinit(); - const call_result = try interpreter.call(.none, func_node, args, .{}); + allocator.destroy(self.config); + allocator.destroy(self.document_store); + allocator.destroy(self.interpreter); + } - return Result{ - .interpreter = interpreter, - .ty = interpreter.ip.indexToKey(call_result.result.value.ty), - .val = interpreter.ip.indexToKey(call_result.result.value.val), - }; -} + pub fn call(self: *Context, func_node: Ast.Node.Index, arguments: []const KV) !KV { + var args = try allocator.alloc(ComptimeInterpreter.Value, arguments.len); + defer allocator.free(args); -fn testCallCheck( + for (arguments) |argument, i| { + args[i] = .{ + .interpreter = self.interpreter, + .node_idx = 0, + .ty = try self.interpreter.ip.get(self.interpreter.allocator, argument.ty), + .val = if (argument.val) |val| try self.interpreter.ip.get(self.interpreter.allocator, val) else .none, + }; + } + + const namespace = @intToEnum(ComptimeInterpreter.NamespaceIndex, 0); // root namespace + const result = (try self.interpreter.call(namespace, func_node, args, .{})).result; + + try std.testing.expect(result == .value); + try std.testing.expect(result.value.ty != .none); + + return KV{ + .ty = self.interpreter.ip.indexToKey(result.value.ty), + .val = if (result.value.val == .none) null else self.interpreter.ip.indexToKey(result.value.val), + }; + } + + pub fn interpret(self: *Context, node: Ast.Node.Index) !KV { + const namespace = @intToEnum(ComptimeInterpreter.NamespaceIndex, 0); // root namespace + const result = try (try self.interpreter.interpret(node, namespace, .{})).getValue(); + + try std.testing.expect(result.ty != .none); + + return KV{ + .ty = self.interpreter.ip.indexToKey(result.ty), + .val = if (result.val == .none) null else self.interpreter.ip.indexToKey(result.val), + }; + } + + pub fn findFn(self: Context, name: []const u8) Ast.Node.Index { + const handle = self.interpreter.getHandle(); + for (handle.tree.nodes.items(.tag)) |tag, i| { + if (tag != .fn_decl) continue; + const node = @intCast(Ast.Node.Index, i); + var buffer: [1]Ast.Node.Index = undefined; + const fn_decl = handle.tree.fullFnProto(&buffer, node).?; + const fn_name = offsets.tokenToSlice(handle.tree, fn_decl.name_token.?); + if (std.mem.eql(u8, fn_name, name)) return node; + } + std.debug.panic("failed to find function with name '{s}'", .{name}); + } + + pub fn findVar(self: Context, name: []const u8) Ast.Node.Index { + const handle = self.interpreter.getHandle(); + var node: Ast.Node.Index = 0; + while (node < handle.tree.nodes.len) : (node += 1) { + const var_decl = handle.tree.fullVarDecl(node) orelse continue; + const name_token = var_decl.ast.mut_token + 1; + const var_name = offsets.tokenToSlice(handle.tree, name_token); + if (std.mem.eql(u8, var_name, name)) return var_decl.ast.init_node; + } + std.debug.panic("failed to find var declaration with name '{s}'", .{name}); + } +}; + +fn testCall( source: []const u8, - arguments: []const Value, + arguments: []const KV, expected_ty: Key, ) !void { - var result = try testCall(source, arguments); - defer result.deinit(); - try std.testing.expect(result.ty == .simple); - try std.testing.expect(result.ty.simple == .type); - if (!expected_ty.eql(result.val)) { - std.debug.print("expected type `{}`, found `{}`\n", .{ expected_ty.fmtType(result.interpreter.ip), result.val.fmtType(result.interpreter.ip) }); - return error.TestExpectedEqual; - } + var context = try Context.init(source); + defer context.deinit(); + + const result = try context.call(context.findFn("Foo"), arguments); + + try expectEqualKey(context.interpreter.ip, Key{ .simple = .type }, result.ty); + try expectEqualKey(context.interpreter.ip, expected_ty, result.val); } -fn testInterpret(source: []const u8, node_idx: Ast.Node.Index) !Result { - var config = zls.Config{}; - var doc_store = zls.DocumentStore{ - .allocator = allocator, - .config = &config, - }; - defer doc_store.deinit(); - - const test_uri: []const u8 = switch (builtin.os.tag) { - .windows => "file:///C:\\test.zig", - else => "file:///test.zig", - }; - - const handle = try doc_store.openDocument(test_uri, source); - - var interpreter = ComptimeInterpreter{ - .allocator = allocator, - .document_store = &doc_store, - .uri = handle.uri, - }; - errdefer interpreter.deinit(); - - _ = try interpretReportErrors(&interpreter, 0, .none); - - const result = try interpreter.interpret(node_idx, .none, .{}); - - try std.testing.expect(result.value.ty != .none); - try std.testing.expect(result.value.val != .none); - - return Result{ - .interpreter = interpreter, - .ty = interpreter.ip.indexToKey(result.value.ty), - .val = interpreter.ip.indexToKey(result.value.val), - }; -} - -fn testExprCheck( +fn testExpr( expr: []const u8, expected_ty: Key, expected_val: ?Key, @@ -327,26 +358,39 @@ fn testExprCheck( , .{expr}); defer allocator.free(source); - var result = try testInterpret(source, 1); - defer result.deinit(); - var ip: *InternPool = &result.interpreter.ip; + var context = try Context.init(source); + defer context.deinit(); - if (!expected_ty.eql(result.ty)) { - std.debug.print("expected type `{}`, found `{}`\n", .{ expected_ty.fmtType(ip.*), result.ty.fmtType(ip.*) }); - return error.TestExpectedEqual; + const result = try context.interpret(context.findVar("foobarbaz")); + + try expectEqualKey(context.interpreter.ip, expected_ty, result.ty); + if (expected_val) |expected| { + try expectEqualKey(context.interpreter.ip, expected, result.val); } +} - if (expected_val) |expected_value| { - if (!expected_value.eql(result.val)) { - const expected_ty_index = try ip.get(allocator, expected_ty); - const actual_ty_index = try ip.get(allocator, result.ty); - std.debug.print("expected value `{}`, found `{}`\n", .{ - expected_value.fmtValue(expected_ty_index, ip.*), - result.val.fmtValue(actual_ty_index, ip.*), - }); - return error.TestExpectedEqual; +/// TODO refactor this code +fn expectEqualKey(ip: InternPool, expected: Key, actual: ?Key) !void { + if (actual) |actual_key| { + if (expected.eql(actual_key)) return; + + if (expected.isType() and actual_key.isType()) { + std.debug.print("expected type `{}`, found type `{}`\n", .{ expected.fmtType(ip), actual_key.fmtType(ip) }); + } else if (expected.isType()) { + std.debug.print("expected type `{}`, found value ({})\n", .{ expected.fmtType(ip), actual_key }); + } else if (actual_key.isType()) { + std.debug.print("expected value ({}), found type `{}`\n", .{ expected, actual_key.fmtType(ip) }); + } else { + std.debug.print("expected value ({}), found value ({})\n", .{ expected, actual_key }); // TODO print value + } + } else { + if (expected.isType()) { + std.debug.print("expected type `{}`, found null\n", .{expected.fmtType(ip)}); + } else { + std.debug.print("expected value ({}), found null\n", .{expected}); } } + return error.TestExpectedEqual; } fn interpretReportErrors( From 4b7a434febd412dde6e1d20f37fbe84e46598259 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 22:21:47 +0100 Subject: [PATCH 57/78] small refactor --- src/ComptimeInterpreter.zig | 103 +++++++++++++++++------------------- src/InternPool.zig | 9 ++++ 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index d5e672e..bfb003a 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -13,8 +13,8 @@ const offsets = @import("offsets.zig"); const DocumentStore = @import("DocumentStore.zig"); pub const InternPool = @import("InternPool.zig"); -pub const IPIndex = InternPool.Index; -pub const IPKey = InternPool.Key; +pub const Index = InternPool.Index; +pub const Key = InternPool.Key; pub const ComptimeInterpreter = @This(); const log = std.log.scoped(.comptime_interpreter); @@ -31,7 +31,7 @@ errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{}, pub fn getHandle(interpreter: *ComptimeInterpreter) *const DocumentStore.Handle { // This interpreter is loaded from a known-valid handle so a valid handle must exist - return interpreter.document_store.getOrLoadHandle(interpreter.uri).?; + return interpreter.document_store.getHandle(interpreter.uri).?; } pub const InterpreterError = struct { @@ -39,7 +39,6 @@ pub const InterpreterError = struct { message: []const u8, }; -/// `message` must be allocated with interpreter allocator pub fn recordError( interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, @@ -73,21 +72,21 @@ pub const Type = struct { interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - ty: IPIndex, + ty: Index, }; pub const Value = struct { interpreter: *ComptimeInterpreter, node_idx: Ast.Node.Index, - ty: IPIndex, - val: IPIndex, + ty: Index, + val: Index, }; pub const Decl = struct { name: []const u8, - ty: IPIndex, - val: IPIndex, + ty: Index, + val: Index, alignment: u16, address_space: std.builtin.AddressSpace, is_pub: bool, @@ -103,7 +102,7 @@ pub const Namespace = struct { parent: NamespaceIndex, node_idx: Ast.Node.Index, /// Will be a struct, enum, union, opaque or .none - ty: IPIndex, + ty: Index, decls: std.StringArrayHashMapUnmanaged(Decl) = .{}, usingnamespaces: std.ArrayListUnmanaged(NamespaceIndex) = .{}, @@ -219,7 +218,7 @@ pub fn interpret( // .tagged_union_enum_tag_trailing, .root, => { - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); try interpreter.namespaces.append(interpreter.allocator, .{ .parent = namespace, @@ -242,7 +241,7 @@ pub fn interpret( var init_type_value = try (try interpreter.interpret(container_field.ast.type_expr, container_namespace, .{})).getValue(); var default_value = if (container_field.ast.value_expr == 0) - IPIndex.none + Index.none else (try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty @@ -269,7 +268,7 @@ pub fn interpret( try fields.append(interpreter.allocator, field); } - const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{ + const struct_type = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = .{ .fields = fields.items, .namespace = namespace, @@ -309,7 +308,7 @@ pub fn interpret( if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; if (type_value) |v| { - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); if (v.ty != type_type) return InterpretResult{ .nothing = {} }; } @@ -420,8 +419,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = simple.toType() }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = simple }), + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = simple.toType() }), + .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = simple }), } }; } @@ -429,8 +428,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{ + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), + .val = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{ .signedness = if (identifier[0] == 'u') .unsigned else .signed, .bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk, } }), @@ -473,7 +472,6 @@ pub fn interpret( .type => blk: { const ty_key = interpreter.ip.indexToKey(irv.val); if (interpreter.huntItDown(ty_key.getNamespace(), field_name, options)) |decl| { - std.debug.print("here {s}: {}\n", .{field_name, decl.ty.fmtType(interpreter.ip)}); return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -488,7 +486,7 @@ pub fn interpret( }, .union_type => {}, // TODO .enum_type => |enum_info| { // TODO - if (interpreter.ip.contains(IPKey{ .bytes = field_name })) |field_name_index| { + 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{ @@ -580,10 +578,9 @@ 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(IPKey{ .bytes = field_name }) orelse break :blk true; + const field_name_index = interpreter.ip.contains(Key{ .bytes = field_name }) orelse break :blk true; for (struct_info.fields) |field, i| { - std.debug.print("field {} {}\n", .{field.ty, field.ty.fmtType(interpreter.ip)}); if (field.name != field_name_index) continue; const val = found_val: { if (irv.val == .none) break :found_val .none; @@ -653,8 +650,8 @@ pub fn interpret( // if (options.observe_values) { const ir = try interpreter.interpret(if_info.ast.cond_expr, namespace, options); - const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); - const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); + const false_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_false }); + const true_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_true }); const condition = (try ir.getValue()).val; std.debug.assert(condition == false_value or condition == true_value); @@ -673,8 +670,8 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (a.value.val == b.value.val) .bool_true else .bool_false }), // TODO eql function required? + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (a.value.val == b.value.val) .bool_true else .bool_false }), // TODO eql function required? }, }; }, @@ -684,19 +681,19 @@ pub fn interpret( if (nl == .failure) return error.CriticalAstFailure; - const number_type = try interpreter.ip.get(interpreter.allocator, IPKey{ + const number_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (nl == .float) .comptime_float else .comptime_int, }); const value = try interpreter.ip.get( interpreter.allocator, switch (nl) { - .float => IPKey{ + .float => Key{ .float_128_value = try std.fmt.parseFloat(f128, s), }, - .int => if (s[0] == '-') IPKey{ + .int => if (s[0] == '-') Key{ .int_i64_value = try std.fmt.parseInt(i64, s, 0), - } else IPKey{ + } else Key{ .int_u64_value = try std.fmt.parseInt(u64, s, 0), }, .big_int => |base| blk: { @@ -704,7 +701,7 @@ pub fn interpret( defer big_int.deinit(); const prefix_length: usize = if (base != .decimal) 2 else 0; try big_int.setString(@enumToInt(base), s[prefix_length..]); - break :blk IPKey{ .int_big_value = big_int.toConst() }; + break :blk Key{ .int_big_value = big_int.toConst() }; }, .failure => return error.CriticalAstFailure, }, @@ -812,13 +809,13 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = .{ .fields = &.{}, .namespace = .none, .layout = .Auto, .backing_int_ty = .none, } }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .undefined_value }), + .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .undefined_value }), } }; } @@ -832,7 +829,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), .val = .none, // TODO }, }; @@ -845,7 +842,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }), + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), .val = value.ty, } }; } @@ -856,7 +853,7 @@ pub fn interpret( const value = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue(); - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); 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 @@ -872,8 +869,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = if (has_decl) .bool_true else .bool_false }), + .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), + .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (has_decl) .bool_true else .bool_false }), } }; } @@ -883,7 +880,7 @@ pub fn interpret( const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const value = try (try interpreter.interpret(params[1], namespace, options)).getValue(); - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); if (as_type.ty != type_type) return error.InvalidBuiltin; @@ -903,14 +900,14 @@ pub fn interpret( .string_literal => { const str = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1]; - const string_literal_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{ - .elem_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .array_type = .{ - .child = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_type = .{ + const string_literal_type = try interpreter.ip.get(interpreter.allocator, Key{ .pointer_type = .{ + .elem_type = try interpreter.ip.get(interpreter.allocator, Key{ .array_type = .{ + .child = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{ .signedness = .unsigned, .bits = 8, } }), - .len = @intCast(u32, str.len), - .sentinel = try interpreter.ip.get(interpreter.allocator, IPKey{ .int_u64_value = 0 }), + .len = @intCast(u64, str.len), + .sentinel = try interpreter.ip.get(interpreter.allocator, Key{ .int_u64_value = 0 }), } }), .sentinel = .none, .alignment = 0, @@ -925,7 +922,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = string_literal_type, - .val = try interpreter.ip.get(interpreter.allocator, IPKey{ .bytes = str }), + .val = try interpreter.ip.get(interpreter.allocator, Key{ .bytes = str }), } }; }, // TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8) @@ -943,14 +940,14 @@ pub fn interpret( // TODO: Resolve function type - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ + const function_type = try interpreter.ip.get(interpreter.allocator, Key{ .function_type = .{ .calling_convention = .Unspecified, .alignment = 0, .is_generic = false, .is_var_args = false, - .return_type = IPIndex.none, + .return_type = Index.none, .args = &.{}, } }); @@ -1028,7 +1025,7 @@ pub fn interpret( }, .bool_not => { const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); - const bool_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool }); + const bool_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }); const value = try result.getValue(); if (value.ty != bool_type) { @@ -1041,8 +1038,8 @@ pub fn interpret( return error.InvalidOperation; } - const false_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_false }); - const true_value = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .bool_true }); + const false_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_false }); + const true_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_true }); std.debug.assert(value.val == false_value or value.val == true_value); return InterpretResult{ .value = .{ @@ -1059,7 +1056,7 @@ pub fn interpret( const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const value = (try result.getValue()); - const pointer_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .pointer_type = .{ + const pointer_type = try interpreter.ip.get(interpreter.allocator, Key{ .pointer_type = .{ .elem_type = value.ty, .sentinel = .none, .alignment = 0, @@ -1134,7 +1131,7 @@ pub fn call( }); const fn_namespace = @intToEnum(NamespaceIndex, interpreter.namespaces.len - 1); - const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type }); + const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); var arg_it = proto.iterate(&tree); var arg_index: usize = 0; diff --git a/src/InternPool.zig b/src/InternPool.zig index ab2968c..fe94d20 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -44,6 +44,7 @@ pub const Struct = struct { backing_int_ty: Index, pub const Field = packed struct { + /// guaranteed to be .bytes name: Index, ty: Index, default_value: Index = .none, @@ -62,6 +63,7 @@ pub const ErrorUnion = packed struct { }; pub const ErrorSet = struct { + /// every element is guaranteed to be .bytes names: []const Index, }; @@ -72,6 +74,7 @@ pub const Enum = struct { tag_type_infered: bool, pub const Field = packed struct { + /// guaranteed to be .bytes name: Index, val: Index, }; @@ -79,8 +82,11 @@ pub const Enum = struct { pub const Function = struct { args: []const Index, + /// zig only lets the first 32 arguments be `comptime` args_is_comptime: std.StaticBitSet(32) = std.StaticBitSet(32).initEmpty(), + /// zig only lets the first 32 arguments be generic args_is_generic: std.StaticBitSet(32) = std.StaticBitSet(32).initEmpty(), + /// zig only lets the first 32 arguments be `noalias` args_is_noalias: std.StaticBitSet(32) = std.StaticBitSet(32).initEmpty(), return_type: Index, alignment: u16 = 0, @@ -96,6 +102,7 @@ pub const Union = struct { layout: std.builtin.Type.ContainerLayout = .Auto, pub const Field = packed struct { + /// guaranteed to be .bytes name: Index, ty: Index, alignment: u16, @@ -1226,6 +1233,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { }; } +/// TODO rename to getOrPut pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { const adapter: KeyAdapter = .{ .ip = ip }; const gop = try ip.map.getOrPutAdapted(gpa, key, adapter); @@ -1254,6 +1262,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { return @intToEnum(Index, ip.items.len - 1); } +/// TODO rename to get pub fn contains(ip: InternPool, key: Key) ?Index { const adapter: KeyAdapter = .{ .ip = &ip }; const index = ip.map.getIndexAdapted(key, adapter) orelse return null; From 829f4aa9c45521385af145dca8bf57816f5d8a64 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 22:23:01 +0100 Subject: [PATCH 58/78] use `analyser/completions.zig` for completing comptime interpreter --- src/InternPool.zig | 3 +-- src/Server.zig | 23 ++++------------------- src/analyser/completions.zig | 31 +++++++++++++++++-------------- src/analysis.zig | 19 ++++++------------- 4 files changed, 28 insertions(+), 48 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index fe94d20..0d7313c 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -31,8 +31,7 @@ pub const Pointer = packed struct { }; pub const Array = packed struct { - // TODO change to Index - len: u32, + len: u64, child: Index, sentinel: Index = .none, }; diff --git a/src/Server.zig b/src/Server.zig index ca71f60..876ae28 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -20,6 +20,7 @@ const tracy = @import("tracy.zig"); const uri_utils = @import("uri.zig"); const diff = @import("diff.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); +const analyser_completions = @import("analyser/completions.zig"); const data = @import("data/data.zig"); const snipped_data = @import("data/snippets.zig"); @@ -595,24 +596,8 @@ fn typeToCompletion( ), .primitive, .array_index => {}, .@"comptime" => |co| { - const key = co.interpreter.ip.indexToKey(co.type.ty); - - switch (key) { - .struct_type => |struct_info| { - for (struct_info.fields) |field| { - const field_name = co.interpreter.ip.indexToKey(field.name).bytes; - try list.append(allocator, .{ - .label = field_name, - .kind = .Field, - .insertText = field_name, - .insertTextFormat = .PlainText, - }); - } - - // TODO declaration completion - }, - else => {}, - } + const items = try analyser_completions.dotCompletions(allocator, &co.interpreter.ip, co.value.ty, co.value.val, co.value.node_idx); + try list.appendSlice(allocator, items); }, } } @@ -934,7 +919,7 @@ fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutO const resolved_type_str = if (resolved_type) |rt| if (rt.type.is_type_val) switch (rt.type.data) { - .@"comptime" => |*co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.type.ty.fmtType(co.interpreter.ip)}), + .@"comptime" => |co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.value.ty.fmtType(co.interpreter.ip)}), else => "type", } else switch (rt.type.data) { // TODO: Investigate random weird numbers like 897 that cause index of bounds .pointer, diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 725e1da..1f74896 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -4,6 +4,8 @@ const types = @import("../lsp.zig"); const Ast = std.zig.Ast; + +/// TODO use std.ArrayListUnmanaged instead of returning a slice pub fn dotCompletions( arena: std.mem.Allocator, ip: *InternPool, @@ -25,7 +27,8 @@ pub fn dotCompletions( .simple => |simple| switch (simple) { .type => { const ty_key = ip.indexToKey(val); - if (ty_key.getNamespace()) { + const namespace = ty_key.getNamespace(); + if (namespace != .none) { // TODO lookup in namespace } switch (ty_key) { @@ -35,7 +38,7 @@ pub fn dotCompletions( try completions.append(arena, .{ .label = error_name, .kind = .Constant, - .detail = std.fmt.allocPrint(arena, "error.{s}", .{std.zig.fmtId(error_name)}), + .detail = try std.fmt.allocPrint(arena, "error.{s}", .{std.zig.fmtId(error_name)}), }); } }, @@ -53,17 +56,17 @@ pub fn dotCompletions( else => {}, } }, - else => false, + else => {}, }, .pointer_type => |pointer_info| { - if (pointer_info == .Slice) { + if (pointer_info.size == .Slice) { var many_ptr_info = InternPool.Key{ .pointer_type = pointer_info }; many_ptr_info.pointer_type.size = .Many; try completions.append(arena, .{ .label = "ptr", .kind = .Field, - .detail = std.fmt.allocPrint(arena, "{}", .{many_ptr_info.fmtType(ip)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{many_ptr_info.fmtType(ip.*)}), }); try completions.append(arena, .{ .label = "len", @@ -81,9 +84,8 @@ pub fn dotCompletions( .array_type => |array_info| { try completions.append(arena, types.CompletionItem{ .label = "len", - .labelDetails = std.fmt.allocPrint(arena, "{d}", .{array_info.len}), .kind = .Field, - .detail = "usize", + .detail = try std.fmt.allocPrint(arena, "usize ({d})", .{array_info.len}), // TODO how should this be displayed }); }, .struct_type => |struct_info| { @@ -93,7 +95,7 @@ pub fn dotCompletions( .label = field_name, .kind = .Field, // TODO include alignment and comptime - .detail = std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip.*)}), }); } }, @@ -101,7 +103,7 @@ pub fn dotCompletions( try completions.append(arena, .{ .label = "?", .kind = .Operator, - .detail = std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmtType(ip)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmtType(ip.*)}), }); }, .enum_type => |enum_info| { @@ -110,7 +112,7 @@ pub fn dotCompletions( try completions.append(arena, .{ .label = field_name, .kind = .Field, - .detail = std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{field.val.fmtValue(enum_info.tag_type, ip.*)}), }); } }, @@ -121,22 +123,23 @@ pub fn dotCompletions( .label = field_name, .kind = .Field, .detail = if (field.alignment != 0) - std.fmt.allocPrint(arena, "align({d}) {}", .{ field.alignment, field.ty.fmtType(ip) }) + try std.fmt.allocPrint(arena, "align({d}) {}", .{ field.alignment, field.ty.fmtType(ip.*) }) else - std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip)}), + try std.fmt.allocPrint(arena, "{}", .{field.ty.fmtType(ip.*)}), }); } }, .tuple_type => |tuple_info| { for (tuple_info.types) |tuple_ty,i| { try completions.append(arena, .{ - .label = std.fmt.allocPrint(arena, "{d}", .{i}), + .label = try std.fmt.allocPrint(arena, "{d}", .{i}), .kind = .Field, - .detail = std.fmt.allocPrint(arena, "{}", .{tuple_ty.fmtType(ip)}), + .detail = try std.fmt.allocPrint(arena, "{}", .{tuple_ty.fmtType(ip.*)}), }); } }, .int_type, + .error_set_type, .error_union_type, .function_type, .vector_type, diff --git a/src/analysis.zig b/src/analysis.zig index f11f4c5..185342e 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -787,7 +787,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl } return null; }; - const val = result.getValue() catch |err| { + const value = result.getValue() catch |err| { log.err("Interpreter error: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -795,23 +795,16 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl return null; }; - const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.IPKey{ .simple = .type }); - if (val.ty != type_type) { - log.err("Not a type: {}", .{val.ty.fmtType(interpreter.ip)}); - return null; - } + const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.Key{ .simple = .type }); + const is_type_val = value.ty == type_type; return TypeWithHandle{ .type = .{ .data = .{ .@"comptime" = .{ .interpreter = interpreter, - .type = ComptimeInterpreter.Type{ - .interpreter = interpreter, - .node_idx = val.node_idx, - .ty = val.val, - }, + .value = value, } }, - .is_type_val = true, + .is_type_val = is_type_val, }, .handle = node_handle.handle, }; @@ -1064,7 +1057,7 @@ pub const Type = struct { array_index, @"comptime": struct { interpreter: *ComptimeInterpreter, - type: ComptimeInterpreter.Type, + value: ComptimeInterpreter.Value, }, }, /// If true, the type `type`, the attached data is the value of the type value. From 95139e09ec97ae1060ca30f0a6db5138dfdf67f2 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 23 Jan 2023 22:26:56 +0100 Subject: [PATCH 59/78] move InternPool and encoding into analyser folder --- src/ComptimeInterpreter.zig | 2 +- src/{ => analyser}/InternPool.zig | 0 src/analyser/analyser.zig | 8 ++++++++ src/analyser/completions.zig | 2 +- src/{ => analyser}/encoding.zig | 0 src/zls.zig | 2 +- tests/language_features/comptime_interpreter.zig | 2 +- 7 files changed, 12 insertions(+), 4 deletions(-) rename src/{ => analyser}/InternPool.zig (100%) create mode 100644 src/analyser/analyser.zig rename src/{ => analyser}/encoding.zig (100%) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index bfb003a..794de32 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -12,7 +12,7 @@ const analysis = @import("analysis.zig"); const offsets = @import("offsets.zig"); const DocumentStore = @import("DocumentStore.zig"); -pub const InternPool = @import("InternPool.zig"); +pub const InternPool = @import("analyser/InternPool.zig"); pub const Index = InternPool.Index; pub const Key = InternPool.Key; pub const ComptimeInterpreter = @This(); diff --git a/src/InternPool.zig b/src/analyser/InternPool.zig similarity index 100% rename from src/InternPool.zig rename to src/analyser/InternPool.zig diff --git a/src/analyser/analyser.zig b/src/analyser/analyser.zig new file mode 100644 index 0000000..94af823 --- /dev/null +++ b/src/analyser/analyser.zig @@ -0,0 +1,8 @@ +pub const completions = @import("completions.zig"); +pub const InternPool = @import("InternPool.zig"); +pub const encoding = @import("encoding.zig"); + +comptime { + const std = @import("std"); + std.testing.refAllDecls(@This()); +} diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 1f74896..276babc 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const InternPool = @import("../InternPool.zig"); +const InternPool = @import("InternPool.zig"); const types = @import("../lsp.zig"); const Ast = std.zig.Ast; diff --git a/src/encoding.zig b/src/analyser/encoding.zig similarity index 100% rename from src/encoding.zig rename to src/analyser/encoding.zig diff --git a/src/zls.zig b/src/zls.zig index f3a5e5c..4ecd8a9 100644 --- a/src/zls.zig +++ b/src/zls.zig @@ -12,7 +12,7 @@ pub const types = @import("lsp.zig"); pub const URI = @import("uri.zig"); pub const DocumentStore = @import("DocumentStore.zig"); pub const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); -pub const InternPool = @import("InternPool.zig"); +pub const analyser = @import("analyser/analyser.zig"); comptime { const std = @import("std"); diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index bc9cfba..920b909 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); const Ast = std.zig.Ast; const ComptimeInterpreter = zls.ComptimeInterpreter; -const InternPool = zls.InternPool; +const InternPool = zls.analyser.InternPool; const Index = InternPool.Index; const Key = InternPool.Key; const ast = zls.ast; From 3577e719fb089f974e2fb37366c5d4447285722e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:59:01 +0100 Subject: [PATCH 60/78] equality compare floats with bitcast in InternPool --- src/analyser/InternPool.zig | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 0d7313c..fdc93e4 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -1342,9 +1342,12 @@ fn deepEql(a: anytype, b: @TypeOf(a)) bool { .C, => @compileError("Unable to equality compare pointer " ++ @typeName(T)), }, + .Float => { + const I = std.meta.Int(.unsigned, @bitSizeOf(T)); + return @bitCast(I, a) == @bitCast(I, b); + }, .Bool, .Int, - .Float, .Enum, => return a == b, else => @compileError("Unable to equality compare type " ++ @typeName(T)), @@ -2757,16 +2760,50 @@ test "float value" { const f80_value = try ip.get(gpa, .{ .float_80_value = 2.0 }); const f128_value = try ip.get(gpa, .{ .float_128_value = 2.75 }); + const f32_nan_value = try ip.get(gpa, .{ .float_32_value = std.math.nan_f32 }); + const f32_qnan_value = try ip.get(gpa, .{ .float_32_value = std.math.qnan_f32 }); + + const f32_inf_value = try ip.get(gpa, .{ .float_32_value = std.math.inf_f32 }); + const f32_ninf_value = try ip.get(gpa, .{ .float_32_value = -std.math.inf_f32 }); + + const f32_zero_value = try ip.get(gpa, .{ .float_32_value = 0.0 }); + const f32_nzero_value = try ip.get(gpa, .{ .float_32_value = -0.0 }); + try std.testing.expect(f16_value != f32_value); try std.testing.expect(f32_value != f64_value); try std.testing.expect(f64_value != f80_value); try std.testing.expect(f80_value != f128_value); + try std.testing.expect(f32_nan_value != f32_qnan_value); + try std.testing.expect(f32_inf_value != f32_ninf_value); + try std.testing.expect(f32_zero_value != f32_nzero_value); + + try std.testing.expect(!ip.indexToKey(f16_value).eql(ip.indexToKey(f32_value))); + try std.testing.expect(ip.indexToKey(f32_value).eql(ip.indexToKey(f32_value))); + + try std.testing.expect(ip.indexToKey(f32_nan_value).eql(ip.indexToKey(f32_nan_value))); + try std.testing.expect(!ip.indexToKey(f32_nan_value).eql(ip.indexToKey(f32_qnan_value))); + + try std.testing.expect(ip.indexToKey(f32_inf_value).eql(ip.indexToKey(f32_inf_value))); + try std.testing.expect(!ip.indexToKey(f32_inf_value).eql(ip.indexToKey(f32_ninf_value))); + + try std.testing.expect(ip.indexToKey(f32_zero_value).eql(ip.indexToKey(f32_zero_value))); + try std.testing.expect(!ip.indexToKey(f32_zero_value).eql(ip.indexToKey(f32_nzero_value))); + try testExpectFmtValue(ip, f16_value, undefined, "0.25"); try testExpectFmtValue(ip, f32_value, undefined, "0.5"); try testExpectFmtValue(ip, f64_value, undefined, "1"); try testExpectFmtValue(ip, f80_value, undefined, "2"); try testExpectFmtValue(ip, f128_value, undefined, "2.75"); + + try testExpectFmtValue(ip, f32_nan_value, undefined, "nan"); + try testExpectFmtValue(ip, f32_qnan_value, undefined, "nan"); + + try testExpectFmtValue(ip, f32_inf_value, undefined, "inf"); + try testExpectFmtValue(ip, f32_ninf_value, undefined, "-inf"); + + try testExpectFmtValue(ip, f32_zero_value, undefined, "0"); + try testExpectFmtValue(ip, f32_nzero_value, undefined, "-0"); } test "pointer type" { From 05ad3294f1aeaf8d501bc6841fe8d52a727b7a20 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 24 Jan 2023 17:00:14 +0100 Subject: [PATCH 61/78] use ArrayList for dotCompletions --- src/Server.zig | 5 ++--- src/analyser/completions.zig | 8 ++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Server.zig b/src/Server.zig index 876ae28..82c0d71 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -20,7 +20,7 @@ const tracy = @import("tracy.zig"); const uri_utils = @import("uri.zig"); const diff = @import("diff.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); -const analyser_completions = @import("analyser/completions.zig"); +const analyser = @import("analyser/analyser.zig"); const data = @import("data/data.zig"); const snipped_data = @import("data/snippets.zig"); @@ -596,8 +596,7 @@ fn typeToCompletion( ), .primitive, .array_index => {}, .@"comptime" => |co| { - const items = try analyser_completions.dotCompletions(allocator, &co.interpreter.ip, co.value.ty, co.value.val, co.value.node_idx); - try list.appendSlice(allocator, items); + try analyser.completions.dotCompletions(allocator, list, &co.interpreter.ip, co.value.ty, co.value.val, co.value.node_idx); }, } } diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 276babc..207d12c 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -4,19 +4,16 @@ const types = @import("../lsp.zig"); const Ast = std.zig.Ast; - -/// TODO use std.ArrayListUnmanaged instead of returning a slice pub fn dotCompletions( arena: std.mem.Allocator, + completions: *std.ArrayListUnmanaged(types.CompletionItem), ip: *InternPool, ty: InternPool.Index, val: InternPool.Index, node: ?Ast.Node.Index -) error{OutOfMemory}![]types.CompletionItem { +) error{OutOfMemory}!void { _ = node; - var completions = std.ArrayListUnmanaged(types.CompletionItem){}; - const key = ip.indexToKey(ty); const inner_key = switch (key) { .pointer_type => |info| if (info.size == .One) ip.indexToKey(info.elem_type) else key, @@ -160,5 +157,4 @@ pub fn dotCompletions( .union_value, => unreachable, } - return try completions.toOwnedSlice(arena); } From b09c317ae7c6ff60ba195848ba61d725a730594d Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:07:19 +0100 Subject: [PATCH 62/78] bug hunting --- src/ComptimeInterpreter.zig | 18 ++++++++++++------ src/DocumentStore.zig | 4 ++++ src/Server.zig | 2 +- src/analyser/InternPool.zig | 6 ++++-- src/analyser/completions.zig | 11 ++++++----- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 794de32..49cc24e 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -46,15 +46,19 @@ pub fn recordError( comptime fmt: []const u8, args: anytype, ) error{OutOfMemory}!void { - try interpreter.errors.put(interpreter.allocator, node_idx, .{ + const message = try std.fmt.allocPrint(interpreter.allocator, fmt, args); + errdefer interpreter.allocator.free(message); + const previous = try interpreter.errors.fetchPut(interpreter.allocator, node_idx, .{ .code = code, - .message = try std.fmt.allocPrint(interpreter.allocator, fmt, args), + .message = message, }); + if (previous != null) interpreter.allocator.free(message); } pub fn deinit(interpreter: *ComptimeInterpreter) void { - var err_it = interpreter.errors.iterator(); - while (err_it.next()) |entry| interpreter.allocator.free(entry.value_ptr.message); + for (interpreter.errors.values()) |err| { + interpreter.allocator.free(err.message); + } interpreter.errors.deinit(interpreter.allocator); interpreter.ip.deinit(interpreter.allocator); @@ -302,8 +306,8 @@ pub fn interpret( const decl = ast.varDecl(tree, node_idx).?; - const type_value = if (decl.ast.type_node != 0) try ((try interpreter.interpret(decl.ast.type_node, namespace, .{})).getValue()) else null; - const init_value = if (decl.ast.init_node != 0) try ((try interpreter.interpret(decl.ast.init_node, namespace, .{})).getValue()) else null; + const type_value = if (decl.ast.type_node != 0) (try interpreter.interpret(decl.ast.type_node, namespace, .{})).maybeGetValue() else null; + const init_value = if (decl.ast.init_node != 0) (try interpreter.interpret(decl.ast.init_node, namespace, .{})).maybeGetValue() else null; if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; @@ -470,6 +474,8 @@ pub fn interpret( const can_have_fields: bool = switch (inner_lhs) { .simple => |simple| switch (simple) { .type => blk: { + 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| { return InterpretResult{ .value = Value{ diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index a94fbca..27903a4 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -68,6 +68,10 @@ pub const Handle = struct { associated_build_file: ?Uri = null, pub fn deinit(self: *Handle, allocator: std.mem.Allocator) void { + if (self.interpreter) |interpreter| { + interpreter.deinit(); + allocator.destroy(interpreter); + } self.document_scope.deinit(allocator); self.tree.deinit(allocator); allocator.free(self.text); diff --git a/src/Server.zig b/src/Server.zig index 82c0d71..d5a08b8 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -361,7 +361,7 @@ fn generateDiagnostics(server: *Server, handle: DocumentStore.Handle) error{OutO var err_it = int.errors.iterator(); while (err_it.next()) |err| { - try diagnostics.append(allocator, .{ + diagnostics.appendAssumeCapacity(.{ .range = offsets.nodeToRange(tree, err.key_ptr.*, server.offset_encoding), .severity = .Error, .code = .{ .string = err.value_ptr.code }, diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index fdc93e4..07fb94e 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -321,7 +321,8 @@ pub const Key = union(enum) { .@"anyframe", .null_type, .undefined_type, - .enum_literal_type, => true, + .enum_literal_type, + => true, .undefined_value, .void_value, @@ -344,7 +345,8 @@ pub const Key = union(enum) { .union_type, .tuple_type, .vector_type, - .anyframe_type, => true, + .anyframe_type, + => true, .int_u64_value, .int_i64_value, diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 207d12c..14e0eee 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -10,7 +10,7 @@ pub fn dotCompletions( ip: *InternPool, ty: InternPool.Index, val: InternPool.Index, - node: ?Ast.Node.Index + node: ?Ast.Node.Index, ) error{OutOfMemory}!void { _ = node; @@ -40,7 +40,7 @@ pub fn dotCompletions( } }, .union_type => {}, // TODO - .enum_type => |enum_info|{ + .enum_type => |enum_info| { for (enum_info.fields) |field| { const field_name = ip.indexToKey(field.name).bytes; try completions.append(arena, .{ @@ -70,7 +70,7 @@ pub fn dotCompletions( .kind = .Field, .detail = "usize", }); - } else if(ip.indexToKey(pointer_info.elem_type) == .array_type) { + } else if (ip.indexToKey(pointer_info.elem_type) == .array_type) { try completions.append(arena, .{ .label = "len", .kind = .Field, @@ -127,7 +127,7 @@ pub fn dotCompletions( } }, .tuple_type => |tuple_info| { - for (tuple_info.types) |tuple_ty,i| { + for (tuple_info.types) |tuple_ty, i| { try completions.append(arena, .{ .label = try std.fmt.allocPrint(arena, "{d}", .{i}), .kind = .Field, @@ -140,7 +140,8 @@ pub fn dotCompletions( .error_union_type, .function_type, .vector_type, - .anyframe_type => {}, + .anyframe_type, + => {}, .int_u64_value, .int_i64_value, From 9131285db1787a82c047a25ba73abfdb28e4b187 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Thu, 26 Jan 2023 17:14:42 +0100 Subject: [PATCH 63/78] improve comptime interpreter stack trace message --- src/ComptimeInterpreter.zig | 2 +- src/analysis.zig | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 40f9f4e..25c1152 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -52,7 +52,7 @@ pub fn recordError( .code = code, .message = message, }); - if (previous != null) interpreter.allocator.free(message); + if (previous) |p| interpreter.allocator.free(p.value.message); } pub fn deinit(interpreter: *ComptimeInterpreter) void { diff --git a/src/analysis.zig b/src/analysis.zig index 8a6868e..c850790 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -764,30 +764,26 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl log.info("Invoking interpreter!", .{}); store.ensureInterpreterExists(handle.uri) catch |err| { - log.err("Interpreter error: {s}", .{@errorName(err)}); + log.err("Failed to interpret file: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } return null; }; - var interpreter = handle.interpreter.?; + var interpreter: *ComptimeInterpreter = handle.interpreter.?; - try interpreter.namespaces.append(interpreter.allocator, .{ - .parent = .none, - .node_idx = 0, - .ty = .none, - }); + const root_namespace = @intToEnum(ComptimeInterpreter.NamespaceIndex, 0); // TODO: Start from current/nearest-current scope - const result = interpreter.interpret(node, .none, .{}) catch |err| { - log.err("Interpreter error: {s}", .{@errorName(err)}); + const result = interpreter.interpret(node, root_namespace, .{}) catch |err| { + log.err("Failed to interpret node: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } return null; }; const value = result.getValue() catch |err| { - log.err("Interpreter error: {s}", .{@errorName(err)}); + log.err("interpreter return no result: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } From 55364f2e2ba3cb5f4753b6a8a7c256e58e40c3f7 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 28 Jan 2023 18:02:29 +0100 Subject: [PATCH 64/78] do not intern Structs, Enums and Unions --- src/ComptimeInterpreter.zig | 145 ++++---- src/analyser/InternPool.zig | 318 ++++++++---------- src/analyser/completions.zig | 46 +-- src/analysis.zig | 2 +- .../comptime_interpreter.zig | 13 +- 5 files changed, 240 insertions(+), 284 deletions(-) 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); From 32b70117efae759334fd45ea8627fda06eeb047e Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 28 Jan 2023 18:53:42 +0100 Subject: [PATCH 65/78] use SegmentedList --- src/analyser/InternPool.zig | 39 ++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 3f6304f..22a7d37 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -4,10 +4,10 @@ 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) = .{}, +decls: std.SegmentedList(InternPool.Decl, 0) = .{}, +structs: std.SegmentedList(InternPool.Struct, 0) = .{}, +enums: std.SegmentedList(InternPool.Enum, 0) = .{}, +unions: std.SegmentedList(InternPool.Union, 0) = .{}, const InternPool = @This(); const std = @import("std"); @@ -1212,14 +1212,17 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.items.deinit(gpa); ip.extra.deinit(gpa); - for (ip.structs.items) |*item| { + var struct_it = ip.structs.iterator(0); + while (struct_it.next()) |item| { item.fields.deinit(gpa); } - for (ip.enums.items) |*item| { + var enum_it = ip.enums.iterator(0); + while (enum_it.next()) |item| { item.fields.deinit(gpa); item.values.deinit(gpa); } - for (ip.unions.items) |*item| { + var union_it = ip.unions.iterator(0); + while (union_it.next()) |item| { item.fields.deinit(gpa); } ip.decls.deinit(gpa); @@ -1320,34 +1323,38 @@ 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 getDecl(ip: InternPool, index: InternPool.DeclIndex) *InternPool.Decl { + var decls = ip.decls; + return decls.at(@enumToInt(index)); } pub fn getStruct(ip: InternPool, index: InternPool.StructIndex) *InternPool.Struct { - return &ip.structs.items[@enumToInt(index)]; + var structs = ip.structs; + return structs.at(@enumToInt(index)); } pub fn getEnum(ip: InternPool, index: InternPool.EnumIndex) *InternPool.Enum { - return &ip.enums.items[@enumToInt(index)]; + var enums = ip.enums; + return enums.at(@enumToInt(index)); } pub fn getUnion(ip: InternPool, index: InternPool.UnionIndex) *InternPool.Union { - return &ip.unions.items[@enumToInt(index)]; + var unions = ip.unions; + return unions.at(@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); + return @intToEnum(InternPool.DeclIndex, ip.decls.count() - 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); + return @intToEnum(InternPool.StructIndex, ip.structs.count() - 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); + return @intToEnum(InternPool.EnumIndex, ip.enums.count() - 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); + return @intToEnum(InternPool.UnionIndex, ip.unions.count() - 1); } fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { From 9cde2495b2969097d7ffde21b3af9b26f5fe5fa3 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 28 Jan 2023 18:54:16 +0100 Subject: [PATCH 66/78] bug fixes --- src/ComptimeInterpreter.zig | 62 ++++++++++++------- .../comptime_interpreter.zig | 10 +++ 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index f90a90e..ba9934e 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -214,7 +214,7 @@ pub fn interpret( try interpreter.namespaces.append(interpreter.allocator, .{ .parent = namespace, .node_idx = node_idx, - .ty = undefined, + .ty = .none, }); const container_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1); @@ -263,7 +263,7 @@ pub fn interpret( }); } - const struct_type = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }); // TODO potential double free on struct_info.field + const struct_type = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }); interpreter.namespaces.items(.ty)[@enumToInt(container_namespace)] = struct_type; return InterpretResult{ .value = Value{ @@ -282,16 +282,36 @@ pub fn interpret( .aligned_var_decl, .simple_var_decl, => { - var decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; + var decls: *std.StringArrayHashMapUnmanaged(InternPool.DeclIndex) = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; const name = analysis.getDeclName(tree, node_idx).?; - if (decls.contains(name)) + const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ + .name = name, + .ty = .none, + .val = .none, + .alignment = 0, // TODO + .address_space = .generic, // TODO + .is_pub = true, // TODO + .is_exported = false, // TODO + }); + + const gop = try decls.getOrPutValue(interpreter.allocator, name, decl_index); + if (gop.found_existing) { return InterpretResult{ .nothing = {} }; + } - const decl = tree.fullVarDecl(node_idx).?; + const var_decl = tree.fullVarDecl(node_idx).?; - const type_value = if (decl.ast.type_node != 0) (try interpreter.interpret(decl.ast.type_node, namespace, .{})).maybeGetValue() else null; - const init_value = if (decl.ast.init_node != 0) (try interpreter.interpret(decl.ast.init_node, namespace, .{})).maybeGetValue() else null; + const type_value = blk: { + if (var_decl.ast.type_node == 0) break :blk null; + const result = interpreter.interpret(var_decl.ast.type_node, namespace, .{}) catch break :blk null; + break :blk result.maybeGetValue(); + }; + const init_value = blk: { + if (var_decl.ast.init_node == 0) break :blk null; + const result = interpreter.interpret(var_decl.ast.init_node, namespace, .{}) catch break :blk null; + break :blk result.maybeGetValue(); + }; if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; @@ -300,16 +320,9 @@ pub fn interpret( if (v.ty != type_type) return InterpretResult{ .nothing = {} }; } - 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, - .alignment = 0, // TODO - .address_space = .generic, // TODO - .is_pub = true, // TODO - .is_exported = false, // TODO - }); - try decls.putNoClobber(interpreter.allocator, name, decl_index); + const decl = interpreter.ip.getDecl(decl_index); + decl.ty = if (type_value) |v| v.val else init_value.?.ty; + decl.val = if (init_value) |init| init.val else .none; // TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...) @@ -428,6 +441,7 @@ pub fn interpret( // Logic to find identifiers in accessible scopes if (interpreter.huntItDown(namespace, identifier, options)) |decl_index| { const decl = interpreter.ip.getDecl(decl_index); + if(decl.ty == .none) return InterpretResult{ .nothing = {} }; return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -656,12 +670,14 @@ pub fn interpret( .equal_equal => { var a = try interpreter.interpret(data[node_idx].lhs, namespace, options); var b = try interpreter.interpret(data[node_idx].rhs, namespace, options); + const a_value = a.maybeGetValue() orelse return InterpretResult{ .nothing = {} }; + const b_value = b.maybeGetValue() orelse return InterpretResult{ .nothing = {} }; return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (a.value.val == b.value.val) .bool_true else .bool_false }), // TODO eql function required? + .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (a_value.val == b_value.val) .bool_true else .bool_false }), // TODO eql function required? }, }; }, @@ -847,9 +863,10 @@ pub fn interpret( const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - if (value.ty != type_type) return error.InvalidBuiltin; + if (value.ty != type_type or value.ty == .none) return error.InvalidBuiltin; if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8 - + if (value.val == .none) return error.InvalidBuiltin; + const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace(interpreter.ip); if (value_namespace == .none) return error.InvalidBuiltin; @@ -930,6 +947,9 @@ pub fn interpret( var buf: [1]Ast.Node.Index = undefined; const func = tree.fullFnProto(&buf, node_idx).?; + if (func.name_token == null) return InterpretResult{ .nothing = {} }; + const name = offsets.tokenToSlice(tree, func.name_token.?); + // TODO: Resolve function type const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); @@ -967,8 +987,6 @@ pub fn interpret( // if ((try interpreter.interpret(func.ast.return_type, func_scope_idx, .{ .observe_values = true, .is_comptime = true })).maybeGetValue()) |value| // fnd.return_type = value.value_data.@"type"; - const name = offsets.tokenToSlice(tree, func.name_token.?); - if (namespace != .none) { const decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)]; diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 36c442d..3e7928f 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -94,6 +94,16 @@ test "ComptimeInterpreter - variable lookup" { \\ break :blk bar; \\} , .{ .simple = .comptime_int }, .{ .int_u64_value = 2 }); + + var context = try Context.init( + \\const bar = foo; + \\const foo = 3; + ); + defer context.deinit(); + + const result = try context.interpret(context.findVar("bar")); + try expectEqualKey(context.interpreter.ip, .{ .simple = .comptime_int }, result.ty); + try expectEqualKey(context.interpreter.ip, .{ .int_u64_value = 3 }, result.val); } test "ComptimeInterpreter - field access" { From 3c7e9e13ce254bbb6431129cf38271a64b7d35dd Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 28 Jan 2023 19:01:49 +0100 Subject: [PATCH 67/78] better error messages on field access --- src/ComptimeInterpreter.zig | 37 ++++++++++++++++++++----------------- src/analyser/InternPool.zig | 4 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index ba9934e..43304ea 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -441,7 +441,7 @@ pub fn interpret( // Logic to find identifiers in accessible scopes if (interpreter.huntItDown(namespace, identifier, options)) |decl_index| { const decl = interpreter.ip.getDecl(decl_index); - if(decl.ty == .none) return InterpretResult{ .nothing = {} }; + if (decl.ty == .none) return InterpretResult{ .nothing = {} }; return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, @@ -613,20 +613,23 @@ pub fn interpret( else => false, }; - if (can_have_fields) { - try interpreter.recordError( - node_idx, - "undeclared_identifier", - "`{}` has no member '{s}'", - .{ irv.ty.fmtType(interpreter.ip), field_name }, - ); - } else { - try interpreter.recordError( - node_idx, - "invalid_field_access", - "`{}` does not support field access", - .{irv.ty.fmtType(interpreter.ip)}, - ); + const accessed_ty = if (inner_lhs == .simple and inner_lhs.simple == .type) irv.val else irv.ty; + if (accessed_ty != .none) { + if (can_have_fields) { + try interpreter.recordError( + node_idx, + "undeclared_identifier", + "`{}` has no member '{s}'", + .{ accessed_ty.fmtType(interpreter.ip), field_name }, + ); + } else { + try interpreter.recordError( + node_idx, + "invalid_field_access", + "`{}` does not support field access", + .{accessed_ty.fmtType(interpreter.ip)}, + ); + } } return error.InvalidOperation; }, @@ -866,7 +869,7 @@ pub fn interpret( if (value.ty != type_type or value.ty == .none) return error.InvalidBuiltin; if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8 if (value.val == .none) return error.InvalidBuiltin; - + const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace(interpreter.ip); if (value_namespace == .none) return error.InvalidBuiltin; @@ -949,7 +952,7 @@ pub fn interpret( if (func.name_token == null) return InterpretResult{ .nothing = {} }; const name = offsets.tokenToSlice(tree, func.name_token.?); - + // TODO: Resolve function type const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 22a7d37..bfafd4f 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -2677,9 +2677,9 @@ fn optionalPtrTy( } } -/// will panic in debug mode or in tests else will return `value` +/// will panic in during testing else will return `value` inline fn panicOrElse(message: []const u8, value: anytype) @TypeOf(value) { - if (builtin.is_test or builtin.mode == .Debug) { + if (builtin.is_test) { @panic(message); } return value; From 387291570130236a3da40377c5a1a5bb0088ca97 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 28 Jan 2023 19:22:51 +0100 Subject: [PATCH 68/78] more comptime interpreter tests --- .../comptime_interpreter.zig | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 3e7928f..0b5889d 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -83,9 +83,30 @@ test "ComptimeInterpreter - if" { \\ break :blk if (false) true else false; \\} , .{ .simple = .bool }, .{ .simple = .bool_false }); + try testExpr( + \\blk: { + \\ if (false) break :blk true; + \\ break :blk false; + \\} + , .{ .simple = .bool }, .{ .simple = .bool_false }); + // TODO + // try testExpr( + // \\outer: { + // \\ if (:inner { + // \\ break :inner true; + // \\ }) break :outer true; + // \\ break :outer false; + // \\} + // , .{ .simple = .bool }, .{ .simple = .bool_true }); } test "ComptimeInterpreter - variable lookup" { + try testExpr( + \\blk: { + \\ var foo = 42; + \\ break :blk foo; + \\} + , .{ .simple = .comptime_int }, .{ .int_u64_value = 42 }); try testExpr( \\blk: { \\ var foo = 1; @@ -113,6 +134,31 @@ test "ComptimeInterpreter - field access" { \\ break :blk foo.beta; \\} , .{ .simple = .bool }, null); + try testExpr( + \\blk: { + \\ const foo: struct {alpha: u64, beta: bool} = undefined; + \\ break :blk foo.alpha; + \\} + , .{ .int_type = .{ + .signedness = .unsigned, + .bits = 64, + } }, null); +} + +test "ComptimeInterpreter - optional operations" { + if (true) return error.SkipZigTest; // TODO + try testExpr( + \\blk: { + \\ const foo: ?bool = true; + \\ break :blk foo.?; + \\} + , .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr( + \\blk: { + \\ const foo: ?bool = true; + \\ break :blk foo == null; + \\} + , .{ .simple = .bool }, .{ .simple = .bool_false }); } test "ComptimeInterpreter - pointer operations" { From 5258a60f0ef7b5aae0bacc5944db24a8b62c20fa Mon Sep 17 00:00:00 2001 From: Techarix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 3 Feb 2023 23:25:42 +0100 Subject: [PATCH 69/78] resolve bit size based target --- src/analyser/InternPool.zig | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index bfafd4f..c5a1860 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -407,16 +407,15 @@ pub const Key = union(enum) { .usize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, .isize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, - // TODO correctly resolve size based on `target` - .c_short => return .{ .signedness = .signed, .bits = @bitSizeOf(c_short) }, - .c_ushort => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ushort) }, - .c_int => return .{ .signedness = .signed, .bits = @bitSizeOf(c_int) }, - .c_uint => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_uint) }, - .c_long => return .{ .signedness = .signed, .bits = @bitSizeOf(c_long) }, - .c_ulong => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ulong) }, - .c_longlong => return .{ .signedness = .signed, .bits = @bitSizeOf(c_longlong) }, - .c_ulonglong => return .{ .signedness = .unsigned, .bits = @bitSizeOf(c_ulonglong) }, - .c_longdouble => return .{ .signedness = .signed, .bits = @bitSizeOf(c_longdouble) }, + .c_short => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) }, + .c_ushort => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) }, + .c_int => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) }, + .c_uint => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) }, + .c_long => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) }, + .c_ulong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) }, + .c_longlong => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) }, + .c_ulonglong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) }, + .c_longdouble => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longdouble) }, // TODO revisit this when error sets support custom int types (comment taken from zig codebase) .anyerror => return .{ .signedness = .unsigned, .bits = 16 }, @@ -443,15 +442,13 @@ pub const Key = union(enum) { /// Asserts the type is a fixed-size float or comptime_float. /// Returns 128 for comptime_float types. pub fn floatBits(ty: Key, target: std.Target) u16 { - _ = target; return switch (ty.simple) { .f16 => 16, .f32 => 32, .f64 => 64, .f80 => 80, .f128, .comptime_float => 128, - // TODO correctly resolve size based on `target` - .c_longdouble => @bitSizeOf(c_longdouble), + .c_longdouble => target.c_type_bit_size(.longdouble), else => unreachable, }; From 57fc10eec0b8b36b03f8e166b51fdf144374b575 Mon Sep 17 00:00:00 2001 From: Techarix <19954306+Techatrix@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:01:15 +0100 Subject: [PATCH 70/78] add predefined values to Index --- src/ComptimeInterpreter.zig | 159 +++--- src/DocumentStore.zig | 22 +- src/analyser/InternPool.zig | 539 ++++++++++-------- src/analyser/completions.zig | 3 +- src/analysis.zig | 9 +- .../comptime_interpreter.zig | 89 ++- 6 files changed, 426 insertions(+), 395 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index 43304ea..a334455 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -20,7 +20,7 @@ pub const ComptimeInterpreter = @This(); const log = std.log.scoped(.comptime_interpreter); allocator: std.mem.Allocator, -ip: InternPool = .{}, +ip: InternPool, document_store: *DocumentStore, uri: DocumentStore.Uri, namespaces: std.MultiArrayList(Namespace) = .{}, @@ -209,8 +209,6 @@ pub fn interpret( // .tagged_union_enum_tag_trailing, .root, => { - const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - try interpreter.namespaces.append(interpreter.allocator, .{ .parent = namespace, .node_idx = node_idx, @@ -243,7 +241,7 @@ pub fn interpret( else (try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty - if (init_type_value.ty != type_type) { + if (init_type_value.ty != Index.type) { try interpreter.recordError( container_field.ast.type_expr, "expected_type", @@ -269,7 +267,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = type_type, + .ty = Index.type, .val = struct_type, } }; }, @@ -316,8 +314,7 @@ pub fn interpret( if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; if (type_value) |v| { - const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - if (v.ty != type_type) return InterpretResult{ .nothing = {} }; + if (v.ty != Index.type) return InterpretResult{ .nothing = {} }; } const decl = interpreter.ip.getDecl(decl_index); @@ -385,44 +382,53 @@ pub fn interpret( .identifier => { const identifier = offsets.nodeToSlice(tree, node_idx); - const simples = std.ComptimeStringMap(InternPool.Simple, .{ - .{ "anyerror", .anyerror }, - .{ "anyframe", .@"anyframe" }, - .{ "anyopaque", .anyopaque }, - .{ "bool", .bool }, - .{ "c_int", .c_int }, - .{ "c_long", .c_long }, - .{ "c_longdouble", .c_longdouble }, - .{ "c_longlong", .c_longlong }, - .{ "c_short", .c_short }, - .{ "c_uint", .c_uint }, - .{ "c_ulong", .c_ulong }, - .{ "c_ulonglong", .c_ulonglong }, - .{ "c_ushort", .c_ushort }, - .{ "comptime_float", .comptime_float }, - .{ "comptime_int", .comptime_int }, - .{ "f128", .f128 }, - .{ "f16", .f16 }, - .{ "f32", .f32 }, - .{ "f64", .f64 }, - .{ "f80", .f80 }, - .{ "false", .bool_false }, - .{ "isize", .isize }, - .{ "noreturn", .noreturn }, - .{ "null", .null_value }, - .{ "true", .bool_true }, - .{ "type", .type }, - .{ "undefined", .undefined_value }, - .{ "usize", .usize }, - .{ "void", .void }, + const simples = std.ComptimeStringMap(Index, .{ + .{ "anyerror", Index.anyerror }, + .{ "anyframe", Index.@"anyframe" }, + .{ "anyopaque", Index.anyopaque }, + .{ "bool", Index.bool }, + .{ "c_int", Index.c_int }, + .{ "c_long", Index.c_long }, + .{ "c_longdouble", Index.c_longdouble }, + .{ "c_longlong", Index.c_longlong }, + .{ "c_short", Index.c_short }, + .{ "c_uint", Index.c_uint }, + .{ "c_ulong", Index.c_ulong }, + .{ "c_ulonglong", Index.c_ulonglong }, + .{ "c_ushort", Index.c_ushort }, + .{ "comptime_float", Index.comptime_float }, + .{ "comptime_int", Index.comptime_int }, + .{ "f128", Index.f128 }, + .{ "f16", Index.f16 }, + .{ "f32", Index.f32 }, + .{ "f64", Index.f64 }, + .{ "f80", Index.f80 }, + .{ "false", Index.bool_false }, + .{ "isize", Index.isize }, + .{ "noreturn", Index.noreturn }, + .{ "null", Index.null_value }, + .{ "true", Index.bool_true }, + .{ "type", Index.type }, + .{ "undefined", Index.undefined_value }, + .{ "usize", Index.usize }, + .{ "void", Index.void }, }); - if (simples.get(identifier)) |simple| { + if (simples.get(identifier)) |index| { + const ty: Index = switch (index) { + .undefined_value => .undefined_type, + .void_value => .void, + .unreachable_value => .noreturn, + .null_value => .null_type, + .bool_true => .bool, + .bool_false => .bool, + else => .type, + }; return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = simple.toType() }), - .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = simple }), + .ty = ty, + .val = index, } }; } @@ -430,7 +436,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), + .ty = Index.type, .val = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{ .signedness = if (identifier[0] == 'u') .unsigned else .signed, .bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk, @@ -456,7 +462,8 @@ pub fn interpret( "use of undeclared identifier '{s}'", .{identifier}, ); - return error.IdentifierNotFound; + // return error.IdentifierNotFound; + return InterpretResult{ .nothing = {} }; }, .field_access => { if (data[node_idx].rhs == 0) return error.CriticalAstFailure; @@ -472,7 +479,7 @@ pub fn interpret( }; const can_have_fields: bool = switch (inner_lhs) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .type => blk: { if (irv.val == .none) break :blk true; @@ -530,7 +537,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = data[node_idx].rhs, - .ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }), + .ty = Index.usize, .val = .none, // TODO resolve length of Slice }, }; @@ -541,7 +548,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = data[node_idx].rhs, - .ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .usize }), + .ty = Index.usize, .val = .none, // TODO resolve length of Slice }, }; @@ -556,7 +563,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = data[node_idx].rhs, - .ty = try interpreter.ip.get(interpreter.allocator, .{ .simple = .comptime_int }), + .ty = Index.comptime_int, .val = len_value, } }; } @@ -564,8 +571,7 @@ pub fn interpret( }, .optional_type => |optional_info| blk: { if (!std.mem.eql(u8, field_name, "?")) break :blk false; - const null_value = try interpreter.ip.get(interpreter.allocator, .{ .simple = .null_value }); - if (irv.val == null_value) { + if (irv.val == Index.null_value) { try interpreter.recordError( node_idx, "null_unwrap", @@ -613,7 +619,7 @@ pub fn interpret( else => false, }; - const accessed_ty = if (inner_lhs == .simple and inner_lhs.simple == .type) irv.val else irv.ty; + const accessed_ty = if (inner_lhs == .simple_type and inner_lhs.simple_type == .type) irv.val else irv.ty; if (accessed_ty != .none) { if (can_have_fields) { try interpreter.recordError( @@ -657,12 +663,9 @@ pub fn interpret( // if (options.observe_values) { const ir = try interpreter.interpret(if_info.ast.cond_expr, namespace, options); - const false_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_false }); - const true_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_true }); - const condition = (try ir.getValue()).val; - std.debug.assert(condition == false_value or condition == true_value); - if (condition == true_value) { + std.debug.assert(condition == Index.bool_false or condition == Index.bool_true); + if (condition == Index.bool_true) { return try interpreter.interpret(if_info.ast.then_expr, namespace, options); } else { if (if_info.ast.else_expr != 0) { @@ -679,8 +682,8 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (a_value.val == b_value.val) .bool_true else .bool_false }), // TODO eql function required? + .ty = Index.bool, + .val = if (a_value.val == b_value.val) Index.bool_true else Index.bool_false, // TODO eql function required? }, }; }, @@ -690,9 +693,7 @@ pub fn interpret( if (nl == .failure) return error.CriticalAstFailure; - const number_type = try interpreter.ip.get(interpreter.allocator, Key{ - .simple = if (nl == .float) .comptime_float else .comptime_int, - }); + const number_type = if (nl == .float) Index.comptime_float else Index.comptime_int; const value = try interpreter.ip.get( interpreter.allocator, @@ -826,7 +827,7 @@ pub fn interpret( .interpreter = interpreter, .node_idx = node_idx, .ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }), - .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .undefined_value }), + .val = Index.undefined_value, } }; } @@ -834,13 +835,13 @@ pub fn interpret( defer interpreter.allocator.free(import_uri); var handle = interpreter.document_store.getOrLoadHandle(import_uri) orelse return error.ImportFailure; - try interpreter.document_store.ensureInterpreterExists(handle.uri); + _ = try interpreter.document_store.ensureInterpreterExists(handle.uri); return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), + .ty = Index.type, .val = .none, // TODO }, }; @@ -853,7 +854,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }), + .ty = Index.type, .val = value.ty, } }; } @@ -864,9 +865,7 @@ pub fn interpret( const value = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue(); - const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - - if (value.ty != type_type or value.ty == .none) return error.InvalidBuiltin; + if (value.ty != Index.type or value.ty == .none) return error.InvalidBuiltin; if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8 if (value.val == .none) return error.InvalidBuiltin; @@ -881,8 +880,8 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }), - .val = try interpreter.ip.get(interpreter.allocator, Key{ .simple = if (has_decl) .bool_true else .bool_false }), + .ty = Index.bool, + .val = if (has_decl) Index.bool_true else Index.bool_false, } }; } @@ -892,9 +891,7 @@ pub fn interpret( const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const value = try (try interpreter.interpret(params[1], namespace, options)).getValue(); - const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - - if (as_type.ty != type_type) return error.InvalidBuiltin; + if (as_type.ty != Index.type) return error.InvalidBuiltin; return InterpretResult{ .value = Value{ @@ -955,8 +952,6 @@ pub fn interpret( // TODO: Resolve function type - const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - const function_type = try interpreter.ip.get(interpreter.allocator, Key{ .function_type = .{ .calling_convention = .Unspecified, .alignment = 0, @@ -995,7 +990,7 @@ pub fn interpret( const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, - .ty = type_type, + .ty = Index.type, .val = function_type, .alignment = 0, // TODO .address_space = .generic, // TODO @@ -1040,10 +1035,9 @@ pub fn interpret( }, .bool_not => { const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); - const bool_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool }); const value = try result.getValue(); - if (value.ty != bool_type) { + if (value.ty != Index.bool) { try interpreter.recordError( node_idx, "invalid_deref", @@ -1053,15 +1047,12 @@ pub fn interpret( return error.InvalidOperation; } - const false_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_false }); - const true_value = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .bool_true }); - - std.debug.assert(value.val == false_value or value.val == true_value); + std.debug.assert(value.val == Index.bool_false or value.val == Index.bool_true); return InterpretResult{ .value = .{ .interpreter = interpreter, .node_idx = node_idx, - .ty = bool_type, - .val = if (value.val == false_value) true_value else false_value, + .ty = Index.bool, + .val = if (value.val == Index.bool_false) Index.bool_true else Index.bool_false, } }; }, .address_of => { @@ -1146,14 +1137,12 @@ pub fn call( }); const fn_namespace = @intToEnum(Namespace.Index, interpreter.namespaces.len - 1); - const type_type = try interpreter.ip.get(interpreter.allocator, Key{ .simple = .type }); - var arg_it = proto.iterate(&tree); var arg_index: usize = 0; while (ast.nextFnParam(&arg_it)) |param| { if (arg_index >= arguments.len) return error.MissingArguments; var tex = try (try interpreter.interpret(param.type_expr, fn_namespace, options)).getValue(); - if (tex.ty != type_type) { + if (tex.ty != Index.type) { try interpreter.recordError( param.type_expr, "expected_type", diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index e4b8c4d..5d7b700 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -989,16 +989,26 @@ pub fn enumCompletionItems(self: DocumentStore, arena: std.mem.Allocator, handle return try self.tagStoreCompletionItems(arena, handle, "enum_completions"); } -pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !void { +pub fn ensureInterpreterExists(self: *DocumentStore, uri: Uri) !*ComptimeInterpreter { var handle = self.handles.get(uri).?; - if (handle.interpreter == null) { - var int = try self.allocator.create(ComptimeInterpreter); - int.* = ComptimeInterpreter{ + if (handle.interpreter != null) return handle.interpreter.?; + + { + var interpreter = try self.allocator.create(ComptimeInterpreter); + errdefer self.allocator.destroy(interpreter); + + var ip = try ComptimeInterpreter.InternPool.init(self.allocator); + errdefer ip.deinit(self.allocator); + + interpreter.* = ComptimeInterpreter{ .allocator = self.allocator, + .ip = ip, .document_store = self, .uri = uri, }; - handle.interpreter = int; - _ = try int.interpret(0, .none, .{}); + handle.interpreter = interpreter; } + + _ = try handle.interpreter.?.interpret(0, .none, .{}); + return handle.interpreter.?; } diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index c5a1860..f602721 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -161,7 +161,8 @@ pub const Decl = struct { }; pub const Key = union(enum) { - simple: Simple, + simple_type: SimpleType, + simple_value: SimpleValue, int_type: Int, pointer_type: Pointer, @@ -209,7 +210,8 @@ pub const Key = union(enum) { pub fn tag(key: Key) Tag { return switch (key) { - .simple => .simple, + .simple_type => .simple_type, + .simple_value => .simple_value, .int_type => |int_info| switch (int_info.signedness) { .signed => .type_int_signed, @@ -245,7 +247,7 @@ pub const Key = union(enum) { pub fn zigTypeTag(key: Key) std.builtin.TypeId { return switch (key) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .f16, .f32, .f64, @@ -279,14 +281,6 @@ pub const Key = union(enum) { .null_type => .Null, .undefined_type => .Undefined, .enum_literal_type => .EnumLiteral, - - .undefined_value, - .void_value, - .unreachable_value, - .null_value, - .bool_true, - .bool_false, - => unreachable, }, .int_type => .Int, @@ -303,6 +297,7 @@ pub const Key = union(enum) { .vector_type => .Vector, .anyframe_type => .AnyFrame, + .simple_value, .int_u64_value, .int_i64_value, .int_big_value, @@ -322,46 +317,7 @@ pub const Key = union(enum) { pub fn isType(key: Key) bool { return switch (key) { - .simple => |simple| switch (simple) { - .f16, - .f32, - .f64, - .f80, - .f128, - .c_longdouble, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .comptime_int, - .comptime_float, - .anyopaque, - .bool, - .void, - .type, - .anyerror, - .noreturn, - .@"anyframe", - .null_type, - .undefined_type, - .enum_literal_type, - => true, - - .undefined_value, - .void_value, - .unreachable_value, - .null_value, - .bool_true, - .bool_false, - => false, - }, - + .simple_type, .int_type, .pointer_type, .array_type, @@ -377,6 +333,7 @@ pub const Key = union(enum) { .anyframe_type, => true, + .simple_value, .int_u64_value, .int_i64_value, .int_big_value, @@ -403,7 +360,7 @@ pub const Key = union(enum) { var key: Key = ty; while (true) switch (key) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .usize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, .isize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, @@ -442,7 +399,7 @@ pub const Key = union(enum) { /// Asserts the type is a fixed-size float or comptime_float. /// Returns 128 for comptime_float types. pub fn floatBits(ty: Key, target: std.Target) u16 { - return switch (ty.simple) { + return switch (ty.simple_type) { .f16 => 16, .f32 => 32, .f64 => 64, @@ -489,8 +446,8 @@ pub const Key = union(enum) { pub fn elemType2(ty: Key) Index { return switch (ty) { - .simple => |simple| switch (simple) { - .@"anyframe" => panicOrElse("TODO: return void type", Index.none), + .simple_type => |simple| switch (simple) { + .@"anyframe" => Index.void, else => unreachable, }, .pointer_type => |pointer_info| pointer_info.elem_type, @@ -521,9 +478,9 @@ pub const Key = union(enum) { }; } - pub fn onePossibleValue(ty: Key, ip: InternPool) ?Key { - switch (ty) { - .simple => |simple| switch (simple) { + pub fn onePossibleValue(ty: Key, ip: InternPool) Index { + return switch (ty) { + .simple_type => |simple| switch (simple) { .f16, .f32, .f64, @@ -548,80 +505,67 @@ pub const Key = union(enum) { .comptime_float, .@"anyframe", .enum_literal_type, - => return null, + => Index.none, - .void => return Key{ .simple = .void_value }, - .noreturn => return Key{ .simple = .unreachable_value }, - .null_type => return Key{ .simple = .null_value }, - .undefined_type => return Key{ .simple = .undefined_value }, - - // values - .undefined_value, - .void_value, - .unreachable_value, - .null_value, - .bool_true, - .bool_false, - => unreachable, + .void => Index.void_value, + .noreturn => Index.unreachable_value, + .null_type => Index.null_value, + .undefined_type => Index.undefined_value, }, .int_type => |int_info| { if (int_info.bits == 0) { switch (int_info.signedness) { - .unsigned => return Key{ .int_u64_value = 0 }, - .signed => return Key{ .int_i64_value = 0 }, + .unsigned => return Index.zero, + .signed => return Index.zero, // do we need a signed zero? } } - return null; + return Index.none; }, - .pointer_type => return null, + .pointer_type => Index.none, .array_type => |array_info| { if (array_info.len == 0) { - return panicOrElse("TODO return empty array value", null); - } else if (ip.indexToKey(array_info.child).onePossibleValue(ip)) |value| { - return value; + return panicOrElse("TODO return empty array value", Index.none); } - return null; + return ip.indexToKey(array_info.child).onePossibleValue(ip); }, .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; + if (ip.indexToKey(entry.value_ptr.ty).onePossibleValue(ip) != Index.none) continue; + return Index.none; } - return panicOrElse("TODO return empty struct value", null); + return panicOrElse("TODO return empty struct value", Index.none); }, .optional_type => |optional_info| { - const child = ip.indexToKey(optional_info.payload_type); - if (child == .simple and child.simple == .noreturn) { - return Key{ .simple = .null_value }; + if (optional_info.payload_type == Index.noreturn) { + return Index.null_value; } - return null; + return Index.none; }, - .error_union_type => return null, - .error_set_type => return null, + .error_union_type => Index.none, + .error_set_type => Index.none, .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.values.keys()[0]), - else => return null, - } + return switch (enum_info.fields.count()) { + 0 => Index.unreachable_value, + 1 => enum_info.values.keys()[0], + else => Index.none, + }; }, - .function_type => return null, - .union_type => return panicOrElse("TODO", null), - .tuple_type => return panicOrElse("TODO", null), + .function_type => Index.none, + .union_type => panicOrElse("TODO", Index.none), + .tuple_type => panicOrElse("TODO", Index.none), .vector_type => |vector_info| { if (vector_info.len == 0) { - return panicOrElse("TODO return empty array value", null); - } else if (ip.indexToKey(vector_info.child).onePossibleValue(ip)) |value| { - return value; + return panicOrElse("TODO return empty array value", Index.none); } - return null; + return ip.indexToKey(vector_info.child).onePossibleValue(ip); }, - .anyframe_type => return null, + .anyframe_type => Index.none, + .simple_value, .int_u64_value, .int_i64_value, .int_big_value, @@ -636,7 +580,7 @@ pub const Key = union(enum) { .aggregate, .union_value, => unreachable, - } + }; } pub const TypeFormatContext = struct { @@ -679,7 +623,7 @@ pub const Key = union(enum) { fn printTypeInternal(ty: Key, ip: InternPool, writer: anytype) @TypeOf(writer).Error!?Index { switch (ty) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .f16, .f32, .f64, @@ -710,14 +654,6 @@ pub const Key = union(enum) { .null_type => try writer.writeAll("@TypeOf(null)"), .undefined_type => try writer.writeAll("@TypeOf(undefined)"), .enum_literal_type => try writer.writeAll("@TypeOf(.enum_literal)"), - - .undefined_value, - .void_value, - .unreachable_value, - .null_value, - .bool_true, - .bool_false, - => unreachable, }, .int_type => |int_info| switch (int_info.signedness) { .signed => try writer.print("i{}", .{int_info.bits}), @@ -848,6 +784,7 @@ pub const Key = union(enum) { return anyframe_info.child; }, + .simple_value, .int_u64_value, .int_i64_value, .int_big_value, @@ -884,7 +821,7 @@ pub const Key = union(enum) { writer: anytype, ) @TypeOf(writer).Error!void { switch (value) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .f16, .f32, .f64, @@ -915,8 +852,8 @@ pub const Key = union(enum) { .null_type => try writer.writeAll("@TypeOf(null)"), .undefined_type => try writer.writeAll("@TypeOf(undefined)"), .enum_literal_type => try writer.writeAll("@TypeOf(.enum_literal)"), - - // values + }, + .simple_value => |simple| switch (simple) { .undefined_value => try writer.writeAll("@Type(.Undefined)"), .void_value => try writer.writeAll("void"), .unreachable_value => try writer.writeAll("unreachable"), @@ -1006,6 +943,44 @@ pub const Item = struct { /// the same `InternPool`. /// TODO split this into an Optional and non-Optional Index pub const Index = enum(u32) { + f16, + f32, + f64, + f80, + f128, + usize, + isize, + c_short, + c_ushort, + c_int, + c_uint, + c_long, + c_ulong, + c_longlong, + c_ulonglong, + c_longdouble, + anyopaque, + bool, + void, + type, + anyerror, + comptime_int, + comptime_float, + noreturn, + @"anyframe", + null_type, + undefined_type, + enum_literal_type, + + undefined_value, + void_value, + unreachable_value, + null_value, + bool_true, + bool_false, + + zero, + none = std.math.maxInt(u32), _, @@ -1031,9 +1006,12 @@ pub const NamespaceIndex = enum(u32) { }; pub const Tag = enum(u8) { - /// A type or value that can be represented with only an enum tag. - /// data is Simple enum value - simple, + /// A type that can be represented with only an enum tag. + /// data is SimpleType enum value + simple_type, + /// A value that can be represented with only an enum tag. + /// data is SimpleValue enum value + simple_value, /// An integer type. /// data is number of bits @@ -1123,7 +1101,7 @@ pub const Tag = enum(u8) { union_value, }; -pub const Simple = enum(u32) { +pub const SimpleType = enum(u32) { f16, f32, f64, @@ -1152,58 +1130,73 @@ pub const Simple = enum(u32) { null_type, undefined_type, enum_literal_type, +}; - // values +pub const SimpleValue = enum(u32) { undefined_value, void_value, unreachable_value, null_value, bool_true, bool_false, - - pub fn toType(self: Simple) Simple { - return switch (self) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .anyopaque, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"anyframe", - .null_type, - .undefined_type, - .enum_literal_type, - => .type, - - // values - .undefined_value => .undefined_type, - .void_value => .void, - .unreachable_value => .noreturn, - .null_value => .null_type, - .bool_true => .bool, - .bool_false => .bool, - }; - } }; +comptime { + std.debug.assert(@sizeOf(SimpleType) == @sizeOf(SimpleValue)); +} + +pub fn init(gpa: Allocator) Allocator.Error!InternPool { + var ip: InternPool = .{}; + + const simple_count = std.meta.fields(SimpleType).len + std.meta.fields(SimpleValue).len; + const count = simple_count + 1; + const extra_count = @sizeOf(u64); + + try ip.map.ensureTotalCapacity(gpa, count); + try ip.items.ensureTotalCapacity(gpa, count); + try ip.extra.ensureTotalCapacity(gpa, extra_count); + + _ = ip.get(undefined, .{ .simple_type = .f16 }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .f32 }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .f64 }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .f80 }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .f128 }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .usize }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .isize }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_short }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_ushort }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_int }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_uint }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_long }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_ulong }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_longlong }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_ulonglong }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .c_longdouble }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .anyopaque }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .bool }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .void }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .type }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .anyerror }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .comptime_int }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .comptime_float }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .noreturn }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .@"anyframe" }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .null_type }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .undefined_type }) catch unreachable; + _ = ip.get(undefined, .{ .simple_type = .enum_literal_type }) catch unreachable; + + _ = ip.get(undefined, .{ .simple_value = .undefined_value }) catch unreachable; + _ = ip.get(undefined, .{ .simple_value = .void_value }) catch unreachable; + _ = ip.get(undefined, .{ .simple_value = .unreachable_value }) catch unreachable; + _ = ip.get(undefined, .{ .simple_value = .null_value }) catch unreachable; + _ = ip.get(undefined, .{ .simple_value = .bool_true }) catch unreachable; + _ = ip.get(undefined, .{ .simple_value = .bool_false }) catch unreachable; + + _ = ip.get(undefined, .{ .int_u64_value = 0 }) catch unreachable; + + return ip; +} + pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.map.deinit(gpa); ip.items.deinit(gpa); @@ -1232,7 +1225,8 @@ 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) }, + .simple_type => .{ .simple_type = @intToEnum(SimpleType, data) }, + .simple_value => .{ .simple_value = @intToEnum(SimpleValue, data) }, .type_int_signed => .{ .int_type = .{ .signedness = .signed, @@ -1280,7 +1274,50 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { }; } -/// TODO rename to getOrPut +pub fn indexToTag(ip: InternPool, index: Index) std.builtin.TypeId { + const item = ip.items.get(@enumToInt(index)); + const data = item.data; + return switch (item.tag) { + .simple_type => { + const key = Key{ .simple_type = @intToEnum(SimpleType, data) }; + return key.zigTypeTag(); + }, + + .type_int_signed => .Int, + .type_int_unsigned => .Int, + .type_pointer => .Pointer, + .type_array => .Array, + .type_struct => .Struct, + .type_optional => .Optional, + .type_anyframe => .AnyFrame, + .type_error_union => .ErrorUnion, + .type_error_set => .ErrorSet, + .type_enum => .Enum, + .type_function => .Fn, + .type_union => .Union, + .type_tuple => .Struct, + .type_vector => .Vector, + + .simple_value, + .int_u32, + .int_i32, + .int_u64, + .int_i64, + .int_big_positive, + .int_big_negative, + .float_f16, + .float_f32, + .float_f64, + .float_f80, + .float_f128, + => unreachable, + + .bytes => unreachable, + .aggregate => unreachable, + .union_value => unreachable, + }; +} + pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { const adapter: KeyAdapter = .{ .ip = ip }; const gop = try ip.map.getOrPutAdapted(gpa, key, adapter); @@ -1288,7 +1325,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { const tag: Tag = key.tag(); const data: u32 = switch (key) { - .simple => |simple| @enumToInt(simple), + .simple_type => |simple| @enumToInt(simple), + .simple_value => |simple| @enumToInt(simple), .int_type => |int_ty| int_ty.bits, .optional_type => |optional_ty| @enumToInt(optional_ty.payload_type), @@ -1313,7 +1351,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { return @intToEnum(Index, ip.items.len - 1); } -/// TODO rename to get pub fn contains(ip: InternPool, key: Key) ?Index { const adapter: KeyAdapter = .{ .ip = &ip }; const index = ip.map.getIndexAdapted(key, adapter) orelse return null; @@ -1517,7 +1554,7 @@ pub fn cast(ip: *InternPool, gpa: Allocator, destination_ty: Index, source_ty: I pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, target: std.Target) Allocator.Error!Index { switch (types.len) { - 0 => return try ip.get(gpa, .{ .simple = .noreturn }), + 0 => return Index.noreturn, 1 => return types[0], else => {}, } @@ -1554,9 +1591,9 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t } switch (candidate_key) { - .simple => |candidate_simple| switch (candidate_simple) { + .simple_type => |candidate_simple| switch (candidate_simple) { .f16, .f32, .f64, .f80, .f128 => switch (chosen_key) { - .simple => |chosen_simple| switch (chosen_simple) { + .simple_type => |chosen_simple| switch (chosen_simple) { .f16, .f32, .f64, .f80, .f128 => { if (chosen_key.floatBits(target) < candidate_key.floatBits(target)) { chosen = candidate; @@ -1586,7 +1623,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .c_ulonglong, .c_longdouble, => switch (chosen_key) { - .simple => |chosen_simple| switch (chosen_simple) { + .simple_type => |chosen_simple| switch (chosen_simple) { .usize, .isize, .c_short, @@ -1629,7 +1666,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t .noreturn, .undefined_type => continue, .comptime_int => switch (chosen_key) { - .simple => |chosen_simple| switch (chosen_simple) { + .simple_type => |chosen_simple| switch (chosen_simple) { .f16, .f32, .f64, @@ -1656,7 +1693,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => {}, }, .comptime_float => switch (chosen_key) { - .simple => |chosen_simple| switch (chosen_simple) { + .simple_type => |chosen_simple| switch (chosen_simple) { .f16, .f32, .f64, .f80, .f128 => continue, .comptime_int => { chosen = candidate; @@ -1675,7 +1712,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => {}, }, .int_type => |candidate_info| switch (chosen_key) { - .simple => |chosen_simple| switch (chosen_simple) { + .simple_type => |chosen_simple| switch (chosen_simple) { .usize, .isize, .c_short, @@ -1715,7 +1752,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t else => {}, }, .pointer_type => |candidate_info| switch (chosen_key) { - .simple => |chosen_simple| switch (chosen_simple) { + .simple_type => |chosen_simple| switch (chosen_simple) { .comptime_int => { if (candidate_info.size == .C) { chosen = candidate; @@ -1890,7 +1927,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t } switch (chosen_key) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .noreturn, .undefined_type, => { @@ -1982,7 +2019,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t if (any_are_null) { const opt_ty = switch (chosen_key) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .null_type => chosen, else => try ip.get(gpa, .{ .optional_type = .{ .payload_type = chosen } }), }, @@ -2467,10 +2504,7 @@ fn coerceInMemoryAllowedFns( } }; } - const return_type_key = ip.indexToKey(src_info.return_type); - const is_noreturn = return_type_key == .simple and return_type_key.simple == .noreturn; - - if (!is_noreturn) { + if (src_info.return_type != Index.noreturn) { const rt = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.return_type, src_info.return_type, true, target); if (rt != .ok) { return InMemoryCoercionResult{ .fn_return_type = .{ @@ -2664,7 +2698,7 @@ fn optionalPtrTy( if (child_ptr_key.is_allowzero) return Index.none; // optionals of zero sized types behave like bools, not pointers - if (child_key.onePossibleValue(ip) != null) return Index.none; + if (child_key.onePossibleValue(ip) != Index.none) return Index.none; return child_type; }, @@ -2697,18 +2731,19 @@ fn testExpectFmtValue(ip: InternPool, val: Index, ty: Index, expected: []const u test "simple types" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const null_type = try ip.get(gpa, .{ .simple = .null_type }); - const undefined_type = try ip.get(gpa, .{ .simple = .undefined_type }); - const enum_literal_type = try ip.get(gpa, .{ .simple = .enum_literal_type }); - const undefined_value = try ip.get(gpa, .{ .simple = .undefined_value }); - const void_value = try ip.get(gpa, .{ .simple = .void_value }); - const unreachable_value = try ip.get(gpa, .{ .simple = .unreachable_value }); - const null_value = try ip.get(gpa, .{ .simple = .null_value }); - const bool_true = try ip.get(gpa, .{ .simple = .bool_true }); - const bool_false = try ip.get(gpa, .{ .simple = .bool_false }); + const null_type = try ip.get(gpa, .{ .simple_type = .null_type }); + const undefined_type = try ip.get(gpa, .{ .simple_type = .undefined_type }); + const enum_literal_type = try ip.get(gpa, .{ .simple_type = .enum_literal_type }); + + const undefined_value = try ip.get(gpa, .{ .simple_value = .undefined_value }); + const void_value = try ip.get(gpa, .{ .simple_value = .void_value }); + const unreachable_value = try ip.get(gpa, .{ .simple_value = .unreachable_value }); + const null_value = try ip.get(gpa, .{ .simple_value = .null_value }); + const bool_true = try ip.get(gpa, .{ .simple_value = .bool_true }); + const bool_false = try ip.get(gpa, .{ .simple_value = .bool_false }); try testExpectFmtType(ip, null_type, "@TypeOf(null)"); try testExpectFmtType(ip, undefined_type, "@TypeOf(undefined)"); @@ -2725,7 +2760,7 @@ test "simple types" { test "int type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); @@ -2747,7 +2782,7 @@ test "int type" { test "int value" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const unsigned_zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); @@ -2787,7 +2822,7 @@ test "int value" { test "big int value" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); var result = try std.math.big.int.Managed.init(gpa); @@ -2807,17 +2842,17 @@ test "big int value" { test "float type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const f16_type = try ip.get(gpa, .{ .simple = .f16 }); - const f32_type = try ip.get(gpa, .{ .simple = .f32 }); - const f64_type = try ip.get(gpa, .{ .simple = .f64 }); - const f80_type = try ip.get(gpa, .{ .simple = .f80 }); - const f128_type = try ip.get(gpa, .{ .simple = .f128 }); + const f16_type = try ip.get(gpa, .{ .simple_type = .f16 }); + const f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); + const f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); + const f80_type = try ip.get(gpa, .{ .simple_type = .f80 }); + const f128_type = try ip.get(gpa, .{ .simple_type = .f128 }); - const another_f32_type = try ip.get(gpa, .{ .simple = .f32 }); - const another_f64_type = try ip.get(gpa, .{ .simple = .f64 }); + const another_f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); + const another_f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); try std.testing.expect(f16_type != f32_type); try std.testing.expect(f32_type != f64_type); @@ -2837,7 +2872,7 @@ test "float type" { test "float value" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 }); @@ -2895,7 +2930,7 @@ test "float value" { test "pointer type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); @@ -2980,14 +3015,14 @@ test "pointer type" { test "optional type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const null_value = try ip.get(gpa, .{ .simple = .null_value }); + const null_value = try ip.get(gpa, .{ .simple_value = .null_value }); const u64_42_value = try ip.get(gpa, .{ .int_u64_value = 42 }); const i32_optional_type_0 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_0 } }); @@ -3007,7 +3042,7 @@ test "optional type" { test "error set type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const foo_name = try ip.get(gpa, .{ .bytes = "foo" }); @@ -3035,11 +3070,11 @@ test "error set type" { test "error union type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); const @"error{}!bool" = try ip.get(gpa, .{ .error_union_type = .{ .error_set_type = empty_error_set, @@ -3052,7 +3087,7 @@ test "error union type" { test "array type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); @@ -3084,11 +3119,11 @@ test "array type" { test "struct value" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); const struct_index = try ip.createStruct(gpa, .{ .fields = .{}, @@ -3103,7 +3138,7 @@ test "struct value" { 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 }); + const true_value = try ip.get(gpa, .{ .simple_value = .bool_true }); const aggregate_value = try ip.get(gpa, Key{ .aggregate = &.{ one_value, true_value } }); @@ -3113,12 +3148,12 @@ test "struct value" { test "function type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const type_type = try ip.get(gpa, .{ .simple = .type }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); + const type_type = try ip.get(gpa, .{ .simple_type = .type }); const @"fn(i32) bool" = try ip.get(gpa, .{ .function_type = .{ .args = &.{i32_type}, @@ -3159,11 +3194,11 @@ test "function type" { test "union value" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const f16_type = try ip.get(gpa, .{ .simple = .f16 }); + const f16_type = try ip.get(gpa, .{ .simple_type = .f16 }); const int_value = try ip.get(gpa, .{ .int_u64_value = 1 }); const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 }); @@ -3196,11 +3231,11 @@ test "union value" { test "anyframe type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); const @"anyframe->i32" = try ip.get(gpa, .{ .anyframe_type = .{ .child = i32_type } }); const @"anyframe->bool" = try ip.get(gpa, .{ .anyframe_type = .{ .child = bool_type } }); @@ -3214,11 +3249,11 @@ test "anyframe type" { test "vector type" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); const @"@Vector(2,u32)" = try ip.get(gpa, .{ .vector_type = .{ .len = 2, @@ -3238,7 +3273,7 @@ test "vector type" { test "bytes value" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); var str1: [43]u8 = "https://www.youtube.com/watch?v=dQw4w9WgXcQ".*; @@ -3274,7 +3309,7 @@ test "coerceInMemoryAllowed integers and floats" { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); @@ -3282,8 +3317,8 @@ test "coerceInMemoryAllowed integers and floats" { const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); const i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); - const f32_type = try ip.get(gpa, .{ .simple = .f32 }); - const f64_type = try ip.get(gpa, .{ .simple = .f64 }); + const f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); + const f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, u32_type, true, builtin.target) == .ok); try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, u16_type, true, builtin.target) == .ok); @@ -3302,13 +3337,13 @@ test "coerceInMemoryAllowed integers and floats" { test "resolvePeerTypes" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); - const type_type = try ip.get(gpa, .{ .simple = .type }); - const noreturn_type = try ip.get(gpa, .{ .simple = .noreturn }); - const undefined_type = try ip.get(gpa, .{ .simple = .undefined_type }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); + const type_type = try ip.get(gpa, .{ .simple_type = .type }); + const noreturn_type = try ip.get(gpa, .{ .simple_type = .noreturn }); + const undefined_type = try ip.get(gpa, .{ .simple_type = .undefined_type }); try std.testing.expect(noreturn_type == try ip.resolvePeerTypes(std.testing.allocator, &.{}, builtin.target)); try std.testing.expect(type_type == try ip.resolvePeerTypes(std.testing.allocator, &.{type_type}, builtin.target)); @@ -3324,7 +3359,7 @@ test "resolvePeerTypes" { test "resolvePeerTypes integers and floats" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); @@ -3334,21 +3369,21 @@ test "resolvePeerTypes integers and floats" { const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } }); - const usize_type = try ip.get(gpa, .{ .simple = .usize }); - const isize_type = try ip.get(gpa, .{ .simple = .isize }); + const usize_type = try ip.get(gpa, .{ .simple_type = .usize }); + const isize_type = try ip.get(gpa, .{ .simple_type = .isize }); - const c_short_type = try ip.get(gpa, .{ .simple = .c_short }); - const c_int_type = try ip.get(gpa, .{ .simple = .c_int }); - const c_long_type = try ip.get(gpa, .{ .simple = .c_long }); + const c_short_type = try ip.get(gpa, .{ .simple_type = .c_short }); + const c_int_type = try ip.get(gpa, .{ .simple_type = .c_int }); + const c_long_type = try ip.get(gpa, .{ .simple_type = .c_long }); - const comptime_int_type = try ip.get(gpa, .{ .simple = .comptime_int }); - const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); + const comptime_int_type = try ip.get(gpa, .{ .simple_type = .comptime_int }); + const comptime_float_type = try ip.get(gpa, .{ .simple_type = .comptime_float }); - const f16_type = try ip.get(gpa, .{ .simple = .f16 }); - const f32_type = try ip.get(gpa, .{ .simple = .f32 }); - const f64_type = try ip.get(gpa, .{ .simple = .f64 }); + const f16_type = try ip.get(gpa, .{ .simple_type = .f16 }); + const f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); + const f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); try ip.testResolvePeerTypes(i16_type, i16_type, i16_type); try ip.testResolvePeerTypes(i16_type, i32_type, i32_type); @@ -3432,12 +3467,12 @@ test "resolvePeerTypes integers and floats" { test "resolvePeerTypes optionals" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const null_type = try ip.get(gpa, .{ .simple = .null_type }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const null_type = try ip.get(gpa, .{ .simple_type = .null_type }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); const @"?u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = u32_type } }); const @"?bool" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = bool_type } }); @@ -3449,13 +3484,13 @@ test "resolvePeerTypes optionals" { test "resolvePeerTypes pointers" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const comptime_int_type = try ip.get(gpa, .{ .simple = .comptime_int }); - const comptime_float_type = try ip.get(gpa, .{ .simple = .comptime_float }); - const bool_type = try ip.get(gpa, .{ .simple = .bool }); + const comptime_int_type = try ip.get(gpa, .{ .simple_type = .comptime_int }); + const comptime_float_type = try ip.get(gpa, .{ .simple_type = .comptime_float }); + const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many } }); @@ -3547,10 +3582,10 @@ test "resolvePeerTypes pointers" { test "resolvePeerTypes function pointers" { const gpa = std.testing.allocator; - var ip: InternPool = .{}; + var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const void_type = try ip.get(gpa, .{ .simple = .void }); + const void_type = try ip.get(gpa, .{ .simple_type = .void }); const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true } }); diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 72d6806..25c6f29 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -21,7 +21,7 @@ pub fn dotCompletions( }; switch (inner_key) { - .simple => |simple| switch (simple) { + .simple_type => |simple| switch (simple) { .type => { const ty_key = ip.indexToKey(val); const namespace = ty_key.getNamespace(ip.*); @@ -147,6 +147,7 @@ pub fn dotCompletions( .anyframe_type, => {}, + .simple_value, .int_u64_value, .int_i64_value, .int_big_value, diff --git a/src/analysis.zig b/src/analysis.zig index 38ffbae..f887d84 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -7,6 +7,7 @@ const URI = @import("uri.zig"); const log = std.log.scoped(.analysis); const ast = @import("ast.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); +const InternPool = ComptimeInterpreter.InternPool; var using_trail: std.ArrayList([*]const u8) = undefined; var resolve_trail: std.ArrayList(NodeWithHandle) = undefined; @@ -765,14 +766,13 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl log.info("Invoking interpreter!", .{}); - store.ensureInterpreterExists(handle.uri) catch |err| { + const interpreter = store.ensureInterpreterExists(handle.uri) catch |err| { log.err("Failed to interpret file: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } return null; }; - var interpreter: *ComptimeInterpreter = handle.interpreter.?; const root_namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); @@ -792,16 +792,13 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl return null; }; - const type_type = try interpreter.ip.get(interpreter.allocator, ComptimeInterpreter.Key{ .simple = .type }); - const is_type_val = value.ty == type_type; - return TypeWithHandle{ .type = .{ .data = .{ .@"comptime" = .{ .interpreter = interpreter, .value = value, } }, - .is_type_val = is_type_val, + .is_type_val = value.ty == InternPool.Index.type, }, .handle = node_handle.handle, }; diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index 0b5889d..a146a7e 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -14,32 +14,32 @@ const offsets = zls.offsets; const allocator: std.mem.Allocator = std.testing.allocator; test "ComptimeInterpreter - primitive types" { - try testExpr("true", .{ .simple = .bool }, .{ .simple = .bool_true }); - try testExpr("false", .{ .simple = .bool }, .{ .simple = .bool_false }); - try testExpr("5", .{ .simple = .comptime_int }, .{ .int_u64_value = 5 }); - // TODO try testExpr("-2", .{ .simple = .comptime_int }, .{ .int_i64_value = -2 }); - try testExpr("3.0", .{ .simple = .comptime_float }, null); + try testExpr("true", .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); + try testExpr("false", .{ .simple_type = .bool }, .{ .simple_value = .bool_false }); + try testExpr("5", .{ .simple_type = .comptime_int }, .{ .int_u64_value = 5 }); + // TODO try testExpr("-2", .{ .simple_type = .comptime_int }, .{ .int_i64_value = -2 }); + try testExpr("3.0", .{ .simple_type = .comptime_float }, null); - try testExpr("null", .{ .simple = .null_type }, .{ .simple = .null_value }); - try testExpr("void", .{ .simple = .type }, .{ .simple = .void }); - try testExpr("undefined", .{ .simple = .undefined_type }, .{ .simple = .undefined_value }); - try testExpr("noreturn", .{ .simple = .type }, .{ .simple = .noreturn }); + try testExpr("null", .{ .simple_type = .null_type }, .{ .simple_value = .null_value }); + try testExpr("void", .{ .simple_type = .type }, .{ .simple_type = .void }); + try testExpr("undefined", .{ .simple_type = .undefined_type }, .{ .simple_value = .undefined_value }); + try testExpr("noreturn", .{ .simple_type = .type }, .{ .simple_type = .noreturn }); } test "ComptimeInterpreter - expressions" { if (true) return error.SkipZigTest; // TODO - try testExpr("5 + 3", .{ .simple = .comptime_int }, .{ .int_u64_value = 8 }); - try testExpr("5.2 + 4.2", .{ .simple = .comptime_float }, null); + try testExpr("5 + 3", .{ .simple_type = .comptime_int }, .{ .int_u64_value = 8 }); + try testExpr("5.2 + 4.2", .{ .simple_type = .comptime_float }, null); - try testExpr("3 == 3", .{ .simple = .bool }, .{ .simple = .bool_true }); - try testExpr("5.2 == 2.1", .{ .simple = .bool }, .{ .simple = .bool_false }); + try testExpr("3 == 3", .{ .simple_type = .bool }, .{ .simple_valueclear = .bool_true }); + try testExpr("5.2 == 2.1", .{ .simple_type = .bool }, .{ .simple_value = .bool_false }); - try testExpr("@as(?bool, null) orelse true", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr("@as(?bool, null) orelse true", .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); } test "ComptimeInterpreter - builtins" { if (true) return error.SkipZigTest; // TODO - try testExpr("@as(bool, true)", .{ .simple = .bool }, .{ .simple = .bool_true }); + try testExpr("@as(bool, true)", .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); try testExpr("@as(u32, 3)", .{ .int_type = .{ .signedness = .unsigned, .bits = 32, @@ -64,12 +64,12 @@ test "ComptimeInterpreter - labeled block" { \\blk: { \\ break :blk true; \\} - , .{ .simple = .bool }, .{ .simple = .bool_true }); + , .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); try testExpr( \\blk: { \\ break :blk 3; \\} - , .{ .simple = .comptime_int }, .{ .int_u64_value = 3 }); + , .{ .simple_type = .comptime_int }, .{ .int_u64_value = 3 }); } test "ComptimeInterpreter - if" { @@ -77,18 +77,18 @@ test "ComptimeInterpreter - if" { \\blk: { \\ break :blk if (true) true else false; \\} - , .{ .simple = .bool }, .{ .simple = .bool_true }); + , .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); try testExpr( \\blk: { \\ break :blk if (false) true else false; \\} - , .{ .simple = .bool }, .{ .simple = .bool_false }); + , .{ .simple_type = .bool }, .{ .simple_value = .bool_false }); try testExpr( \\blk: { \\ if (false) break :blk true; \\ break :blk false; \\} - , .{ .simple = .bool }, .{ .simple = .bool_false }); + , .{ .simple_type = .bool }, .{ .simple_value = .bool_false }); // TODO // try testExpr( // \\outer: { @@ -97,7 +97,7 @@ test "ComptimeInterpreter - if" { // \\ }) break :outer true; // \\ break :outer false; // \\} - // , .{ .simple = .bool }, .{ .simple = .bool_true }); + // , .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); } test "ComptimeInterpreter - variable lookup" { @@ -106,7 +106,7 @@ test "ComptimeInterpreter - variable lookup" { \\ var foo = 42; \\ break :blk foo; \\} - , .{ .simple = .comptime_int }, .{ .int_u64_value = 42 }); + , .{ .simple_type = .comptime_int }, .{ .int_u64_value = 42 }); try testExpr( \\blk: { \\ var foo = 1; @@ -114,7 +114,7 @@ test "ComptimeInterpreter - variable lookup" { \\ var baz = 3; \\ break :blk bar; \\} - , .{ .simple = .comptime_int }, .{ .int_u64_value = 2 }); + , .{ .simple_type = .comptime_int }, .{ .int_u64_value = 2 }); var context = try Context.init( \\const bar = foo; @@ -123,7 +123,7 @@ test "ComptimeInterpreter - variable lookup" { defer context.deinit(); const result = try context.interpret(context.findVar("bar")); - try expectEqualKey(context.interpreter.ip, .{ .simple = .comptime_int }, result.ty); + try expectEqualKey(context.interpreter.ip, .{ .simple_type = .comptime_int }, result.ty); try expectEqualKey(context.interpreter.ip, .{ .int_u64_value = 3 }, result.val); } @@ -133,7 +133,7 @@ test "ComptimeInterpreter - field access" { \\ const foo: struct {alpha: u64, beta: bool} = undefined; \\ break :blk foo.beta; \\} - , .{ .simple = .bool }, null); + , .{ .simple_type = .bool }, null); try testExpr( \\blk: { \\ const foo: struct {alpha: u64, beta: bool} = undefined; @@ -152,13 +152,13 @@ test "ComptimeInterpreter - optional operations" { \\ const foo: ?bool = true; \\ break :blk foo.?; \\} - , .{ .simple = .bool }, .{ .simple = .bool_true }); + , .{ .simple_type = .bool }, .{ .simple_value = .bool_true }); try testExpr( \\blk: { \\ const foo: ?bool = true; \\ break :blk foo == null; \\} - , .{ .simple = .bool }, .{ .simple = .bool_false }); + , .{ .simple_type = .bool }, .{ .simple_value = .bool_false }); } test "ComptimeInterpreter - pointer operations" { @@ -168,20 +168,20 @@ test "ComptimeInterpreter - pointer operations" { \\ const foo: []const u8 = ""; \\ break :blk foo.len; \\} - , .{ .simple = .usize }, .{ .bytes = "" }); + , .{ .simple_type = .usize }, .{ .bytes = "" }); try testExpr( \\blk: { \\ const foo = true; \\ break :blk &foo; \\} - , @panic("TODO"), .{ .simple = .bool_true }); + , @panic("TODO"), .{ .simple_value = .bool_true }); try testExpr( \\blk: { \\ const foo = true; \\ const bar = &foo; \\ break :blk bar.*; \\} - , @panic("TODO"), .{ .simple = .bool_true }); + , @panic("TODO"), .{ .simple_value = .bool_true }); } test "ComptimeInterpreter - call return primitive type" { @@ -189,7 +189,7 @@ test "ComptimeInterpreter - call return primitive type" { \\pub fn Foo() type { \\ return bool; \\} - , &.{}, .{ .simple = .bool }); + , &.{}, .{ .simple_type = .bool }); try testCall( \\pub fn Foo() type { @@ -223,17 +223,15 @@ test "ComptimeInterpreter - call return struct" { defer context.deinit(); const result = try context.call(context.findFn("Foo"), &.{}); - try std.testing.expect(result.ty == .simple); - try std.testing.expect(result.ty.simple == .type); + try std.testing.expect(result.ty == .simple_type); + try std.testing.expect(result.ty.simple_type == .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 bool_type = try context.interpreter.ip.get(allocator, .{ .simple = .bool }); - 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); + try std.testing.expect(struct_info.fields.values()[0].ty == Index.bool); } test "ComptimeInterpreter - call comptime argument" { @@ -247,19 +245,19 @@ test "ComptimeInterpreter - call comptime argument" { defer context.deinit(); const result1 = try context.call(context.findFn("Foo"), &.{KV{ - .ty = .{ .simple = .bool }, - .val = .{ .simple = .bool_true }, + .ty = .{ .simple_type = .bool }, + .val = .{ .simple_value = .bool_true }, }}); - try std.testing.expect(result1.ty == .simple); - try std.testing.expect(result1.ty.simple == .type); + try std.testing.expect(result1.ty == .simple_type); + try std.testing.expect(result1.ty.simple_type == .type); try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }, result1.val.?); var result2 = try context.call(context.findFn("Foo"), &.{KV{ - .ty = .{ .simple = .bool }, - .val = .{ .simple = .bool_false }, + .ty = .{ .simple_type = .bool }, + .val = .{ .simple_value = .bool_false }, }}); - try std.testing.expect(result2.ty == .simple); - try std.testing.expect(result2.ty.simple == .type); + try std.testing.expect(result2.ty == .simple_type); + try std.testing.expect(result2.ty.simple_type == .type); try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val.?); } @@ -303,6 +301,7 @@ const Context = struct { interpreter.* = .{ .allocator = allocator, + .ip = try InternPool.init(allocator), .document_store = document_store, .uri = handle.uri, }; @@ -399,7 +398,7 @@ fn testCall( const result = try context.call(context.findFn("Foo"), arguments); - try expectEqualKey(context.interpreter.ip, Key{ .simple = .type }, result.ty); + try expectEqualKey(context.interpreter.ip, Key{ .simple_type = .type }, result.ty); try expectEqualKey(context.interpreter.ip, expected_ty, result.val); } From d98060b3d605abb4535745ee294d56259548e9a5 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 10 Feb 2023 19:10:58 +0100 Subject: [PATCH 71/78] more closely match Zir.Inst.Ref to InternPool.Index --- src/ComptimeInterpreter.zig | 101 +++-- src/analyser/InternPool.zig | 359 ++++++++++++------ src/analysis.zig | 2 +- .../comptime_interpreter.zig | 2 +- 4 files changed, 291 insertions(+), 173 deletions(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index a334455..dd85a38 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -241,7 +241,7 @@ pub fn interpret( else (try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty - if (init_type_value.ty != Index.type) { + if (init_type_value.ty != Index.type_type) { try interpreter.recordError( container_field.ast.type_expr, "expected_type", @@ -267,7 +267,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.type, + .ty = Index.type_type, .val = struct_type, } }; }, @@ -314,7 +314,7 @@ pub fn interpret( if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} }; if (type_value) |v| { - if (v.ty != Index.type) return InterpretResult{ .nothing = {} }; + if (v.ty != Index.type_type) return InterpretResult{ .nothing = {} }; } const decl = interpreter.ip.getDecl(decl_index); @@ -383,46 +383,46 @@ pub fn interpret( const identifier = offsets.nodeToSlice(tree, node_idx); const simples = std.ComptimeStringMap(Index, .{ - .{ "anyerror", Index.anyerror }, - .{ "anyframe", Index.@"anyframe" }, - .{ "anyopaque", Index.anyopaque }, - .{ "bool", Index.bool }, - .{ "c_int", Index.c_int }, - .{ "c_long", Index.c_long }, - .{ "c_longdouble", Index.c_longdouble }, - .{ "c_longlong", Index.c_longlong }, - .{ "c_short", Index.c_short }, - .{ "c_uint", Index.c_uint }, - .{ "c_ulong", Index.c_ulong }, - .{ "c_ulonglong", Index.c_ulonglong }, - .{ "c_ushort", Index.c_ushort }, - .{ "comptime_float", Index.comptime_float }, - .{ "comptime_int", Index.comptime_int }, - .{ "f128", Index.f128 }, - .{ "f16", Index.f16 }, - .{ "f32", Index.f32 }, - .{ "f64", Index.f64 }, - .{ "f80", Index.f80 }, + .{ "anyerror", Index.anyerror_type }, + .{ "anyframe", Index.anyframe_type }, + .{ "anyopaque", Index.anyopaque_type }, + .{ "bool", Index.bool_type }, + .{ "c_int", Index.c_int_type }, + .{ "c_long", Index.c_long_type }, + .{ "c_longdouble", Index.c_longdouble_type }, + .{ "c_longlong", Index.c_longlong_type }, + .{ "c_short", Index.c_short_type }, + .{ "c_uint", Index.c_uint_type }, + .{ "c_ulong", Index.c_ulong_type }, + .{ "c_ulonglong", Index.c_ulonglong_type }, + .{ "c_ushort", Index.c_ushort_type }, + .{ "comptime_float", Index.comptime_float_type }, + .{ "comptime_int", Index.comptime_int_type }, + .{ "f128", Index.f128_type }, + .{ "f16", Index.f16_type }, + .{ "f32", Index.f32_type }, + .{ "f64", Index.f64_type }, + .{ "f80", Index.f80_type }, .{ "false", Index.bool_false }, - .{ "isize", Index.isize }, - .{ "noreturn", Index.noreturn }, + .{ "isize", Index.isize_type }, + .{ "noreturn", Index.noreturn_type }, .{ "null", Index.null_value }, .{ "true", Index.bool_true }, - .{ "type", Index.type }, + .{ "type", Index.type_type }, .{ "undefined", Index.undefined_value }, - .{ "usize", Index.usize }, - .{ "void", Index.void }, + .{ "usize", Index.usize_type }, + .{ "void", Index.void_type }, }); if (simples.get(identifier)) |index| { const ty: Index = switch (index) { .undefined_value => .undefined_type, - .void_value => .void, - .unreachable_value => .noreturn, + .void_value => .void_type, + .unreachable_value => .noreturn_type, .null_value => .null_type, - .bool_true => .bool, - .bool_false => .bool, - else => .type, + .bool_true => .bool_type, + .bool_false => .bool_type, + else => .type_type, }; return InterpretResult{ .value = Value{ .interpreter = interpreter, @@ -436,7 +436,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.type, + .ty = Index.type_type, .val = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{ .signedness = if (identifier[0] == 'u') .unsigned else .signed, .bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk, @@ -537,7 +537,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = data[node_idx].rhs, - .ty = Index.usize, + .ty = Index.usize_type, .val = .none, // TODO resolve length of Slice }, }; @@ -548,7 +548,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = data[node_idx].rhs, - .ty = Index.usize, + .ty = Index.usize_type, .val = .none, // TODO resolve length of Slice }, }; @@ -563,7 +563,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = data[node_idx].rhs, - .ty = Index.comptime_int, + .ty = Index.comptime_int_type, .val = len_value, } }; } @@ -682,7 +682,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.bool, + .ty = Index.bool_type, .val = if (a_value.val == b_value.val) Index.bool_true else Index.bool_false, // TODO eql function required? }, }; @@ -693,7 +693,7 @@ pub fn interpret( if (nl == .failure) return error.CriticalAstFailure; - const number_type = if (nl == .float) Index.comptime_float else Index.comptime_int; + const number_type = if (nl == .float) Index.comptime_float_type else Index.comptime_int_type; const value = try interpreter.ip.get( interpreter.allocator, @@ -841,7 +841,7 @@ pub fn interpret( .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.type, + .ty = Index.type_type, .val = .none, // TODO }, }; @@ -854,7 +854,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.type, + .ty = Index.type_type, .val = value.ty, } }; } @@ -865,7 +865,7 @@ pub fn interpret( const value = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue(); - if (value.ty != Index.type or value.ty == .none) return error.InvalidBuiltin; + if (value.ty != Index.type_type or value.ty == .none) return error.InvalidBuiltin; if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8 if (value.val == .none) return error.InvalidBuiltin; @@ -880,7 +880,7 @@ pub fn interpret( return InterpretResult{ .value = Value{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.bool, + .ty = Index.bool_type, .val = if (has_decl) Index.bool_true else Index.bool_false, } }; } @@ -891,7 +891,7 @@ pub fn interpret( const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue(); const value = try (try interpreter.interpret(params[1], namespace, options)).getValue(); - if (as_type.ty != Index.type) return error.InvalidBuiltin; + if (as_type.ty != Index.type_type) return error.InvalidBuiltin; return InterpretResult{ .value = Value{ @@ -911,10 +911,7 @@ pub fn interpret( const string_literal_type = try interpreter.ip.get(interpreter.allocator, Key{ .pointer_type = .{ .elem_type = try interpreter.ip.get(interpreter.allocator, Key{ .array_type = .{ - .child = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{ - .signedness = .unsigned, - .bits = 8, - } }), + .child = Index.u8_type, .len = @intCast(u64, str.len), .sentinel = try interpreter.ip.get(interpreter.allocator, Key{ .int_u64_value = 0 }), } }), @@ -990,7 +987,7 @@ pub fn interpret( const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, - .ty = Index.type, + .ty = Index.type_type, .val = function_type, .alignment = 0, // TODO .address_space = .generic, // TODO @@ -1037,7 +1034,7 @@ pub fn interpret( const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{}); const value = try result.getValue(); - if (value.ty != Index.bool) { + if (value.ty != Index.bool_type) { try interpreter.recordError( node_idx, "invalid_deref", @@ -1051,7 +1048,7 @@ pub fn interpret( return InterpretResult{ .value = .{ .interpreter = interpreter, .node_idx = node_idx, - .ty = Index.bool, + .ty = Index.bool_type, .val = if (value.val == Index.bool_false) Index.bool_true else Index.bool_false, } }; }, @@ -1142,7 +1139,7 @@ pub fn call( while (ast.nextFnParam(&arg_it)) |param| { if (arg_index >= arguments.len) return error.MissingArguments; var tex = try (try interpreter.interpret(param.type_expr, fn_namespace, options)).getValue(); - if (tex.ty != Index.type) { + if (tex.ty != Index.type_type) { try interpreter.recordError( param.type_expr, "expected_type", diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index f602721..0c74b85 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -277,10 +277,24 @@ pub const Key = union(enum) { .type => .Type, .anyerror => .ErrorSet, .noreturn => .NoReturn, - .@"anyframe" => .AnyFrame, + .anyframe_type => .AnyFrame, .null_type => .Null, .undefined_type => .Undefined, .enum_literal_type => .EnumLiteral, + + .atomic_order => .Enum, + .atomic_rmw_op => .Enum, + .calling_convention => .Enum, + .address_space => .Enum, + .float_mode => .Enum, + .reduce_op => .Enum, + .modifier => .Enum, + .prefetch_options => .Struct, + .export_options => .Struct, + .extern_options => .Struct, + .type_info => .Union, + + .generic_poison => unreachable, }, .int_type => .Int, @@ -293,7 +307,7 @@ pub const Key = union(enum) { .enum_type => .Enum, .function_type => .Fn, .union_type => .Union, - .tuple_type => .Struct, // TODO this correct? + .tuple_type => .Struct, .vector_type => .Vector, .anyframe_type => .AnyFrame, @@ -447,7 +461,7 @@ pub const Key = union(enum) { pub fn elemType2(ty: Key) Index { return switch (ty) { .simple_type => |simple| switch (simple) { - .@"anyframe" => Index.void, + .anyframe_type => Index.void_type, else => unreachable, }, .pointer_type => |pointer_info| pointer_info.elem_type, @@ -503,7 +517,7 @@ pub const Key = union(enum) { .anyerror, .comptime_int, .comptime_float, - .@"anyframe", + .anyframe_type, .enum_literal_type, => Index.none, @@ -511,6 +525,21 @@ pub const Key = union(enum) { .noreturn => Index.unreachable_value, .null_type => Index.null_value, .undefined_type => Index.undefined_value, + + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .modifier, + .prefetch_options, + .export_options, + .extern_options, + .type_info, + => Index.none, + + .generic_poison => unreachable, }, .int_type => |int_info| { if (int_info.bits == 0) { @@ -536,10 +565,10 @@ pub const Key = union(enum) { if (ip.indexToKey(entry.value_ptr.ty).onePossibleValue(ip) != Index.none) continue; return Index.none; } - return panicOrElse("TODO return empty struct value", Index.none); + return Index.empty_struct; }, .optional_type => |optional_info| { - if (optional_info.payload_type == Index.noreturn) { + if (optional_info.payload_type == Index.noreturn_type) { return Index.null_value; } return Index.none; @@ -648,12 +677,25 @@ pub const Key = union(enum) { .comptime_int, .comptime_float, .noreturn, - .@"anyframe", + .anyframe_type, => try writer.writeAll(@tagName(simple)), .null_type => try writer.writeAll("@TypeOf(null)"), .undefined_type => try writer.writeAll("@TypeOf(undefined)"), .enum_literal_type => try writer.writeAll("@TypeOf(.enum_literal)"), + + .atomic_order => try writer.writeAll("std.builtin.AtomicOrder"), + .atomic_rmw_op => try writer.writeAll("std.builtin.AtomicRmwOp"), + .calling_convention => try writer.writeAll("std.builtin.CallingConvention"), + .address_space => try writer.writeAll("std.builtin.AddressSpace"), + .float_mode => try writer.writeAll("std.builtin.FloatMode"), + .reduce_op => try writer.writeAll("std.builtin.ReduceOp"), + .modifier => try writer.writeAll("std.builtin.CallModifier"), + .prefetch_options => try writer.writeAll("std.builtin.PrefetchOptions"), + .export_options => try writer.writeAll("std.builtin.ExportOptions"), + .extern_options => try writer.writeAll("std.builtin.ExternOptions"), + .type_info => try writer.writeAll("std.builtin.Type"), + .generic_poison => unreachable, }, .int_type => |int_info| switch (int_info.signedness) { .signed => try writer.print("i{}", .{int_info.bits}), @@ -821,45 +863,15 @@ pub const Key = union(enum) { writer: anytype, ) @TypeOf(writer).Error!void { switch (value) { - .simple_type => |simple| switch (simple) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .anyopaque, - .bool, - .void, - .type, - .anyerror, - .comptime_int, - .comptime_float, - .noreturn, - .@"anyframe", - => try writer.writeAll(@tagName(simple)), - - .null_type => try writer.writeAll("@TypeOf(null)"), - .undefined_type => try writer.writeAll("@TypeOf(undefined)"), - .enum_literal_type => try writer.writeAll("@TypeOf(.enum_literal)"), - }, + .simple_type => try printType(ty, ip, writer), .simple_value => |simple| switch (simple) { - .undefined_value => try writer.writeAll("@Type(.Undefined)"), - .void_value => try writer.writeAll("void"), + .undefined_value => try writer.writeAll("undefined"), + .void_value => try writer.writeAll("{}"), .unreachable_value => try writer.writeAll("unreachable"), .null_value => try writer.writeAll("null"), .bool_true => try writer.writeAll("true"), .bool_false => try writer.writeAll("false"), + .generic_poison => unreachable, }, .int_type, @@ -943,44 +955,89 @@ pub const Item = struct { /// the same `InternPool`. /// TODO split this into an Optional and non-Optional Index pub const Index = enum(u32) { - f16, - f32, - f64, - f80, - f128, - usize, - isize, - c_short, - c_ushort, - c_int, - c_uint, - c_long, - c_ulong, - c_longlong, - c_ulonglong, - c_longdouble, - anyopaque, - bool, - void, - type, - anyerror, - comptime_int, - comptime_float, - noreturn, - @"anyframe", + u1_type, + u8_type, + i8_type, + u16_type, + i16_type, + u29_type, + u32_type, + i32_type, + u64_type, + i64_type, + u128_type, + i128_type, + usize_type, + isize_type, + c_short_type, + c_ushort_type, + c_int_type, + c_uint_type, + c_long_type, + c_ulong_type, + c_longlong_type, + c_ulonglong_type, + c_longdouble_type, + f16_type, + f32_type, + f64_type, + f80_type, + f128_type, + anyopaque_type, + bool_type, + void_type, + type_type, + anyerror_type, + comptime_int_type, + comptime_float_type, + noreturn_type, + anyframe_type, null_type, undefined_type, enum_literal_type, + atomic_order_type, + atomic_rmw_op_type, + calling_convention_type, + address_space_type, + float_mode_type, + reduce_op_type, + modifier_type, + prefetch_options_type, + export_options_type, + extern_options_type, + type_info_type, + manyptr_u8_type, + manyptr_const_u8_type, + fn_noreturn_no_args_type, + fn_void_no_args_type, + fn_naked_noreturn_no_args_type, + fn_ccc_void_no_args_type, + single_const_pointer_to_comptime_int_type, + const_slice_u8_type, + anyerror_void_error_union_type, + generic_poison_type, + /// `undefined` (untyped) undefined_value, - void_value, - unreachable_value, - null_value, - bool_true, - bool_false, - + /// `0` (comptime_int) zero, + /// `1` (comptime_int) + one, + /// `{}` + void_value, + /// `unreachable` (noreturn type) + unreachable_value, + /// `null` (untyped) + null_value, + /// `true` + bool_true, + /// `false` + bool_false, + /// `.{}` (untyped) + empty_struct, + generic_poison, + unknown = std.math.maxInt(u32) - 1, none = std.math.maxInt(u32), _, @@ -1126,10 +1183,24 @@ pub const SimpleType = enum(u32) { comptime_int, comptime_float, noreturn, - @"anyframe", + anyframe_type, null_type, undefined_type, enum_literal_type, + + atomic_order, + atomic_rmw_op, + calling_convention, + address_space, + float_mode, + reduce_op, + modifier, + prefetch_options, + export_options, + extern_options, + type_info, + + generic_poison, }; pub const SimpleValue = enum(u32) { @@ -1139,6 +1210,7 @@ pub const SimpleValue = enum(u32) { null_value, bool_true, bool_false, + generic_poison, }; comptime { @@ -1148,51 +1220,98 @@ comptime { pub fn init(gpa: Allocator) Allocator.Error!InternPool { var ip: InternPool = .{}; - const simple_count = std.meta.fields(SimpleType).len + std.meta.fields(SimpleValue).len; - const count = simple_count + 1; - const extra_count = @sizeOf(u64); + const items = [_]struct { index: Index, key: Key }{ + .{ .index = .u1_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 1 } } }, + .{ .index = .u8_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 8 } } }, + .{ .index = .i8_type, .key = .{ .int_type = .{ .signedness = .signed, .bits = 8 } } }, + .{ .index = .u16_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 16 } } }, + .{ .index = .i16_type, .key = .{ .int_type = .{ .signedness = .signed, .bits = 16 } } }, + .{ .index = .u29_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 29 } } }, + .{ .index = .u32_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } } }, + .{ .index = .i32_type, .key = .{ .int_type = .{ .signedness = .signed, .bits = 32 } } }, + .{ .index = .u64_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } } }, + .{ .index = .i64_type, .key = .{ .int_type = .{ .signedness = .signed, .bits = 64 } } }, + .{ .index = .u128_type, .key = .{ .int_type = .{ .signedness = .unsigned, .bits = 128 } } }, + .{ .index = .i128_type, .key = .{ .int_type = .{ .signedness = .signed, .bits = 128 } } }, - try ip.map.ensureTotalCapacity(gpa, count); - try ip.items.ensureTotalCapacity(gpa, count); + .{ .index = .usize_type, .key = .{ .simple_type = .usize } }, + .{ .index = .isize_type, .key = .{ .simple_type = .isize } }, + .{ .index = .c_short_type, .key = .{ .simple_type = .c_short } }, + .{ .index = .c_ushort_type, .key = .{ .simple_type = .c_ushort } }, + .{ .index = .c_int_type, .key = .{ .simple_type = .c_int } }, + .{ .index = .c_uint_type, .key = .{ .simple_type = .c_uint } }, + .{ .index = .c_long_type, .key = .{ .simple_type = .c_long } }, + .{ .index = .c_ulong_type, .key = .{ .simple_type = .c_ulong } }, + .{ .index = .c_longlong_type, .key = .{ .simple_type = .c_longlong } }, + .{ .index = .c_ulonglong_type, .key = .{ .simple_type = .c_ulonglong } }, + .{ .index = .c_longdouble_type, .key = .{ .simple_type = .c_longdouble } }, + .{ .index = .f16_type, .key = .{ .simple_type = .f16 } }, + .{ .index = .f32_type, .key = .{ .simple_type = .f32 } }, + .{ .index = .f64_type, .key = .{ .simple_type = .f64 } }, + .{ .index = .f80_type, .key = .{ .simple_type = .f80 } }, + .{ .index = .f128_type, .key = .{ .simple_type = .f128 } }, + .{ .index = .anyopaque_type, .key = .{ .simple_type = .anyopaque } }, + .{ .index = .bool_type, .key = .{ .simple_type = .bool } }, + .{ .index = .void_type, .key = .{ .simple_type = .void } }, + .{ .index = .type_type, .key = .{ .simple_type = .type } }, + .{ .index = .anyerror_type, .key = .{ .simple_type = .anyerror } }, + .{ .index = .comptime_int_type, .key = .{ .simple_type = .comptime_int } }, + .{ .index = .comptime_float_type, .key = .{ .simple_type = .comptime_float } }, + .{ .index = .noreturn_type, .key = .{ .simple_type = .noreturn } }, + .{ .index = .anyframe_type, .key = .{ .simple_type = .anyframe_type } }, + .{ .index = .null_type, .key = .{ .simple_type = .null_type } }, + .{ .index = .undefined_type, .key = .{ .simple_type = .undefined_type } }, + .{ .index = .enum_literal_type, .key = .{ .simple_type = .enum_literal_type } }, + + .{ .index = .atomic_order_type, .key = .{ .simple_type = .atomic_order } }, + .{ .index = .atomic_rmw_op_type, .key = .{ .simple_type = .atomic_rmw_op } }, + .{ .index = .calling_convention_type, .key = .{ .simple_type = .calling_convention } }, + .{ .index = .address_space_type, .key = .{ .simple_type = .address_space } }, + .{ .index = .float_mode_type, .key = .{ .simple_type = .float_mode } }, + .{ .index = .reduce_op_type, .key = .{ .simple_type = .reduce_op } }, + .{ .index = .modifier_type, .key = .{ .simple_type = .modifier } }, + .{ .index = .prefetch_options_type, .key = .{ .simple_type = .prefetch_options } }, + .{ .index = .export_options_type, .key = .{ .simple_type = .export_options } }, + .{ .index = .extern_options_type, .key = .{ .simple_type = .extern_options } }, + .{ .index = .type_info_type, .key = .{ .simple_type = .type_info } }, + .{ .index = .manyptr_u8_type, .key = .{ .pointer_type = .{ .elem_type = .u8_type, .size = .Many } } }, + .{ .index = .manyptr_const_u8_type, .key = .{ .pointer_type = .{ .elem_type = .u8_type, .size = .Many, .is_const = true } } }, + .{ .index = .fn_noreturn_no_args_type, .key = .{ .function_type = .{ .args = &.{}, .return_type = .noreturn_type } } }, + .{ .index = .fn_void_no_args_type, .key = .{ .function_type = .{ .args = &.{}, .return_type = .void_type } } }, + .{ .index = .fn_naked_noreturn_no_args_type, .key = .{ .function_type = .{ .args = &.{}, .return_type = .void_type, .calling_convention = .Naked } } }, + .{ .index = .fn_ccc_void_no_args_type, .key = .{ .function_type = .{ .args = &.{}, .return_type = .void_type, .calling_convention = .C } } }, + .{ .index = .single_const_pointer_to_comptime_int_type, .key = .{ .pointer_type = .{ .elem_type = .comptime_int_type, .size = .One, .is_const = true } } }, + .{ .index = .const_slice_u8_type, .key = .{ .pointer_type = .{ .elem_type = .u8_type, .size = .Slice, .is_const = true } } }, + .{ .index = .anyerror_void_error_union_type, .key = .{ .error_union_type = .{ .error_set_type = .anyerror_type, .payload_type = .void_type } } }, + .{ .index = .generic_poison_type, .key = .{ .simple_type = .generic_poison } }, + + .{ .index = .undefined_value, .key = .{ .simple_value = .undefined_value } }, + .{ .index = .zero, .key = .{ .int_u64_value = 0 } }, + .{ .index = .one, .key = .{ .int_u64_value = 1 } }, + .{ .index = .void_value, .key = .{ .simple_value = .void_value } }, + .{ .index = .unreachable_value, .key = .{ .simple_value = .unreachable_value } }, + .{ .index = .null_value, .key = .{ .simple_value = .null_value } }, + .{ .index = .bool_true, .key = .{ .simple_value = .bool_true } }, + .{ .index = .bool_false, .key = .{ .simple_value = .bool_false } }, + + .{ .index = .empty_struct, .key = .{ .aggregate = &.{} } }, + .{ .index = .generic_poison, .key = .{ .simple_value = .generic_poison } }, + }; + + const extra_count = 4 * @sizeOf(Pointer) + @sizeOf(ErrorUnion) + 4 * @sizeOf(Function); + + try ip.map.ensureTotalCapacity(gpa, items.len); + try ip.items.ensureTotalCapacity(gpa, items.len); try ip.extra.ensureTotalCapacity(gpa, extra_count); - _ = ip.get(undefined, .{ .simple_type = .f16 }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .f32 }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .f64 }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .f80 }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .f128 }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .usize }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .isize }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_short }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_ushort }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_int }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_uint }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_long }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_ulong }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_longlong }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_ulonglong }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .c_longdouble }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .anyopaque }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .bool }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .void }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .type }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .anyerror }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .comptime_int }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .comptime_float }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .noreturn }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .@"anyframe" }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .null_type }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .undefined_type }) catch unreachable; - _ = ip.get(undefined, .{ .simple_type = .enum_literal_type }) catch unreachable; - - _ = ip.get(undefined, .{ .simple_value = .undefined_value }) catch unreachable; - _ = ip.get(undefined, .{ .simple_value = .void_value }) catch unreachable; - _ = ip.get(undefined, .{ .simple_value = .unreachable_value }) catch unreachable; - _ = ip.get(undefined, .{ .simple_value = .null_value }) catch unreachable; - _ = ip.get(undefined, .{ .simple_value = .bool_true }) catch unreachable; - _ = ip.get(undefined, .{ .simple_value = .bool_false }) catch unreachable; - - _ = ip.get(undefined, .{ .int_u64_value = 0 }) catch unreachable; + for (items) |item| { + if (builtin.is_test or builtin.mode == .Debug) { + var failing_allocator = std.testing.FailingAllocator.init(undefined, 0); + std.testing.expectEqual(item.index, ip.get(failing_allocator.allocator(), item.key) catch unreachable) catch unreachable; + } else { + std.debug.assert(item.index == ip.get(undefined, item.key) catch unreachable); + } + } return ip; } @@ -1222,6 +1341,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { } pub fn indexToKey(ip: InternPool, index: Index) Key { + std.debug.assert(index != .none); + std.debug.assert(index != .unknown); const item = ip.items.get(@enumToInt(index)); const data = item.data; return switch (item.tag) { @@ -1554,7 +1675,7 @@ pub fn cast(ip: *InternPool, gpa: Allocator, destination_ty: Index, source_ty: I pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, target: std.Target) Allocator.Error!Index { switch (types.len) { - 0 => return Index.noreturn, + 0 => return Index.noreturn_type, 1 => return types[0], else => {}, } @@ -2504,7 +2625,7 @@ fn coerceInMemoryAllowedFns( } }; } - if (src_info.return_type != Index.noreturn) { + if (src_info.return_type != Index.noreturn_type) { const rt = try ip.coerceInMemoryAllowed(gpa, arena, dest_info.return_type, src_info.return_type, true, target); if (rt != .ok) { return InMemoryCoercionResult{ .fn_return_type = .{ @@ -2749,8 +2870,8 @@ test "simple types" { try testExpectFmtType(ip, undefined_type, "@TypeOf(undefined)"); try testExpectFmtType(ip, enum_literal_type, "@TypeOf(.enum_literal)"); - try testExpectFmtValue(ip, undefined_value, Index.none, "@Type(.Undefined)"); - try testExpectFmtValue(ip, void_value, Index.none, "void"); + try testExpectFmtValue(ip, undefined_value, Index.none, "undefined"); + try testExpectFmtValue(ip, void_value, Index.none, "{}"); try testExpectFmtValue(ip, unreachable_value, Index.none, "unreachable"); try testExpectFmtValue(ip, null_value, Index.none, "null"); try testExpectFmtValue(ip, bool_true, Index.none, "true"); diff --git a/src/analysis.zig b/src/analysis.zig index f887d84..5dccd6f 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -798,7 +798,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl .interpreter = interpreter, .value = value, } }, - .is_type_val = value.ty == InternPool.Index.type, + .is_type_val = value.ty == InternPool.Index.type_type, }, .handle = node_handle.handle, }; diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index a146a7e..bdd24a6 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -231,7 +231,7 @@ test "ComptimeInterpreter - call return struct" { 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 == Index.bool); + try std.testing.expect(struct_info.fields.values()[0].ty == Index.bool_type); } test "ComptimeInterpreter - call comptime argument" { From 83f33f1c167c2dce9303020ea3df299b72ba8703 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 10 Feb 2023 19:39:21 +0100 Subject: [PATCH 72/78] implement intInfo for enums --- src/analyser/InternPool.zig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 0c74b85..14617fe 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -394,7 +394,10 @@ pub const Key = union(enum) { else => unreachable, }, .int_type => |int_info| return int_info, - .enum_type => return panicOrElse("TODO", .{ .signedness = .unsigned, .bits = 0 }), + .enum_type => |enum_index| { + const enum_info = ip.getEnum(enum_index); + key = ip.indexToKey(enum_info.tag_type); + }, .struct_type => |struct_index| { const struct_info = ip.getStruct(struct_index); assert(struct_info.layout == .Packed); @@ -953,7 +956,6 @@ 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) { u1_type, u8_type, @@ -1214,7 +1216,7 @@ pub const SimpleValue = enum(u32) { }; comptime { - std.debug.assert(@sizeOf(SimpleType) == @sizeOf(SimpleValue)); + assert(@sizeOf(SimpleType) == @sizeOf(SimpleValue)); } pub fn init(gpa: Allocator) Allocator.Error!InternPool { @@ -1309,7 +1311,7 @@ pub fn init(gpa: Allocator) Allocator.Error!InternPool { var failing_allocator = std.testing.FailingAllocator.init(undefined, 0); std.testing.expectEqual(item.index, ip.get(failing_allocator.allocator(), item.key) catch unreachable) catch unreachable; } else { - std.debug.assert(item.index == ip.get(undefined, item.key) catch unreachable); + assert(item.index == ip.get(undefined, item.key) catch unreachable); } } @@ -1341,8 +1343,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { } pub fn indexToKey(ip: InternPool, index: Index) Key { - std.debug.assert(index != .none); - std.debug.assert(index != .unknown); + assert(index != .none); + assert(index != .unknown); const item = ip.items.get(@enumToInt(index)); const data = item.data; return switch (item.tag) { From 09c35b8eaa4db1773726021a772e4e1c5854af47 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 10 Feb 2023 22:44:26 +0100 Subject: [PATCH 73/78] update onePossibleValue for arrays --- src/analyser/InternPool.zig | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 14617fe..a6132a1 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -556,9 +556,11 @@ pub const Key = union(enum) { .pointer_type => Index.none, .array_type => |array_info| { if (array_info.len == 0) { - return panicOrElse("TODO return empty array value", Index.none); + return Index.empty_aggregate; + } else if (ip.indexToKey(array_info.child).onePossibleValue(ip) != Index.none) { + return Index.the_only_possible_value; } - return ip.indexToKey(array_info.child).onePossibleValue(ip); + return Index.none; }, .struct_type => |struct_index| { const struct_info = ip.getStruct(struct_index); @@ -568,7 +570,7 @@ pub const Key = union(enum) { if (ip.indexToKey(entry.value_ptr.ty).onePossibleValue(ip) != Index.none) continue; return Index.none; } - return Index.empty_struct; + return Index.empty_aggregate; }, .optional_type => |optional_info| { if (optional_info.payload_type == Index.noreturn_type) { @@ -874,6 +876,7 @@ pub const Key = union(enum) { .null_value => try writer.writeAll("null"), .bool_true => try writer.writeAll("true"), .bool_false => try writer.writeAll("false"), + .the_only_possible_value => try writer.writeAll("(the only possible value)"), .generic_poison => unreachable, }, @@ -1036,7 +1039,8 @@ pub const Index = enum(u32) { /// `false` bool_false, /// `.{}` (untyped) - empty_struct, + empty_aggregate, + the_only_possible_value, generic_poison, unknown = std.math.maxInt(u32) - 1, @@ -1212,6 +1216,7 @@ pub const SimpleValue = enum(u32) { null_value, bool_true, bool_false, + the_only_possible_value, generic_poison, }; @@ -1296,7 +1301,8 @@ pub fn init(gpa: Allocator) Allocator.Error!InternPool { .{ .index = .bool_true, .key = .{ .simple_value = .bool_true } }, .{ .index = .bool_false, .key = .{ .simple_value = .bool_false } }, - .{ .index = .empty_struct, .key = .{ .aggregate = &.{} } }, + .{ .index = .empty_aggregate, .key = .{ .aggregate = &.{} } }, + .{ .index = .the_only_possible_value, .key = .{ .simple_value = .the_only_possible_value } }, .{ .index = .generic_poison, .key = .{ .simple_value = .generic_poison } }, }; From 3fda3b5414893e2200808f73e222a44bdf147e33 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 10 Feb 2023 22:57:46 +0100 Subject: [PATCH 74/78] remove some InternPool test boilerplate --- src/analyser/InternPool.zig | 395 ++++++++++++++---------------------- 1 file changed, 155 insertions(+), 240 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index a6132a1..059a93e 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -2878,12 +2878,12 @@ test "simple types" { try testExpectFmtType(ip, undefined_type, "@TypeOf(undefined)"); try testExpectFmtType(ip, enum_literal_type, "@TypeOf(.enum_literal)"); - try testExpectFmtValue(ip, undefined_value, Index.none, "undefined"); - try testExpectFmtValue(ip, void_value, Index.none, "{}"); - try testExpectFmtValue(ip, unreachable_value, Index.none, "unreachable"); - try testExpectFmtValue(ip, null_value, Index.none, "null"); - try testExpectFmtValue(ip, bool_true, Index.none, "true"); - try testExpectFmtValue(ip, bool_false, Index.none, "false"); + try testExpectFmtValue(ip, undefined_value, .none, "undefined"); + try testExpectFmtValue(ip, void_value, .none, "{}"); + try testExpectFmtValue(ip, unreachable_value, .none, "unreachable"); + try testExpectFmtValue(ip, null_value, .none, "null"); + try testExpectFmtValue(ip, bool_true, .none, "true"); + try testExpectFmtValue(ip, bool_false, .none, "false"); } test "int type" { @@ -2964,8 +2964,8 @@ test "big int value" { const positive_big_int_value = try ip.get(gpa, .{ .int_big_value = result.toConst() }); const negative_big_int_value = try ip.get(gpa, .{ .int_big_value = result.toConst().negate() }); - try testExpectFmtValue(ip, positive_big_int_value, Index.none, "340282366920938463463374607431768211456"); - try testExpectFmtValue(ip, negative_big_int_value, Index.none, "-340282366920938463463374607431768211456"); + try testExpectFmtValue(ip, positive_big_int_value, .none, "340282366920938463463374607431768211456"); + try testExpectFmtValue(ip, negative_big_int_value, .none, "-340282366920938463463374607431768211456"); } test "float type" { @@ -3062,59 +3062,54 @@ test "pointer type" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - - const zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); - const @"*i32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = i32_type, + .elem_type = .i32_type, .size = .One, } }); const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .One, } }); const @"*const volatile u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .One, .is_const = true, .is_volatile = true, } }); const @"*align(4:2:3) u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .One, .alignment = 4, .bit_offset = 2, .host_size = 3, } }); const @"*addrspace(.shared) const u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .One, .is_const = true, .address_space = .shared, } }); const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .Many, } }); const @"[*:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .Many, - .sentinel = zero_value, + .sentinel = .zero, } }); const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .Slice, } }); const @"[:0]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .Slice, - .sentinel = zero_value, + .sentinel = .zero, } }); const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ - .elem_type = u32_type, + .elem_type = .u32_type, .size = .C, } }); @@ -3147,24 +3142,17 @@ test "optional type" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - - const null_value = try ip.get(gpa, .{ .simple_value = .null_value }); const u64_42_value = try ip.get(gpa, .{ .int_u64_value = 42 }); - const i32_optional_type_0 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_0 } }); - const i32_optional_type_1 = try ip.get(gpa, .{ .optional_type = .{ .payload_type = i32_type_1 } }); - const u32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = u32_type } }); + const i32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .i32_type } }); + const u32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .u32_type } }); - try std.testing.expect(i32_optional_type_0 == i32_optional_type_1); - try std.testing.expect(i32_optional_type_0 != u32_optional_type); + try std.testing.expect(i32_optional_type != u32_optional_type); - try testExpectFmtType(ip, i32_optional_type_0, "?i32"); + try testExpectFmtType(ip, i32_optional_type, "?i32"); try testExpectFmtType(ip, u32_optional_type, "?u32"); - try testExpectFmtValue(ip, null_value, u32_optional_type, "null"); + try testExpectFmtValue(ip, .null_value, u32_optional_type, "null"); try testExpectFmtValue(ip, u64_42_value, u32_optional_type, "42"); } @@ -3219,29 +3207,19 @@ test "array type" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i32_type_0 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const i32_type_1 = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const zero_value = try ip.get(gpa, .{ .int_u64_value = 0 }); - - const i32_3_array_type_0 = try ip.get(gpa, .{ .array_type = .{ + const i32_3_array_type = try ip.get(gpa, .{ .array_type = .{ .len = 3, - .child = i32_type_0, - } }); - const i32_3_array_type_1 = try ip.get(gpa, .{ .array_type = .{ - .len = 3, - .child = i32_type_1, + .child = .i32_type, } }); const u32_0_0_array_type = try ip.get(gpa, .{ .array_type = .{ .len = 3, - .child = u32_type, - .sentinel = zero_value, + .child = .u32_type, + .sentinel = .zero, } }); - try std.testing.expect(i32_3_array_type_0 == i32_3_array_type_1); - try std.testing.expect(i32_3_array_type_1 != u32_0_0_array_type); + try std.testing.expect(i32_3_array_type != u32_0_0_array_type); - try testExpectFmtType(ip, i32_3_array_type_0, "[3]i32"); + try testExpectFmtType(ip, i32_3_array_type, "[3]i32"); try testExpectFmtType(ip, u32_0_0_array_type, "[3:0]u32"); } @@ -3251,9 +3229,6 @@ test "struct value" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); - const struct_index = try ip.createStruct(gpa, .{ .fields = .{}, .namespace = .none, @@ -3263,13 +3238,13 @@ test "struct value" { }); 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 }); + 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_value = .bool_true }); - const aggregate_value = try ip.get(gpa, Key{ .aggregate = &.{ one_value, true_value } }); + const aggregate_value = try ip.get(gpa, .{ .aggregate = &.{ one_value, true_value } }); try ip.testExpectFmtValue(aggregate_value, struct_type, ".{.foo = 1, .bar = true}"); } @@ -3280,13 +3255,9 @@ test "function type" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); - const type_type = try ip.get(gpa, .{ .simple_type = .type }); - const @"fn(i32) bool" = try ip.get(gpa, .{ .function_type = .{ - .args = &.{i32_type}, - .return_type = bool_type, + .args = &.{.i32_type}, + .return_type = .bool_type, } }); var args_is_comptime = std.StaticBitSet(32).initEmpty(); @@ -3295,21 +3266,21 @@ test "function type" { args_is_noalias.set(1); const @"fn(comptime type, noalias i32) type" = try ip.get(gpa, .{ .function_type = .{ - .args = &.{ type_type, i32_type }, + .args = &.{ .type_type, .i32_type }, .args_is_comptime = args_is_comptime, .args_is_noalias = args_is_noalias, - .return_type = type_type, + .return_type = .type_type, } }); const @"fn(i32, ...) type" = try ip.get(gpa, .{ .function_type = .{ - .args = &.{i32_type}, - .return_type = type_type, + .args = &.{.i32_type}, + .return_type = .type_type, .is_var_args = true, } }); const @"fn() align(4) callconv(.C) type" = try ip.get(gpa, .{ .function_type = .{ .args = &.{}, - .return_type = type_type, + .return_type = .type_type, .alignment = 4, .calling_convention = .C, } }); @@ -3326,9 +3297,6 @@ test "union value" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const f16_type = try ip.get(gpa, .{ .simple_type = .f16 }); - const int_value = try ip.get(gpa, .{ .int_u64_value = 1 }); const f16_value = try ip.get(gpa, .{ .float_16_value = 0.25 }); @@ -3341,8 +3309,8 @@ test "union value" { }); 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 }); + 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, @@ -3363,11 +3331,8 @@ test "anyframe type" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); - - const @"anyframe->i32" = try ip.get(gpa, .{ .anyframe_type = .{ .child = i32_type } }); - const @"anyframe->bool" = try ip.get(gpa, .{ .anyframe_type = .{ .child = bool_type } }); + const @"anyframe->i32" = try ip.get(gpa, .{ .anyframe_type = .{ .child = .i32_type } }); + const @"anyframe->bool" = try ip.get(gpa, .{ .anyframe_type = .{ .child = .bool_type } }); try std.testing.expect(@"anyframe->i32" != @"anyframe->bool"); @@ -3381,16 +3346,13 @@ test "vector type" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); - const @"@Vector(2,u32)" = try ip.get(gpa, .{ .vector_type = .{ .len = 2, - .child = u32_type, + .child = .u32_type, } }); const @"@Vector(2,bool)" = try ip.get(gpa, .{ .vector_type = .{ .len = 2, - .child = bool_type, + .child = .bool_type, } }); try std.testing.expect(@"@Vector(2,u32)" != @"@Vector(2,bool)"); @@ -3441,26 +3403,18 @@ test "coerceInMemoryAllowed integers and floats" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const u16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 16 } }); - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .u32_type, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .u16_type, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u16_type, .u32_type, true, builtin.target) == .int_not_coercible); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .i32_type, .u32_type, true, builtin.target) == .int_not_coercible); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .i32_type, true, builtin.target) == .int_not_coercible); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .i16_type, true, builtin.target) == .int_not_coercible); - const f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); - const f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); - - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, u32_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, u16_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u16_type, u32_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, i32_type, u32_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, i32_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, i16_type, true, builtin.target) == .int_not_coercible); - - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f32_type, f32_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f64_type, f32_type, true, builtin.target) == .no_match); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f32_type, f64_type, true, builtin.target) == .no_match); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, u32_type, f32_type, true, builtin.target) == .no_match); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, f32_type, u32_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .f32_type, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f64_type, .f32_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .f64_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .f32_type, true, builtin.target) == .no_match); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .u32_type, true, builtin.target) == .no_match); } test "resolvePeerTypes" { @@ -3469,20 +3423,15 @@ test "resolvePeerTypes" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); - const type_type = try ip.get(gpa, .{ .simple_type = .type }); - const noreturn_type = try ip.get(gpa, .{ .simple_type = .noreturn }); - const undefined_type = try ip.get(gpa, .{ .simple_type = .undefined_type }); + try std.testing.expect(.noreturn_type == try ip.resolvePeerTypes(std.testing.allocator, &.{}, builtin.target)); + try std.testing.expect(.type_type == try ip.resolvePeerTypes(std.testing.allocator, &.{.type_type}, builtin.target)); - try std.testing.expect(noreturn_type == try ip.resolvePeerTypes(std.testing.allocator, &.{}, builtin.target)); - try std.testing.expect(type_type == try ip.resolvePeerTypes(std.testing.allocator, &.{type_type}, builtin.target)); - - try ip.testResolvePeerTypes(Index.none, Index.none, Index.none); - try ip.testResolvePeerTypes(bool_type, bool_type, bool_type); - try ip.testResolvePeerTypes(bool_type, noreturn_type, bool_type); - try ip.testResolvePeerTypes(bool_type, undefined_type, bool_type); - try ip.testResolvePeerTypes(type_type, noreturn_type, type_type); - try ip.testResolvePeerTypes(type_type, undefined_type, type_type); + try ip.testResolvePeerTypes(.none, .none, .none); + try ip.testResolvePeerTypes(.bool_type, .bool_type, .bool_type); + try ip.testResolvePeerTypes(.bool_type, .noreturn_type, .bool_type); + try ip.testResolvePeerTypes(.bool_type, .undefined_type, .bool_type); + try ip.testResolvePeerTypes(.type_type, .noreturn_type, .type_type); + try ip.testResolvePeerTypes(.type_type, .undefined_type, .type_type); } test "resolvePeerTypes integers and floats" { @@ -3491,106 +3440,83 @@ test "resolvePeerTypes integers and floats" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const i16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 16 } }); - const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - const i64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 64 } }); - const u16_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 16 } }); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const u64_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 64 } }); + try ip.testResolvePeerTypes(.i16_type, .i16_type, .i16_type); + try ip.testResolvePeerTypes(.i16_type, .i32_type, .i32_type); + try ip.testResolvePeerTypes(.i32_type, .i64_type, .i64_type); - const usize_type = try ip.get(gpa, .{ .simple_type = .usize }); - const isize_type = try ip.get(gpa, .{ .simple_type = .isize }); + try ip.testResolvePeerTypes(.u16_type, .u16_type, .u16_type); + try ip.testResolvePeerTypes(.u16_type, .u32_type, .u32_type); + try ip.testResolvePeerTypes(.u32_type, .u64_type, .u64_type); - const c_short_type = try ip.get(gpa, .{ .simple_type = .c_short }); - const c_int_type = try ip.get(gpa, .{ .simple_type = .c_int }); - const c_long_type = try ip.get(gpa, .{ .simple_type = .c_long }); + try ip.testResolvePeerTypesInOrder(.i16_type, .u16_type, .i16_type); + try ip.testResolvePeerTypesInOrder(.u16_type, .i16_type, .u16_type); + try ip.testResolvePeerTypesInOrder(.i32_type, .u32_type, .i32_type); + try ip.testResolvePeerTypesInOrder(.u32_type, .i32_type, .u32_type); + try ip.testResolvePeerTypesInOrder(.isize_type, .usize_type, .isize_type); + try ip.testResolvePeerTypesInOrder(.usize_type, .isize_type, .usize_type); - const comptime_int_type = try ip.get(gpa, .{ .simple_type = .comptime_int }); - const comptime_float_type = try ip.get(gpa, .{ .simple_type = .comptime_float }); + try ip.testResolvePeerTypes(.i16_type, .u32_type, .u32_type); + try ip.testResolvePeerTypes(.u16_type, .i32_type, .i32_type); + try ip.testResolvePeerTypes(.i32_type, .u64_type, .u64_type); + try ip.testResolvePeerTypes(.u32_type, .i64_type, .i64_type); - const f16_type = try ip.get(gpa, .{ .simple_type = .f16 }); - const f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); - const f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); + try ip.testResolvePeerTypes(.i16_type, .usize_type, .usize_type); + try ip.testResolvePeerTypes(.i16_type, .isize_type, .isize_type); + try ip.testResolvePeerTypes(.u16_type, .usize_type, .usize_type); + try ip.testResolvePeerTypes(.u16_type, .isize_type, .isize_type); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); + try ip.testResolvePeerTypes(.c_short_type, .usize_type, .usize_type); + try ip.testResolvePeerTypes(.c_short_type, .isize_type, .isize_type); - try ip.testResolvePeerTypes(i16_type, i16_type, i16_type); - try ip.testResolvePeerTypes(i16_type, i32_type, i32_type); - try ip.testResolvePeerTypes(i32_type, i64_type, i64_type); + try ip.testResolvePeerTypes(.i16_type, .c_long_type, .c_long_type); + try ip.testResolvePeerTypes(.i16_type, .c_long_type, .c_long_type); + try ip.testResolvePeerTypes(.u16_type, .c_long_type, .c_long_type); + try ip.testResolvePeerTypes(.u16_type, .c_long_type, .c_long_type); - try ip.testResolvePeerTypes(u16_type, u16_type, u16_type); - try ip.testResolvePeerTypes(u16_type, u32_type, u32_type); - try ip.testResolvePeerTypes(u32_type, u64_type, u64_type); + try ip.testResolvePeerTypes(.comptime_int_type, .i16_type, .i16_type); + try ip.testResolvePeerTypes(.comptime_int_type, .u64_type, .u64_type); + try ip.testResolvePeerTypes(.comptime_int_type, .isize_type, .isize_type); + try ip.testResolvePeerTypes(.comptime_int_type, .usize_type, .usize_type); + try ip.testResolvePeerTypes(.comptime_int_type, .c_short_type, .c_short_type); + try ip.testResolvePeerTypes(.comptime_int_type, .c_int_type, .c_int_type); + try ip.testResolvePeerTypes(.comptime_int_type, .c_long_type, .c_long_type); - try ip.testResolvePeerTypesInOrder(i16_type, u16_type, i16_type); - try ip.testResolvePeerTypesInOrder(u16_type, i16_type, u16_type); - try ip.testResolvePeerTypesInOrder(i32_type, u32_type, i32_type); - try ip.testResolvePeerTypesInOrder(u32_type, i32_type, u32_type); - try ip.testResolvePeerTypesInOrder(isize_type, usize_type, isize_type); - try ip.testResolvePeerTypesInOrder(usize_type, isize_type, usize_type); + try ip.testResolvePeerTypes(.comptime_float_type, .i16_type, .none); + try ip.testResolvePeerTypes(.comptime_float_type, .u64_type, .none); + try ip.testResolvePeerTypes(.comptime_float_type, .isize_type, .none); + try ip.testResolvePeerTypes(.comptime_float_type, .usize_type, .none); + try ip.testResolvePeerTypes(.comptime_float_type, .c_short_type, .none); + try ip.testResolvePeerTypes(.comptime_float_type, .c_int_type, .none); + try ip.testResolvePeerTypes(.comptime_float_type, .c_long_type, .none); - try ip.testResolvePeerTypes(i16_type, u32_type, u32_type); - try ip.testResolvePeerTypes(u16_type, i32_type, i32_type); - try ip.testResolvePeerTypes(i32_type, u64_type, u64_type); - try ip.testResolvePeerTypes(u32_type, i64_type, i64_type); + try ip.testResolvePeerTypes(.comptime_float_type, .comptime_int_type, .comptime_float_type); - try ip.testResolvePeerTypes(i16_type, usize_type, usize_type); - try ip.testResolvePeerTypes(i16_type, isize_type, isize_type); - try ip.testResolvePeerTypes(u16_type, usize_type, usize_type); - try ip.testResolvePeerTypes(u16_type, isize_type, isize_type); + try ip.testResolvePeerTypes(.f16_type, .f32_type, .f32_type); + try ip.testResolvePeerTypes(.f32_type, .f64_type, .f64_type); - try ip.testResolvePeerTypes(c_short_type, usize_type, usize_type); - try ip.testResolvePeerTypes(c_short_type, isize_type, isize_type); + try ip.testResolvePeerTypes(.comptime_int_type, .f16_type, .f16_type); + try ip.testResolvePeerTypes(.comptime_int_type, .f32_type, .f32_type); + try ip.testResolvePeerTypes(.comptime_int_type, .f64_type, .f64_type); - try ip.testResolvePeerTypes(i16_type, c_long_type, c_long_type); - try ip.testResolvePeerTypes(i16_type, c_long_type, c_long_type); - try ip.testResolvePeerTypes(u16_type, c_long_type, c_long_type); - try ip.testResolvePeerTypes(u16_type, c_long_type, c_long_type); + try ip.testResolvePeerTypes(.comptime_float_type, .f16_type, .f16_type); + try ip.testResolvePeerTypes(.comptime_float_type, .f32_type, .f32_type); + try ip.testResolvePeerTypes(.comptime_float_type, .f64_type, .f64_type); - try ip.testResolvePeerTypes(comptime_int_type, i16_type, i16_type); - try ip.testResolvePeerTypes(comptime_int_type, u64_type, u64_type); - try ip.testResolvePeerTypes(comptime_int_type, isize_type, isize_type); - try ip.testResolvePeerTypes(comptime_int_type, usize_type, usize_type); - try ip.testResolvePeerTypes(comptime_int_type, c_short_type, c_short_type); - try ip.testResolvePeerTypes(comptime_int_type, c_int_type, c_int_type); - try ip.testResolvePeerTypes(comptime_int_type, c_long_type, c_long_type); + try ip.testResolvePeerTypes(.f16_type, .i16_type, .none); + try ip.testResolvePeerTypes(.f32_type, .u64_type, .none); + try ip.testResolvePeerTypes(.f64_type, .isize_type, .none); + try ip.testResolvePeerTypes(.f16_type, .usize_type, .none); + try ip.testResolvePeerTypes(.f32_type, .c_short_type, .none); + try ip.testResolvePeerTypes(.f64_type, .c_int_type, .none); + try ip.testResolvePeerTypes(.f64_type, .c_long_type, .none); - try ip.testResolvePeerTypes(comptime_float_type, i16_type, Index.none); - try ip.testResolvePeerTypes(comptime_float_type, u64_type, Index.none); - try ip.testResolvePeerTypes(comptime_float_type, isize_type, Index.none); - try ip.testResolvePeerTypes(comptime_float_type, usize_type, Index.none); - try ip.testResolvePeerTypes(comptime_float_type, c_short_type, Index.none); - try ip.testResolvePeerTypes(comptime_float_type, c_int_type, Index.none); - try ip.testResolvePeerTypes(comptime_float_type, c_long_type, Index.none); - - try ip.testResolvePeerTypes(comptime_float_type, comptime_int_type, comptime_float_type); - - try ip.testResolvePeerTypes(f16_type, f32_type, f32_type); - try ip.testResolvePeerTypes(f32_type, f64_type, f64_type); - - try ip.testResolvePeerTypes(comptime_int_type, f16_type, f16_type); - try ip.testResolvePeerTypes(comptime_int_type, f32_type, f32_type); - try ip.testResolvePeerTypes(comptime_int_type, f64_type, f64_type); - - try ip.testResolvePeerTypes(comptime_float_type, f16_type, f16_type); - try ip.testResolvePeerTypes(comptime_float_type, f32_type, f32_type); - try ip.testResolvePeerTypes(comptime_float_type, f64_type, f64_type); - - try ip.testResolvePeerTypes(f16_type, i16_type, Index.none); - try ip.testResolvePeerTypes(f32_type, u64_type, Index.none); - try ip.testResolvePeerTypes(f64_type, isize_type, Index.none); - try ip.testResolvePeerTypes(f16_type, usize_type, Index.none); - try ip.testResolvePeerTypes(f32_type, c_short_type, Index.none); - try ip.testResolvePeerTypes(f64_type, c_int_type, Index.none); - try ip.testResolvePeerTypes(f64_type, c_long_type, Index.none); - - try ip.testResolvePeerTypes(bool_type, i16_type, Index.none); - try ip.testResolvePeerTypes(bool_type, u64_type, Index.none); - try ip.testResolvePeerTypes(bool_type, usize_type, Index.none); - try ip.testResolvePeerTypes(bool_type, c_int_type, Index.none); - try ip.testResolvePeerTypes(bool_type, comptime_int_type, Index.none); - try ip.testResolvePeerTypes(bool_type, comptime_float_type, Index.none); - try ip.testResolvePeerTypes(bool_type, f32_type, Index.none); + try ip.testResolvePeerTypes(.bool_type, .i16_type, .none); + try ip.testResolvePeerTypes(.bool_type, .u64_type, .none); + try ip.testResolvePeerTypes(.bool_type, .usize_type, .none); + try ip.testResolvePeerTypes(.bool_type, .c_int_type, .none); + try ip.testResolvePeerTypes(.bool_type, .comptime_int_type, .none); + try ip.testResolvePeerTypes(.bool_type, .comptime_float_type, .none); + try ip.testResolvePeerTypes(.bool_type, .f32_type, .none); } test "resolvePeerTypes optionals" { @@ -3599,15 +3525,11 @@ test "resolvePeerTypes optionals" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const null_type = try ip.get(gpa, .{ .simple_type = .null_type }); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); + const @"?u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .u32_type } }); + const @"?bool" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .bool_type } }); - const @"?u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = u32_type } }); - const @"?bool" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = bool_type } }); - - try ip.testResolvePeerTypes(u32_type, null_type, @"?u32"); - try ip.testResolvePeerTypes(bool_type, null_type, @"?bool"); + try ip.testResolvePeerTypes(.u32_type, .null_type, @"?u32"); + try ip.testResolvePeerTypes(.bool_type, .null_type, @"?bool"); } test "resolvePeerTypes pointers" { @@ -3616,15 +3538,10 @@ test "resolvePeerTypes pointers" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const comptime_int_type = try ip.get(gpa, .{ .simple_type = .comptime_int }); - const comptime_float_type = try ip.get(gpa, .{ .simple_type = .comptime_float }); - const bool_type = try ip.get(gpa, .{ .simple_type = .bool }); - - const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); - const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many } }); - const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice } }); - const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C } }); + const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .One } }); + const @"[*]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .Many } }); + const @"[]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .Slice } }); + const @"[*c]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .C } }); const @"?*u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*u32" } }); const @"?[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]u32" } }); @@ -3638,8 +3555,8 @@ test "resolvePeerTypes pointers" { const @"?*[*]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[*]u32" } }); const @"?*[]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[]u32" } }); - const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 1, .child = u32_type, .sentinel = .none } }); - const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 2, .child = u32_type, .sentinel = .none } }); + const @"[1]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 1, .child = .u32_type, .sentinel = .none } }); + const @"[2]u32" = try ip.get(gpa, .{ .array_type = .{ .len = 2, .child = .u32_type, .sentinel = .none } }); const @"*[1]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[1]u32", .size = .One } }); const @"*[2]u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = @"[2]u32", .size = .One } }); @@ -3647,10 +3564,10 @@ test "resolvePeerTypes pointers" { const @"?*[1]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[1]u32" } }); const @"?*[2]u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*[2]u32" } }); - const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true } }); - const @"[*]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Many, .is_const = true } }); - const @"[]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .Slice, .is_const = true } }); - const @"[*c]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .C, .is_const = true } }); + const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .One, .is_const = true } }); + const @"[*]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .Many, .is_const = true } }); + const @"[]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .Slice, .is_const = true } }); + const @"[*c]const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .C, .is_const = true } }); const @"?*const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"*const u32" } }); const @"?[*]const u32" = try ip.get(gpa, .{ .optional_type = .{ .payload_type = @"[*]const u32" } }); @@ -3679,21 +3596,21 @@ test "resolvePeerTypes pointers" { try ip.testResolvePeerTypesInOrder(@"*u32", @"?[*]u32", @"?[*]u32"); try ip.testResolvePeerTypesInOrder(@"[*]u32", @"?*u32", @"?*u32"); - try ip.testResolvePeerTypes(@"[*c]u32", comptime_int_type, @"[*c]u32"); - try ip.testResolvePeerTypes(@"[*c]u32", u32_type, @"[*c]u32"); - try ip.testResolvePeerTypes(@"[*c]u32", comptime_float_type, Index.none); - try ip.testResolvePeerTypes(@"[*c]u32", bool_type, Index.none); + try ip.testResolvePeerTypes(@"[*c]u32", .comptime_int_type, @"[*c]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", .u32_type, @"[*c]u32"); + try ip.testResolvePeerTypes(@"[*c]u32", .comptime_float_type, .none); + try ip.testResolvePeerTypes(@"[*c]u32", .bool_type, .none); try ip.testResolvePeerTypes(@"[*c]u32", @"*u32", @"[*c]u32"); try ip.testResolvePeerTypes(@"[*c]u32", @"[*]u32", @"[*c]u32"); try ip.testResolvePeerTypes(@"[*c]u32", @"[]u32", @"[*c]u32"); - try ip.testResolvePeerTypes(@"[*c]u32", @"*[1]u32", Index.none); + try ip.testResolvePeerTypes(@"[*c]u32", @"*[1]u32", .none); try ip.testResolvePeerTypesInOrder(@"[*c]u32", @"?*[1]u32", @"?*[1]u32"); - try ip.testResolvePeerTypesInOrder(@"?*[1]u32", @"[*c]u32", Index.none); - try ip.testResolvePeerTypes(@"[*c]u32", @"*[*]u32", Index.none); + try ip.testResolvePeerTypesInOrder(@"?*[1]u32", @"[*c]u32", .none); + try ip.testResolvePeerTypes(@"[*c]u32", @"*[*]u32", .none); try ip.testResolvePeerTypesInOrder(@"[*c]u32", @"?*[*]u32", @"?*[*]u32"); - try ip.testResolvePeerTypesInOrder(@"?*[*]u32", @"[*c]u32", Index.none); + try ip.testResolvePeerTypesInOrder(@"?*[*]u32", @"[*c]u32", .none); try ip.testResolvePeerTypes(@"[*c]u32", @"[]u32", @"[*c]u32"); // TODO try ip.testResolvePeerTypesInOrder(@"[*c]u32", @"?[]u32", @"?[]u32"); // TODO try ip.testResolvePeerTypesInOrder(@"?[]u32", @"[*c]u32", Index.none); @@ -3714,19 +3631,17 @@ test "resolvePeerTypes function pointers" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const void_type = try ip.get(gpa, .{ .simple_type = .void }); - const u32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }); - const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One } }); - const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = u32_type, .size = .One, .is_const = true } }); + const @"*u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .One } }); + const @"*const u32" = try ip.get(gpa, .{ .pointer_type = .{ .elem_type = .u32_type, .size = .One, .is_const = true } }); const @"fn(*u32) void" = try ip.get(gpa, .{ .function_type = .{ .args = &.{@"*u32"}, - .return_type = void_type, + .return_type = .void_type, } }); const @"fn(*const u32) void" = try ip.get(gpa, .{ .function_type = .{ .args = &.{@"*const u32"}, - .return_type = void_type, + .return_type = .void_type, } }); try ip.testResolvePeerTypes(@"fn(*u32) void", @"fn(*u32) void", @"fn(*u32) void"); From 5cb0c98db14314304c71adb2311c6d5255618f54 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 11 Feb 2023 00:04:08 +0100 Subject: [PATCH 75/78] fix function call resolution --- src/ComptimeInterpreter.zig | 8 +++++++- src/analyser/InternPool.zig | 1 + tests/language_features/comptime_interpreter.zig | 13 +++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ComptimeInterpreter.zig b/src/ComptimeInterpreter.zig index dd85a38..973d65b 100644 --- a/src/ComptimeInterpreter.zig +++ b/src/ComptimeInterpreter.zig @@ -285,6 +285,7 @@ pub fn interpret( const name = analysis.getDeclName(tree, node_idx).?; const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, + .node_idx = node_idx, .ty = .none, .val = .none, .alignment = 0, // TODO @@ -450,7 +451,7 @@ pub fn interpret( if (decl.ty == .none) return InterpretResult{ .nothing = {} }; return InterpretResult{ .value = Value{ .interpreter = interpreter, - .node_idx = node_idx, + .node_idx = decl.node_idx, .ty = decl.ty, .val = decl.val, } }; @@ -987,6 +988,7 @@ pub fn interpret( const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, + .node_idx = node_idx, .ty = Index.type_type, .val = function_type, .alignment = 0, // TODO @@ -1122,6 +1124,9 @@ pub fn call( // TODO: type check args const tree = interpreter.getHandle().tree; + const node_tags = tree.nodes.items(.tag); + + if (node_tags[func_node_idx] != .fn_decl) return error.CriticalAstFailure; var buf: [1]Ast.Node.Index = undefined; var proto = tree.fullFnProto(&buf, func_node_idx) orelse return error.CriticalAstFailure; @@ -1154,6 +1159,7 @@ pub fn call( const decls = &interpreter.namespaces.items(.decls)[@enumToInt(fn_namespace)]; const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{ .name = name, + .node_idx = name_token, .ty = tex.val, .val = arguments[arg_index].val, .alignment = 0, // TODO diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 059a93e..50c20d2 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -152,6 +152,7 @@ pub const DeclIndex = enum(u32) { _ }; pub const Decl = struct { name: []const u8, + node_idx: u32, ty: Index, val: Index, alignment: u16, diff --git a/tests/language_features/comptime_interpreter.zig b/tests/language_features/comptime_interpreter.zig index bdd24a6..4f7c9c4 100644 --- a/tests/language_features/comptime_interpreter.zig +++ b/tests/language_features/comptime_interpreter.zig @@ -261,6 +261,17 @@ test "ComptimeInterpreter - call comptime argument" { try std.testing.expectEqual(Key{ .int_type = .{ .signedness = .unsigned, .bits = 69 } }, result2.val.?); } +test "ComptimeInterpreter - call inner function" { + try testCall( + \\pub fn Inner() type { + \\ return bool; + \\} + \\pub fn Foo() type { + \\ return Inner(); + \\} + , &.{}, .{ .simple_type = .bool }); +} + // // Helper functions // @@ -299,6 +310,8 @@ const Context = struct { const handle = try document_store.openDocument(test_uri, source); + // TODO handle handle.tree.errors + interpreter.* = .{ .allocator = allocator, .ip = try InternPool.init(allocator), From 6d387bca11de92c45d74d0f125094d7f336e5979 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 11 Feb 2023 00:34:30 +0100 Subject: [PATCH 76/78] improve comptime interpreter dot completions --- src/Server.zig | 6 +++- src/analyser/completions.zig | 69 +++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/Server.zig b/src/Server.zig index 61d3aa8..14863e2 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -597,7 +597,11 @@ fn typeToCompletion( ), .primitive, .array_index => {}, .@"comptime" => |co| { - try analyser.completions.dotCompletions(allocator, list, &co.interpreter.ip, co.value.ty, co.value.val, co.value.node_idx); + if (type_handle.type.is_type_val) { + try analyser.completions.dotCompletions(allocator, list, &co.interpreter.ip, co.value.ty, co.value.val, co.value.node_idx); + } else { + try analyser.completions.dotCompletions(allocator, list, &co.interpreter.ip, co.value.val, .none, co.value.node_idx); + } }, } } diff --git a/src/analyser/completions.zig b/src/analyser/completions.zig index 25c6f29..bddeeeb 100644 --- a/src/analyser/completions.zig +++ b/src/analyser/completions.zig @@ -64,18 +64,18 @@ pub fn dotCompletions( try completions.append(arena, .{ .label = "ptr", .kind = .Field, - .detail = try std.fmt.allocPrint(arena, "{}", .{many_ptr_info.fmtType(ip.*)}), + .detail = try std.fmt.allocPrint(arena, "ptr: {}", .{many_ptr_info.fmtType(ip.*)}), }); try completions.append(arena, .{ .label = "len", .kind = .Field, - .detail = "usize", + .detail = "len: usize", }); } else if (ip.indexToKey(pointer_info.elem_type) == .array_type) { try completions.append(arena, .{ .label = "len", .kind = .Field, - .detail = "usize", + .detail = "len: usize", }); } }, @@ -83,18 +83,23 @@ pub fn dotCompletions( try completions.append(arena, types.CompletionItem{ .label = "len", .kind = .Field, - .detail = try std.fmt.allocPrint(arena, "usize ({d})", .{array_info.len}), // TODO how should this be displayed + .detail = try std.fmt.allocPrint(arena, "const len: usize ({d})", .{array_info.len}), // TODO how should this be displayed }); }, .struct_type => |struct_index| { const struct_info = ip.getStruct(struct_index); + try completions.ensureUnusedCapacity(arena, struct_info.fields.count()); var field_it = struct_info.fields.iterator(); while (field_it.next()) |entry| { - try completions.append(arena, types.CompletionItem{ - .label = entry.key_ptr.*, + const label = entry.key_ptr.*; + const field = entry.value_ptr.*; + completions.appendAssumeCapacity(types.CompletionItem{ + .label = label, .kind = .Field, - // TODO include alignment and comptime - .detail = try std.fmt.allocPrint(arena, "{}", .{entry.value_ptr.ty.fmtType(ip.*)}), + .detail = try std.fmt.allocPrint(arena, "{s}: {}", .{ + label, + fmtFieldDetail(field, ip), + }), }); } }, @@ -120,13 +125,15 @@ pub fn dotCompletions( const union_info = ip.getUnion(union_index); var field_it = union_info.fields.iterator(); while (field_it.next()) |entry| { + const label = entry.key_ptr.*; + const field = entry.value_ptr.*; try completions.append(arena, .{ - .label = entry.key_ptr.*, + .label = label, .kind = .Field, - .detail = if (entry.value_ptr.alignment != 0) - try std.fmt.allocPrint(arena, "align({d}) {}", .{ entry.value_ptr.alignment, entry.value_ptr.ty.fmtType(ip.*) }) + .detail = if (field.alignment != 0) + try std.fmt.allocPrint(arena, "{s}: align({d}) {}", .{ label, field.alignment, field.ty.fmtType(ip.*) }) else - try std.fmt.allocPrint(arena, "{}", .{entry.value_ptr.ty.fmtType(ip.*)}), + try std.fmt.allocPrint(arena, "{s}: {}", .{ label, field.ty.fmtType(ip.*) }), }); } }, @@ -135,7 +142,7 @@ pub fn dotCompletions( try completions.append(arena, .{ .label = try std.fmt.allocPrint(arena, "{d}", .{i}), .kind = .Field, - .detail = try std.fmt.allocPrint(arena, "{}", .{tuple_ty.fmtType(ip.*)}), + .detail = try std.fmt.allocPrint(arena, "{d}: {}", .{ i, tuple_ty.fmtType(ip.*) }), }); } }, @@ -164,3 +171,39 @@ pub fn dotCompletions( => unreachable, } } + +fn FormatContext(comptime T: type) type { + return struct { + ip: *InternPool, + item: T, + }; +} + +fn formatFieldDetail( + ctx: FormatContext(InternPool.Struct.Field), + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + _ = options; + if (fmt.len != 0) std.fmt.invalidFmtError(fmt, InternPool.Struct.Field); + + const field = ctx.item; + if (field.is_comptime) { + try writer.writeAll("comptime "); + } + if (field.alignment != 0) { + try writer.print("align({d}) ", .{field.alignment}); + } + try writer.print("{}", .{field.ty.fmtType(ctx.ip.*)}); + if (field.default_value != .none) { + try writer.print(" = {},", .{field.default_value.fmtValue(field.ty, ctx.ip.*)}); + } +} + +pub fn fmtFieldDetail(field: InternPool.Struct.Field, ip: *InternPool) std.fmt.Formatter(formatFieldDetail) { + return .{ .data = .{ + .ip = ip, + .item = field, + } }; +} From 654913ae47e7656a44daf7a6bbde76a46b2f13ff Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:37:26 +0100 Subject: [PATCH 77/78] implement coerceInMemoryAllowedErrorSets --- src/analyser/InternPool.zig | 221 ++++++++++++------------------------ 1 file changed, 74 insertions(+), 147 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index 50c20d2..c633f48 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -2179,7 +2179,7 @@ const InMemoryCoercionResult = union(enum) { optional_shape: Pair, optional_child: PairAndChild, from_anyerror, - missing_error: []const []const u8, + missing_error: []const Index, /// true if wanted is var args fn_var_args: bool, /// true if wanted is generic @@ -2332,7 +2332,6 @@ fn coerceInMemoryAllowed( const dest_bits = dest_key.floatBits(target); const src_bits = src_key.floatBits(target); if (dest_bits == src_bits) return .ok; - // TODO return float_not_coercible return InMemoryCoercionResult{ .no_match = .{ .actual = dest_ty, .wanted = src_ty, @@ -2391,9 +2390,7 @@ fn coerceInMemoryAllowed( return try ip.coerceInMemoryAllowed(gpa, arena, dest_set, src_set, dest_is_const, target); }, .ErrorSet => { - return .ok; - // TODO: implement coerceInMemoryAllowedErrorSets - // return try ip.coerceInMemoryAllowedErrorSets(dest_ty, src_ty); + return try ip.coerceInMemoryAllowedErrorSets(gpa, arena, dest_ty, src_ty); }, .Array => { const dest_info = dest_key.array_type; @@ -2460,156 +2457,40 @@ fn coerceInMemoryAllowed( } } -// fn coerceInMemoryAllowedErrorSets( -// ip: *InternPool, -// gpa: Allocator, -// arena: Allocator, -// dest_ty: Index, -// src_ty: Index, -// ) !InMemoryCoercionResult { -// if(dest_ty == src_ty) return .ok; +fn coerceInMemoryAllowedErrorSets( + ip: *InternPool, + gpa: Allocator, + arena: Allocator, + dest_ty: Index, + src_ty: Index, +) !InMemoryCoercionResult { + if (dest_ty == src_ty) return .ok; -// const dest_key = ip.indexToKey(dest_ty); + const dest_key = ip.indexToKey(dest_ty); + assert(dest_key.zigTypeTag() == .ErrorSet); -// // Coercion to `anyerror`. Note that this check can return false negatives -// // in case the error sets did not get resolved. -// if(dest_key.simple) |simple| if(simple == .anyerror) return .ok; + if (dest_ty == .anyerror_type) return .ok; -// const src_key = ip.indexToKey(src_ty); + const src_key = ip.indexToKey(src_ty); + assert(src_key.zigTypeTag() == .ErrorSet); -// // const dest_tag = dest_key.zigTypeTag(); -// // const src_tag = src_key.zigTypeTag(); + if (src_ty == .anyerror_type) return .from_anyerror; -// if (dest_ty.castTag(.error_set_inferred)) |dst_payload| { -// const dst_ies = dst_payload.data; -// // We will make an effort to return `ok` without resolving either error set, to -// // avoid unnecessary "unable to resolve error set" dependency loop errors. -// switch (src_ty.tag()) { -// .error_set_inferred => { -// // If both are inferred error sets of functions, and -// // the dest includes the source function, the coercion is OK. -// // This check is important because it works without forcing a full resolution -// // of inferred error sets. -// const src_ies = src_ty.castTag(.error_set_inferred).?.data; + var missing_error_buf = std.ArrayListUnmanaged(Index){}; + defer missing_error_buf.deinit(gpa); -// if (dst_ies.inferred_error_sets.contains(src_ies)) { -// return .ok; -// } -// }, -// .error_set_single => { -// const name = src_ty.castTag(.error_set_single).?.data; -// if (dst_ies.errors.contains(name)) return .ok; -// }, -// .error_set_merged => { -// const names = src_ty.castTag(.error_set_merged).?.data.keys(); -// for (names) |name| { -// if (!dst_ies.errors.contains(name)) break; -// } else return .ok; -// }, -// .error_set => { -// const names = src_ty.castTag(.error_set).?.data.names.keys(); -// for (names) |name| { -// if (!dst_ies.errors.contains(name)) break; -// } else return .ok; -// }, -// .anyerror => {}, -// else => unreachable, -// } + for (src_key.error_set_type.names) |name| { + if (std.mem.indexOfScalar(Index, dest_key.error_set_type.names, name) == null) { + try missing_error_buf.append(gpa, name); + } + } -// if (dst_ies.func == sema.owner_func) { -// // We are trying to coerce an error set to the current function's -// // inferred error set. -// try dst_ies.addErrorSet(sema.gpa, src_ty); -// return .ok; -// } + if (missing_error_buf.items.len == 0) return .ok; -// try sema.resolveInferredErrorSet(block, dest_src, dst_payload.data); -// // isAnyError might have changed from a false negative to a true positive after resolution. -// if (dest_ty.isAnyError()) { -// return .ok; -// } -// } - -// var missing_error_buf = std.ArrayList([]const u8).init(sema.gpa); -// defer missing_error_buf.deinit(); - -// switch (src_ty.tag()) { -// .error_set_inferred => { -// const src_data = src_ty.castTag(.error_set_inferred).?.data; - -// try sema.resolveInferredErrorSet(block, src_src, src_data); -// // src anyerror status might have changed after the resolution. -// if (src_ty.isAnyError()) { -// // dest_ty.isAnyError() == true is already checked for at this point. -// return .from_anyerror; -// } - -// for (src_data.errors.keys()) |key| { -// if (!dest_ty.errorSetHasField(key)) { -// try missing_error_buf.append(key); -// } -// } - -// if (missing_error_buf.items.len != 0) { -// return InMemoryCoercionResult{ -// .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), -// }; -// } - -// return .ok; -// }, -// .error_set_single => { -// const name = src_ty.castTag(.error_set_single).?.data; -// if (dest_ty.errorSetHasField(name)) { -// return .ok; -// } -// const list = try sema.arena.alloc([]const u8, 1); -// list[0] = name; -// return InMemoryCoercionResult{ .missing_error = list }; -// }, -// .error_set_merged => { -// const names = src_ty.castTag(.error_set_merged).?.data.keys(); -// for (names) |name| { -// if (!dest_ty.errorSetHasField(name)) { -// try missing_error_buf.append(name); -// } -// } - -// if (missing_error_buf.items.len != 0) { -// return InMemoryCoercionResult{ -// .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), -// }; -// } - -// return .ok; -// }, -// .error_set => { -// const names = src_ty.castTag(.error_set).?.data.names.keys(); -// for (names) |name| { -// if (!dest_ty.errorSetHasField(name)) { -// try missing_error_buf.append(name); -// } -// } - -// if (missing_error_buf.items.len != 0) { -// return InMemoryCoercionResult{ -// .missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items), -// }; -// } - -// return .ok; -// }, -// .anyerror => switch (dest_ty.tag()) { -// .error_set_inferred => unreachable, // Caught by dest_ty.isAnyError() above. -// .error_set_single, .error_set_merged, .error_set => return .from_anyerror, -// .anyerror => unreachable, // Filtered out above. -// else => unreachable, -// }, -// else => unreachable, -// } - -// unreachable; -// } + return InMemoryCoercionResult{ + .missing_error = try arena.dupe(Index, missing_error_buf.items), + }; +} fn coerceInMemoryAllowedFns( ip: *InternPool, @@ -3418,6 +3299,52 @@ test "coerceInMemoryAllowed integers and floats" { try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .u32_type, true, builtin.target) == .no_match); } +test "coerceInMemoryAllowed error set" { + const gpa = std.testing.allocator; + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + var ip = try InternPool.init(gpa); + defer ip.deinit(gpa); + + const foo = try ip.get(gpa, .{ .bytes = "foo" }); + const bar = try ip.get(gpa, .{ .bytes = "bar" }); + const baz = try ip.get(gpa, .{ .bytes = "baz" }); + + const foo_bar_baz_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ baz, bar, foo } } }); + const foo_bar_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ foo, bar } } }); + const foo_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{foo} } }); + const empty_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); + + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_baz_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, empty_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, .anyerror_type, true, builtin.target) == .ok); + + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, .anyerror_type, true, builtin.target) == .from_anyerror); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, .anyerror_type, true, builtin.target) == .from_anyerror); + + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_bar_baz_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_bar_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, empty_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_bar_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, empty_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, empty_set, true, builtin.target) == .ok); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, empty_set, true, builtin.target) == .ok); + + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, foo_set, true, builtin.target) == .missing_error); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, foo_bar_baz_set, true, builtin.target) == .missing_error); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_bar_set, true, builtin.target) == .missing_error); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_bar_baz_set, true, builtin.target) == .missing_error); + try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_bar_baz_set, true, builtin.target) == .missing_error); +} + test "resolvePeerTypes" { const gpa = std.testing.allocator; From 99b5fd4f69531b8252ed6ece24f9f100fdd321b5 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:45:51 +0100 Subject: [PATCH 78/78] small refactor --- src/analyser/InternPool.zig | 215 ++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 107 deletions(-) diff --git a/src/analyser/InternPool.zig b/src/analyser/InternPool.zig index c633f48..25479ee 100644 --- a/src/analyser/InternPool.zig +++ b/src/analyser/InternPool.zig @@ -14,6 +14,7 @@ const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const expect = std.testing.expect; const encoding = @import("encoding.zig"); @@ -1504,19 +1505,19 @@ pub fn getUnion(ip: InternPool, index: InternPool.UnionIndex) *InternPool.Union return unions.at(@enumToInt(index)); } -pub fn createDecl(ip: *InternPool, gpa: Allocator, decl: InternPool.Decl) error{OutOfMemory}!InternPool.DeclIndex { +pub fn createDecl(ip: *InternPool, gpa: Allocator, decl: InternPool.Decl) Allocator.Error!InternPool.DeclIndex { try ip.decls.append(gpa, decl); return @intToEnum(InternPool.DeclIndex, ip.decls.count() - 1); } -pub fn createStruct(ip: *InternPool, gpa: Allocator, struct_info: InternPool.Struct) error{OutOfMemory}!InternPool.StructIndex { +pub fn createStruct(ip: *InternPool, gpa: Allocator, struct_info: InternPool.Struct) Allocator.Error!InternPool.StructIndex { try ip.structs.append(gpa, struct_info); return @intToEnum(InternPool.StructIndex, ip.structs.count() - 1); } -pub fn createEnum(ip: *InternPool, gpa: Allocator, enum_info: InternPool.Enum) error{OutOfMemory}!InternPool.EnumIndex { +pub fn createEnum(ip: *InternPool, gpa: Allocator, enum_info: InternPool.Enum) Allocator.Error!InternPool.EnumIndex { try ip.enums.append(gpa, enum_info); return @intToEnum(InternPool.EnumIndex, ip.enums.count() - 1); } -pub fn createUnion(ip: *InternPool, gpa: Allocator, union_info: InternPool.Union) error{OutOfMemory}!InternPool.UnionIndex { +pub fn createUnion(ip: *InternPool, gpa: Allocator, union_info: InternPool.Union) Allocator.Error!InternPool.UnionIndex { try ip.unions.append(gpa, union_info); return @intToEnum(InternPool.UnionIndex, ip.unions.count() - 1); } @@ -2291,7 +2292,7 @@ fn coerceInMemoryAllowed( src_ty: Index, dest_is_const: bool, target: std.Target, -) error{OutOfMemory}!InMemoryCoercionResult { +) Allocator.Error!InMemoryCoercionResult { if (dest_ty == src_ty) return .ok; const dest_key = ip.indexToKey(dest_ty); @@ -2499,7 +2500,7 @@ fn coerceInMemoryAllowedFns( dest_info: Function, src_info: Function, target: std.Target, -) error{OutOfMemory}!InMemoryCoercionResult { +) Allocator.Error!InMemoryCoercionResult { if (dest_info.is_var_args != src_info.is_var_args) { return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; } @@ -2584,7 +2585,7 @@ fn coerceInMemoryAllowedPtrs( src_ptr_info: Key, dest_is_const: bool, target: std.Target, -) error{OutOfMemory}!InMemoryCoercionResult { +) Allocator.Error!InMemoryCoercionResult { const dest_info = dest_ptr_info.pointer_type; const src_info = src_ptr_info.pointer_type; @@ -2779,11 +2780,11 @@ test "int type" { const u7_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .unsigned, .bits = 7 } }); const another_i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32 } }); - try std.testing.expect(i32_type == another_i32_type); - try std.testing.expect(i32_type != u7_type); + try expect(i32_type == another_i32_type); + try expect(i32_type != u7_type); - try std.testing.expect(i16_type != another_i32_type); - try std.testing.expect(i16_type != u7_type); + try expect(i16_type != another_i32_type); + try expect(i16_type != u7_type); try testExpectFmtType(ip, i32_type, "i32"); try testExpectFmtType(ip, i16_type, "i16"); @@ -2806,19 +2807,19 @@ test "int value" { const i64_min_value = try ip.get(gpa, .{ .int_i64_value = std.math.minInt(i64) }); const tags = ip.items.items(.tag); - try std.testing.expect(tags[@enumToInt(unsigned_one_value)] == .int_u32); - try std.testing.expect(tags[@enumToInt(signed_one_value)] == .int_i32); - try std.testing.expect(tags[@enumToInt(u64_max_value)] == .int_u64); - try std.testing.expect(tags[@enumToInt(i64_max_value)] == .int_i64); - try std.testing.expect(tags[@enumToInt(i64_min_value)] == .int_i64); + try expect(tags[@enumToInt(unsigned_one_value)] == .int_u32); + try expect(tags[@enumToInt(signed_one_value)] == .int_i32); + try expect(tags[@enumToInt(u64_max_value)] == .int_u64); + try expect(tags[@enumToInt(i64_max_value)] == .int_i64); + try expect(tags[@enumToInt(i64_min_value)] == .int_i64); - try std.testing.expect(unsigned_zero_value != unsigned_one_value); - try std.testing.expect(unsigned_one_value != signed_zero_value); - try std.testing.expect(signed_zero_value != signed_one_value); + try expect(unsigned_zero_value != unsigned_one_value); + try expect(unsigned_one_value != signed_zero_value); + try expect(signed_zero_value != signed_one_value); - try std.testing.expect(signed_one_value != u64_max_value); - try std.testing.expect(u64_max_value != i64_max_value); - try std.testing.expect(i64_max_value != i64_min_value); + try expect(signed_one_value != u64_max_value); + try expect(u64_max_value != i64_max_value); + try expect(i64_max_value != i64_min_value); try testExpectFmtValue(ip, unsigned_zero_value, undefined, "0"); try testExpectFmtValue(ip, unsigned_one_value, undefined, "1"); @@ -2865,13 +2866,13 @@ test "float type" { const another_f32_type = try ip.get(gpa, .{ .simple_type = .f32 }); const another_f64_type = try ip.get(gpa, .{ .simple_type = .f64 }); - try std.testing.expect(f16_type != f32_type); - try std.testing.expect(f32_type != f64_type); - try std.testing.expect(f64_type != f80_type); - try std.testing.expect(f80_type != f128_type); + try expect(f16_type != f32_type); + try expect(f32_type != f64_type); + try expect(f64_type != f80_type); + try expect(f80_type != f128_type); - try std.testing.expect(f32_type == another_f32_type); - try std.testing.expect(f64_type == another_f64_type); + try expect(f32_type == another_f32_type); + try expect(f64_type == another_f64_type); try testExpectFmtType(ip, f16_type, "f16"); try testExpectFmtType(ip, f32_type, "f32"); @@ -2901,26 +2902,26 @@ test "float value" { const f32_zero_value = try ip.get(gpa, .{ .float_32_value = 0.0 }); const f32_nzero_value = try ip.get(gpa, .{ .float_32_value = -0.0 }); - try std.testing.expect(f16_value != f32_value); - try std.testing.expect(f32_value != f64_value); - try std.testing.expect(f64_value != f80_value); - try std.testing.expect(f80_value != f128_value); + try expect(f16_value != f32_value); + try expect(f32_value != f64_value); + try expect(f64_value != f80_value); + try expect(f80_value != f128_value); - try std.testing.expect(f32_nan_value != f32_qnan_value); - try std.testing.expect(f32_inf_value != f32_ninf_value); - try std.testing.expect(f32_zero_value != f32_nzero_value); + try expect(f32_nan_value != f32_qnan_value); + try expect(f32_inf_value != f32_ninf_value); + try expect(f32_zero_value != f32_nzero_value); - try std.testing.expect(!ip.indexToKey(f16_value).eql(ip.indexToKey(f32_value))); - try std.testing.expect(ip.indexToKey(f32_value).eql(ip.indexToKey(f32_value))); + try expect(!ip.indexToKey(f16_value).eql(ip.indexToKey(f32_value))); + try expect(ip.indexToKey(f32_value).eql(ip.indexToKey(f32_value))); - try std.testing.expect(ip.indexToKey(f32_nan_value).eql(ip.indexToKey(f32_nan_value))); - try std.testing.expect(!ip.indexToKey(f32_nan_value).eql(ip.indexToKey(f32_qnan_value))); + try expect(ip.indexToKey(f32_nan_value).eql(ip.indexToKey(f32_nan_value))); + try expect(!ip.indexToKey(f32_nan_value).eql(ip.indexToKey(f32_qnan_value))); - try std.testing.expect(ip.indexToKey(f32_inf_value).eql(ip.indexToKey(f32_inf_value))); - try std.testing.expect(!ip.indexToKey(f32_inf_value).eql(ip.indexToKey(f32_ninf_value))); + try expect(ip.indexToKey(f32_inf_value).eql(ip.indexToKey(f32_inf_value))); + try expect(!ip.indexToKey(f32_inf_value).eql(ip.indexToKey(f32_ninf_value))); - try std.testing.expect(ip.indexToKey(f32_zero_value).eql(ip.indexToKey(f32_zero_value))); - try std.testing.expect(!ip.indexToKey(f32_zero_value).eql(ip.indexToKey(f32_nzero_value))); + try expect(ip.indexToKey(f32_zero_value).eql(ip.indexToKey(f32_zero_value))); + try expect(!ip.indexToKey(f32_zero_value).eql(ip.indexToKey(f32_nzero_value))); try testExpectFmtValue(ip, f16_value, undefined, "0.25"); try testExpectFmtValue(ip, f32_value, undefined, "0.5"); @@ -2995,15 +2996,15 @@ test "pointer type" { .size = .C, } }); - try std.testing.expect(@"*i32" != @"*u32"); - try std.testing.expect(@"*u32" != @"*const volatile u32"); - try std.testing.expect(@"*const volatile u32" != @"*align(4:2:3) u32"); - try std.testing.expect(@"*align(4:2:3) u32" != @"*addrspace(.shared) const u32"); + try expect(@"*i32" != @"*u32"); + try expect(@"*u32" != @"*const volatile u32"); + try expect(@"*const volatile u32" != @"*align(4:2:3) u32"); + try expect(@"*align(4:2:3) u32" != @"*addrspace(.shared) const u32"); - try std.testing.expect(@"[*]u32" != @"[*:0]u32"); - try std.testing.expect(@"[*:0]u32" != @"[]u32"); - try std.testing.expect(@"[*:0]u32" != @"[:0]u32"); - try std.testing.expect(@"[:0]u32" != @"[*c]u32"); + try expect(@"[*]u32" != @"[*:0]u32"); + try expect(@"[*:0]u32" != @"[]u32"); + try expect(@"[*:0]u32" != @"[:0]u32"); + try expect(@"[:0]u32" != @"[*c]u32"); try testExpectFmtType(ip, @"*i32", "*i32"); try testExpectFmtType(ip, @"*u32", "*u32"); @@ -3029,7 +3030,7 @@ test "optional type" { const i32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .i32_type } }); const u32_optional_type = try ip.get(gpa, .{ .optional_type = .{ .payload_type = .u32_type } }); - try std.testing.expect(i32_optional_type != u32_optional_type); + try expect(i32_optional_type != u32_optional_type); try testExpectFmtType(ip, i32_optional_type, "?i32"); try testExpectFmtType(ip, u32_optional_type, "?u32"); @@ -3050,20 +3051,20 @@ test "error set type" { const empty_error_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); - const error_set_0 = try ip.get(gpa, .{ .error_set_type = .{ + const foo_bar_baz_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ foo_name, bar_name, baz_name }, } }); - const error_set_1 = try ip.get(gpa, .{ .error_set_type = .{ + const foo_bar_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ foo_name, bar_name }, } }); - try std.testing.expect(empty_error_set != error_set_0); - try std.testing.expect(error_set_0 != error_set_1); + try expect(empty_error_set != foo_bar_baz_set); + try expect(foo_bar_baz_set != foo_bar_set); try testExpectFmtType(ip, empty_error_set, "error{}"); - try testExpectFmtType(ip, error_set_0, "error{foo,bar,baz}"); - try testExpectFmtType(ip, error_set_1, "error{foo,bar}"); + try testExpectFmtType(ip, foo_bar_baz_set, "error{foo,bar,baz}"); + try testExpectFmtType(ip, foo_bar_set, "error{foo,bar}"); } test "error union type" { @@ -3099,7 +3100,7 @@ test "array type" { .sentinel = .zero, } }); - try std.testing.expect(i32_3_array_type != u32_0_0_array_type); + try expect(i32_3_array_type != u32_0_0_array_type); try testExpectFmtType(ip, i32_3_array_type, "[3]i32"); try testExpectFmtType(ip, u32_0_0_array_type, "[3:0]u32"); @@ -3216,7 +3217,7 @@ test "anyframe type" { const @"anyframe->i32" = try ip.get(gpa, .{ .anyframe_type = .{ .child = .i32_type } }); const @"anyframe->bool" = try ip.get(gpa, .{ .anyframe_type = .{ .child = .bool_type } }); - try std.testing.expect(@"anyframe->i32" != @"anyframe->bool"); + try expect(@"anyframe->i32" != @"anyframe->bool"); try testExpectFmtType(ip, @"anyframe->i32", "anyframe->i32"); try testExpectFmtType(ip, @"anyframe->bool", "anyframe->bool"); @@ -3237,7 +3238,7 @@ test "vector type" { .child = .bool_type, } }); - try std.testing.expect(@"@Vector(2,u32)" != @"@Vector(2,bool)"); + try expect(@"@Vector(2,u32)" != @"@Vector(2,bool)"); try testExpectFmtType(ip, @"@Vector(2,u32)", "@Vector(2,u32)"); try testExpectFmtType(ip, @"@Vector(2,bool)", "@Vector(2,bool)"); @@ -3261,12 +3262,12 @@ test "bytes value" { const bytes_value3 = try ip.get(gpa, .{ .bytes = &str3 }); @memset(&str3, 0, str3.len); - try std.testing.expect(bytes_value1 == bytes_value2); - try std.testing.expect(bytes_value2 != bytes_value3); + try expect(bytes_value1 == bytes_value2); + try expect(bytes_value2 != bytes_value3); - try std.testing.expect(@ptrToInt(&str1) != @ptrToInt(ip.indexToKey(bytes_value1).bytes.ptr)); - try std.testing.expect(@ptrToInt(&str2) != @ptrToInt(ip.indexToKey(bytes_value2).bytes.ptr)); - try std.testing.expect(@ptrToInt(&str3) != @ptrToInt(ip.indexToKey(bytes_value3).bytes.ptr)); + try expect(@ptrToInt(&str1) != @ptrToInt(ip.indexToKey(bytes_value1).bytes.ptr)); + try expect(@ptrToInt(&str2) != @ptrToInt(ip.indexToKey(bytes_value2).bytes.ptr)); + try expect(@ptrToInt(&str3) != @ptrToInt(ip.indexToKey(bytes_value3).bytes.ptr)); try std.testing.expectEqual(ip.indexToKey(bytes_value1).bytes.ptr, ip.indexToKey(bytes_value2).bytes.ptr); @@ -3285,18 +3286,18 @@ test "coerceInMemoryAllowed integers and floats" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .u32_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .u16_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u16_type, .u32_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .i32_type, .u32_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .i32_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .i16_type, true, builtin.target) == .int_not_coercible); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .u32_type, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .u16_type, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .u16_type, .u32_type, true, builtin.target) == .int_not_coercible); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .i32_type, .u32_type, true, builtin.target) == .int_not_coercible); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .i32_type, true, builtin.target) == .int_not_coercible); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .i16_type, true, builtin.target) == .int_not_coercible); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .f32_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f64_type, .f32_type, true, builtin.target) == .no_match); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .f64_type, true, builtin.target) == .no_match); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .f32_type, true, builtin.target) == .no_match); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .u32_type, true, builtin.target) == .no_match); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .f32_type, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .f64_type, .f32_type, true, builtin.target) == .no_match); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .f64_type, true, builtin.target) == .no_match); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .u32_type, .f32_type, true, builtin.target) == .no_match); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .f32_type, .u32_type, true, builtin.target) == .no_match); } test "coerceInMemoryAllowed error set" { @@ -3309,40 +3310,40 @@ test "coerceInMemoryAllowed error set" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - const foo = try ip.get(gpa, .{ .bytes = "foo" }); - const bar = try ip.get(gpa, .{ .bytes = "bar" }); - const baz = try ip.get(gpa, .{ .bytes = "baz" }); + 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 foo_bar_baz_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ baz, bar, foo } } }); - const foo_bar_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ foo, bar } } }); - const foo_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{foo} } }); + const foo_bar_baz_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ baz_name, bar_name, foo_name } } }); + const foo_bar_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{ foo_name, bar_name } } }); + const foo_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{foo_name} } }); const empty_set = try ip.get(gpa, .{ .error_set_type = .{ .names = &.{} } }); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_baz_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, empty_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, .anyerror_type, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_baz_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_bar_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, foo_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, empty_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, .anyerror_type, .anyerror_type, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, .anyerror_type, true, builtin.target) == .from_anyerror); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, .anyerror_type, true, builtin.target) == .from_anyerror); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, .anyerror_type, true, builtin.target) == .from_anyerror); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, .anyerror_type, true, builtin.target) == .from_anyerror); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_bar_baz_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_bar_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, empty_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_bar_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, empty_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, empty_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, empty_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_bar_baz_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_bar_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, foo_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_baz_set, empty_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_bar_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, empty_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, empty_set, true, builtin.target) == .ok); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, empty_set, true, builtin.target) == .ok); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, foo_set, true, builtin.target) == .missing_error); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, foo_bar_baz_set, true, builtin.target) == .missing_error); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_bar_set, true, builtin.target) == .missing_error); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_bar_baz_set, true, builtin.target) == .missing_error); - try std.testing.expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_bar_baz_set, true, builtin.target) == .missing_error); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, foo_set, true, builtin.target) == .missing_error); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, empty_set, foo_bar_baz_set, true, builtin.target) == .missing_error); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_bar_set, true, builtin.target) == .missing_error); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_set, foo_bar_baz_set, true, builtin.target) == .missing_error); + try expect(try ip.coerceInMemoryAllowed(gpa, arena, foo_bar_set, foo_bar_baz_set, true, builtin.target) == .missing_error); } test "resolvePeerTypes" { @@ -3351,8 +3352,8 @@ test "resolvePeerTypes" { var ip = try InternPool.init(gpa); defer ip.deinit(gpa); - try std.testing.expect(.noreturn_type == try ip.resolvePeerTypes(std.testing.allocator, &.{}, builtin.target)); - try std.testing.expect(.type_type == try ip.resolvePeerTypes(std.testing.allocator, &.{.type_type}, builtin.target)); + try expect(.noreturn_type == try ip.resolvePeerTypes(std.testing.allocator, &.{}, builtin.target)); + try expect(.type_type == try ip.resolvePeerTypes(std.testing.allocator, &.{.type_type}, builtin.target)); try ip.testResolvePeerTypes(.none, .none, .none); try ip.testResolvePeerTypes(.bool_type, .bool_type, .bool_type);