more comptime interpreter work

This commit is contained in:
Techatrix 2023-01-04 11:11:48 +01:00
parent 651955399e
commit 8828ff117d
2 changed files with 97 additions and 94 deletions

View File

@ -9,6 +9,7 @@ const ast = @import("ast.zig");
const zig = std.zig; const zig = std.zig;
const Ast = zig.Ast; const Ast = zig.Ast;
const analysis = @import("analysis.zig"); const analysis = @import("analysis.zig");
const offsets = @import("offsets.zig");
const DocumentStore = @import("DocumentStore.zig"); const DocumentStore = @import("DocumentStore.zig");
pub const InternPool = @import("InternPool.zig"); pub const InternPool = @import("InternPool.zig");
@ -26,6 +27,8 @@ ip: InternPool = .{},
document_store: *DocumentStore, document_store: *DocumentStore,
uri: DocumentStore.Uri, uri: DocumentStore.Uri,
scopes: std.MultiArrayList(Scope) = .{}, scopes: std.MultiArrayList(Scope) = .{},
decls: std.ArrayListUnmanaged(Decl) = .{},
namespaces: std.MultiArrayList(Namespace) = .{},
/// Interpreter diagnostic errors /// Interpreter diagnostic errors
errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{}, errors: std.AutoArrayHashMapUnmanaged(Ast.Node.Index, InterpreterError) = .{},
@ -56,10 +59,15 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void {
interpreter.ip.deinit(interpreter.allocator); interpreter.ip.deinit(interpreter.allocator);
var i: usize = 0; var i: usize = 0;
while (i < interpreter.scopes.len) : (i += 1) { while (i < interpreter.namespaces.len) : (i += 1) {
interpreter.scopes.items(.child_scopes)[i].deinit(interpreter.allocator); 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.scopes.deinit(interpreter.allocator);
interpreter.arena.deinit();
} }
pub const Type = struct { pub const Type = struct {
@ -77,6 +85,27 @@ pub const Value = struct {
val: IPIndex, 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 Comptimeness = enum { @"comptime", runtime };
pub const Scope = struct { pub const Scope = struct {
@ -87,8 +116,7 @@ pub const Scope = struct {
parent: u32, // zero indicates root scope parent: u32, // zero indicates root scope
node_idx: Ast.Node.Index, node_idx: Ast.Node.Index,
namespace: IPIndex, namespace: NamespaceIndex,
child_scopes: std.ArrayListUnmanaged(u32) = .{},
pub const ScopeKind = enum { container, block, function }; pub const ScopeKind = enum { container, block, function };
pub fn scopeKind(scope: Scope) ScopeKind { pub fn scopeKind(scope: Scope) ScopeKind {
@ -177,22 +205,19 @@ fn getDeclCount(tree: Ast, node_idx: Ast.Node.Index) usize {
pub fn huntItDown( pub fn huntItDown(
interpreter: *ComptimeInterpreter, interpreter: *ComptimeInterpreter,
namespace: IPIndex, namespace: NamespaceIndex,
decl_name: []const u8, decl_name: []const u8,
options: InterpretOptions, options: InterpretOptions,
) InterpretError!InternPool.Decl { ) InterpretError!Decl {
_ = options; _ = options;
var current_namespace = namespace; var current_namespace = namespace;
while (current_namespace != IPIndex.none) { while (current_namespace != .none) {
const namespace_info = interpreter.ip.indexToKey(current_namespace).namespace; const decls: std.StringArrayHashMapUnmanaged(Decl) = interpreter.namespaces.items(.decls)[@enumToInt(current_namespace)];
defer current_namespace = namespace_info.parent; defer current_namespace = interpreter.namespaces.items(.parent)[@enumToInt(current_namespace)];
for (namespace_info.decls) |decl_index| { if (decls.get(decl_name)) |decl| {
const decl_info = interpreter.ip.indexToKey(decl_index).declaration; return decl;
if (std.mem.eql(u8, decl_info.name, decl_name)) {
return decl_info;
}
} }
} }
@ -260,45 +285,37 @@ pub fn interpret(
.interpreter = interpreter, .interpreter = interpreter,
.parent = scope, .parent = scope,
.node_idx = node_idx, .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); const container_scope = @intCast(u32, interpreter.scopes.len - 1);
var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){}; var fields = std.StringArrayHashMapUnmanaged(InternPool.Struct.Field){};
defer fields.deinit(interpreter.allocator);
var buffer: [2]Ast.Node.Index = undefined; var buffer: [2]Ast.Node.Index = undefined;
const members = ast.declMembers(tree, node_idx, &buffer); const members = ast.declMembers(tree, node_idx, &buffer);
for (members) |member| { for (members) |member| {
const maybe_container_field: ?zig.Ast.full.ContainerField = switch (tags[member]) { const container_field = ast.containerField(tree, member) orelse {
.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 {
_ = try interpreter.interpret(member, container_scope, options); _ = try interpreter.interpret(member, container_scope, options);
continue; 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 IPIndex.none
else 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) { if (init_type_value.ty != type_type) {
try interpreter.recordError( try interpreter.recordError(
field_info.ast.type_expr, container_field.ast.type_expr,
"expected_type", "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; continue;
} }
const name = tree.tokenSlice(field_info.ast.main_token); const name = tree.tokenSlice(container_field.ast.main_token);
const field: InternPool.Struct.Field = .{ const field: InternPool.Struct.Field = .{
.ty = init_type_value.val, .ty = init_type_value.val,
@ -310,21 +327,12 @@ pub fn interpret(
try fields.put(interpreter.arena.allocator(), name, field); 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{ const struct_type = try interpreter.ip.get(interpreter.allocator, IPKey{
.struct_type = .{ .struct_type = .{
.fields = fields, .fields = fields,
.namespace = namespace, // TODO .namespace = .none, // TODO
.layout = std.builtin.Type.ContainerLayout.Auto, // TODO .layout = .Auto, // TODO
.backing_int_ty = IPIndex.none, // TODO .backing_int_ty = .none, // TODO
}, },
}); });
@ -372,7 +380,7 @@ pub fn interpret(
.interpreter = interpreter, .interpreter = interpreter,
.parent = scope, .parent = scope,
.node_idx = node_idx, .node_idx = node_idx,
.namespace = IPIndex.none, .namespace = .none,
}); });
const block_scope = @intCast(u32, interpreter.scopes.len - 1); 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 ir = try interpreter.interpret(data[node_idx].lhs, scope, options);
var irv = try ir.getValue(); 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| { var scope_sub_decl = irv.interpreter.huntItDown(namespace, rhs_str, options) catch |err| {
if (err == error.IdentifierNotFound) try interpreter.recordError( if (err == error.IdentifierNotFound) try interpreter.recordError(
@ -688,7 +696,7 @@ pub fn interpret(
.node_idx = node_idx, .node_idx = node_idx,
.ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{ .ty = try interpreter.ip.get(interpreter.allocator, IPKey{ .struct_type = .{
.fields = .{}, .fields = .{},
.namespace = IPIndex.none, .namespace = .none,
.layout = .Auto, .layout = .Auto,
.backing_int_ty = IPIndex.none, .backing_int_ty = IPIndex.none,
} }), } }),
@ -735,15 +743,13 @@ pub fn interpret(
if (value.ty != type_type) 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 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(value.val).getNamespace();
const namespace = interpreter.ip.indexToKey(namespace_index).namespace; if (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.data; // TODO add checks
const has_decl = for (namespace.decls) |decl| { const decls = interpreter.namespaces.items(.decls)[@enumToInt(namespace)];
const decl_name = interpreter.ip.indexToKey(decl).declaration.name; const has_decl = decls.contains(name);
if (std.mem.eql(u8, decl_name, name)) break true;
} else false;
return InterpretResult{ .value = Value{ return InterpretResult{ .value = Value{
.interpreter = interpreter, .interpreter = interpreter,
@ -812,24 +818,22 @@ pub fn interpret(
.@"comptime" => { .@"comptime" => {
return try interpreter.interpret(data[node_idx].lhs, scope, .{}); return try interpreter.interpret(data[node_idx].lhs, scope, .{});
}, },
// .fn_proto, .fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_simple, .fn_decl => {
// .fn_proto_multi, var buf: [1]Ast.Node.Index = undefined;
// .fn_proto_one, const func = ast.fnProto(tree, node_idx, &buf).?;
// .fn_proto_simple,
.fn_decl => {
// var buf: [1]Ast.Node.Index = undefined;
// const func = ast.fnProto(tree, node_idx, &buf).?;
// TODO: Resolve function type // TODO: Resolve function type
// const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{ const type_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .simple = .type });
// .calling_convention = .Unspecified,
// .alignment = 0, const function_type = try interpreter.ip.get(interpreter.allocator, IPKey{ .function_type = .{
// .is_generic = false, .calling_convention = .Unspecified,
// .is_var_args = false, .alignment = 0,
// .return_type = IPIndex.none, .is_generic = false,
// .args = &.{}, .is_var_args = false,
// } }); .return_type = IPIndex.none,
.args = &.{},
} });
// var it = func.iterate(&tree); // var it = func.iterate(&tree);
// while (ast.nextFnParam(&it)) |param| { // 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| // 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"; // fnd.return_type = value.value_data.@"type";
// var value = Value{ const name = offsets.tokenToSlice(tree, func.name_token.?);
// .interpreter = interpreter,
// .node_idx = node_idx,
// .ty = function_type,
// .val = IPIndex.none, // TODO
// };
// const name = analysis.getDeclName(tree, node_idx).?; const namespace = interpreter.scopes.items(.namespace)[scope];
// var namespace = interpreter.ip.indexToKey(scope.?.namespace).namespace; if (namespace != .none) {
// try namespace.decls.put(interpreter.allocator, name, .{ const decls = &interpreter.namespaces.items(.decls)[@enumToInt(namespace)];
// .scope = scope.?, try decls.put(interpreter.allocator, name, .{
// .node_idx = node_idx, .name = name,
// .name = name, .ty = type_type,
// .value = value, .val = function_type,
// }); .alignment = 0, // TODO
.address_space = .generic, // TODO
.is_pub = false, // TODO
.is_exported = false, // TODO
});
}
return InterpretResult{ .nothing = {} }; return InterpretResult{ .nothing = {} };
}, },
@ -889,7 +893,7 @@ pub fn interpret(
defer args.deinit(interpreter.allocator); defer args.deinit(interpreter.allocator);
for (call_full.ast.params) |param| { 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, .{}); const func_id_result = try interpreter.interpret(call_full.ast.fn_expr, scope, .{});
@ -1008,7 +1012,7 @@ pub fn call(
.interpreter = interpreter, .interpreter = interpreter,
.parent = scope, .parent = scope,
.node_idx = func_node_idx, .node_idx = func_node_idx,
.namespace = IPIndex.none, .namespace = .none,
}); });
const fn_scope = @intCast(u32, interpreter.scopes.len - 1); const fn_scope = @intCast(u32, interpreter.scopes.len - 1);

View File

@ -519,8 +519,8 @@ pub const Key = union(enum) {
.optional_type => |optional_info| { .optional_type => |optional_info| {
const child_ty = optional_info.payload_type; const child_ty = optional_info.payload_type;
const child_key = ip.indexToKey(child_ty); const child_key = ip.indexToKey(child_ty);
if(child_key != .pointer_type) return false; if (child_key != .pointer_type) return false;
const info = child_key.pointer_type; const info = child_key.pointer_type;
switch (info.size) { switch (info.size) {
.Slice, .C => return false, .Slice, .C => return false,
.Many, .One => return !info.is_allowzero, .Many, .One => return !info.is_allowzero,
@ -533,7 +533,7 @@ pub const Key = union(enum) {
pub fn elemType2(ty: Key) Index { pub fn elemType2(ty: Key) Index {
return switch (ty) { return switch (ty) {
.simple => |simple| switch(simple) { .simple => |simple| switch (simple) {
.@"anyframe" => @panic("TODO: return void type"), .@"anyframe" => @panic("TODO: return void type"),
else => unreachable, else => unreachable,
}, },
@ -1544,7 +1544,7 @@ pub fn resolvePeerTypes(ip: *InternPool, gpa: Allocator, types: []const Index, t
return Index.none; return Index.none;
} }
if(chosen == .none) return chosen; if (chosen == .none) return chosen;
const chosen_key = ip.indexToKey(chosen); const chosen_key = ip.indexToKey(chosen);
if (convert_to_slice) { if (convert_to_slice) {
@ -2163,7 +2163,7 @@ fn coerceInMemoryAllowedPtrs(
const ok_cv_qualifiers = const ok_cv_qualifiers =
(!src_info.is_const or dest_info.is_const) and (!src_info.is_const or dest_info.is_const) and
(!src_info.is_volatile or dest_info.is_volatile); (!src_info.is_volatile or dest_info.is_volatile);
if (!ok_cv_qualifiers) { if (!ok_cv_qualifiers) {
return InMemoryCoercionResult{ .ptr_qualifiers = .{ return InMemoryCoercionResult{ .ptr_qualifiers = .{
.actual_const = src_info.is_const, .actual_const = src_info.is_const,
@ -2188,7 +2188,7 @@ fn coerceInMemoryAllowedPtrs(
.wanted = dest_info.elem_type, .wanted = dest_info.elem_type,
} }; } };
} }
const dest_allow_zero = dest_ptr_info.ptrAllowsZero(ip); const dest_allow_zero = dest_ptr_info.ptrAllowsZero(ip);
const src_allow_zero = src_ptr_info.ptrAllowsZero(ip); const src_allow_zero = src_ptr_info.ptrAllowsZero(ip);
@ -2199,7 +2199,7 @@ fn coerceInMemoryAllowedPtrs(
.wanted = dest_ty, .wanted = dest_ty,
} }; } };
} }
// if (src_info.host_size != dest_info.host_size or // if (src_info.host_size != dest_info.host_size or
// src_info.bit_offset != dest_info.bit_offset) // 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? 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) { if (!ok_sent) {
return InMemoryCoercionResult{ .ptr_sentinel = .{ return InMemoryCoercionResult{ .ptr_sentinel = .{
.actual = if(src_info.sentinel != .none) src_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}), .wanted = if (dest_info.sentinel != .none) dest_info.sentinel else try ip.get(gpa, .{ .simple = .unreachable_value }),
.ty = dest_info.elem_type, .ty = dest_info.elem_type,
} }; } };
} }
@ -2227,11 +2227,10 @@ fn coerceInMemoryAllowedPtrs(
alignment: { alignment: {
if (src_info.alignment == 0 and dest_info.alignment == 0 and 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? dest_info.elem_type == src_info.elem_type // is this enough for a value equality check?
) ) {
{
break :alignment; break :alignment;
} }
// const src_align = if (src_info.alignment != 0) // const src_align = if (src_info.alignment != 0)
// src_info.alignment // src_info.alignment
// else // else
@ -2394,7 +2393,7 @@ test "pointer type" {
.size = .One, .size = .One,
.is_const = true, .is_const = true,
} }); } });
try std.testing.expect(const_u32_pointer_type != u32_pointer_type); try std.testing.expect(const_u32_pointer_type != u32_pointer_type);
try testExpectFmtType(&ip, i32_pointer_type_0, "*i32"); try testExpectFmtType(&ip, i32_pointer_type_0, "*i32");