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 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);

View File

@ -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");