additive: complete resolver traversal gaps
This commit is contained in:
@@ -342,6 +342,26 @@ fn findFn(root: *const ast.Node, name: []const u8) ?*const ast.Node {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn findDecl(root: *const ast.Node, name: []const u8, comptime kind: std.meta.Tag(ast.Node.Data)) ?*const ast.Node {
|
||||||
|
for (root.data.root.decls) |d| {
|
||||||
|
if (std.meta.activeTag(d.data) != kind) continue;
|
||||||
|
const dn = d.data.declName() orelse continue;
|
||||||
|
if (std.mem.eql(u8, dn, name)) return d;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expectTypeRefOwnTag(
|
||||||
|
rp: *const resolver.ResolvedProgram,
|
||||||
|
node: *const ast.Node,
|
||||||
|
expected: std.meta.Tag(resolver.RawDeclRef),
|
||||||
|
) !void {
|
||||||
|
const ref = rp.type_refs.get(node) orelse return error.MissingTypeRef;
|
||||||
|
try std.testing.expect(ref == .authors);
|
||||||
|
try std.testing.expect(ref.authors.own != null);
|
||||||
|
try std.testing.expectEqual(expected, std.meta.activeTag(ref.authors.own.?.raw));
|
||||||
|
}
|
||||||
|
|
||||||
// The pass populates the three bare-name domains over REAL Phase A facts: a type
|
// The pass populates the three bare-name domains over REAL Phase A facts: a type
|
||||||
// reference (own struct author), a value/const reference (own const), and a
|
// reference (own struct author), a value/const reference (own const), and a
|
||||||
// callable head (flat-imported author). It keys every entry by NODE IDENTITY, and
|
// callable head (flat-imported author). It keys every entry by NODE IDENTITY, and
|
||||||
@@ -462,3 +482,56 @@ test "resolver: resolve — bare-name domains populated, keyed by node, symbolic
|
|||||||
try std.testing.expectEqual(@as(u32, 0), rp.struct_const_refs.count());
|
try std.testing.expectEqual(@as(u32, 0), rp.struct_const_refs.count());
|
||||||
try std.testing.expectEqual(@as(u32, 0), rp.ufcs_refs.count());
|
try std.testing.expectEqual(@as(u32, 0), rp.ufcs_refs.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "resolver: resolve — generic constraints and named error sets are type refs" {
|
||||||
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
const io = testIo();
|
||||||
|
|
||||||
|
var tmp = std.testing.tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
|
||||||
|
try tmp.dir.writeFile(io, .{ .sub_path = "main.sx", .data =
|
||||||
|
\\Alias :: struct { x: s64 }
|
||||||
|
\\ParseErr :: error { Bad }
|
||||||
|
\\Box :: struct($N: Alias) { value: Alias }
|
||||||
|
\\Constrained :: protocol(T: Alias) { get :: () -> T; }
|
||||||
|
\\take :: ($N: Alias) -> s64 { return 0; }
|
||||||
|
\\fail :: () -> !ParseErr { raise error.Bad; }
|
||||||
|
\\main :: () -> s32 { return 0; }
|
||||||
|
\\
|
||||||
|
});
|
||||||
|
|
||||||
|
var dirbuf: [4096]u8 = undefined;
|
||||||
|
const absdir = dirbuf[0..try tmp.dir.realPath(io, &dirbuf)];
|
||||||
|
const main_path = try std.fmt.allocPrint(alloc, "{s}/main.sx", .{absdir});
|
||||||
|
|
||||||
|
var prog = try buildResolved(alloc, io, absdir, main_path);
|
||||||
|
|
||||||
|
var idx = ProgramIndex.init(alloc);
|
||||||
|
defer idx.deinit();
|
||||||
|
idx.module_decls = &prog.decls;
|
||||||
|
idx.flat_import_graph = &prog.flat_import_graph;
|
||||||
|
idx.import_graph = &prog.import_graph;
|
||||||
|
|
||||||
|
var rp = resolver.resolve(prog.root, &idx, main_path, alloc);
|
||||||
|
defer rp.deinit();
|
||||||
|
|
||||||
|
const alias_tag = std.meta.Tag(resolver.RawDeclRef).struct_decl;
|
||||||
|
const error_tag = std.meta.Tag(resolver.RawDeclRef).error_set_decl;
|
||||||
|
|
||||||
|
const box = findDecl(prog.root, "Box", .struct_decl) orelse return error.MissingStruct;
|
||||||
|
try expectTypeRefOwnTag(&rp, box.data.struct_decl.type_params[0].constraint, alias_tag);
|
||||||
|
|
||||||
|
const constrained = findDecl(prog.root, "Constrained", .protocol_decl) orelse return error.MissingProtocol;
|
||||||
|
try expectTypeRefOwnTag(&rp, constrained.data.protocol_decl.type_params[0].constraint, alias_tag);
|
||||||
|
|
||||||
|
const take = findFn(prog.root, "take") orelse return error.MissingFn;
|
||||||
|
try expectTypeRefOwnTag(&rp, take.data.fn_decl.type_params[0].constraint, alias_tag);
|
||||||
|
|
||||||
|
const fail = findFn(prog.root, "fail") orelse return error.MissingFn;
|
||||||
|
const err_ty = fail.data.fn_decl.return_type orelse return error.NoReturnType;
|
||||||
|
try std.testing.expect(err_ty.data == .error_type_expr);
|
||||||
|
try expectTypeRefOwnTag(&rp, err_ty, error_tag);
|
||||||
|
}
|
||||||
|
|||||||
@@ -392,6 +392,7 @@ const ResolvePass = struct {
|
|||||||
.fn_decl => |*fd| {
|
.fn_decl => |*fd| {
|
||||||
var frame = Frame{ .params = fd.type_params, .owner = node, .parent = here.scope };
|
var frame = Frame{ .params = fd.type_params, .owner = node, .parent = here.scope };
|
||||||
const inner = Ctx{ .source = here.source, .scope = &frame };
|
const inner = Ctx{ .source = here.source, .scope = &frame };
|
||||||
|
self.visitTypeParamConstraints(fd.type_params, inner);
|
||||||
for (fd.params) |p| {
|
for (fd.params) |p| {
|
||||||
self.visit(p.type_expr, inner);
|
self.visit(p.type_expr, inner);
|
||||||
if (p.default_expr) |d| self.visit(d, inner);
|
if (p.default_expr) |d| self.visit(d, inner);
|
||||||
@@ -402,6 +403,7 @@ const ResolvePass = struct {
|
|||||||
.lambda => |*l| {
|
.lambda => |*l| {
|
||||||
var frame = Frame{ .params = l.type_params, .owner = node, .parent = here.scope };
|
var frame = Frame{ .params = l.type_params, .owner = node, .parent = here.scope };
|
||||||
const inner = Ctx{ .source = here.source, .scope = &frame };
|
const inner = Ctx{ .source = here.source, .scope = &frame };
|
||||||
|
self.visitTypeParamConstraints(l.type_params, inner);
|
||||||
for (l.params) |p| {
|
for (l.params) |p| {
|
||||||
self.visit(p.type_expr, inner);
|
self.visit(p.type_expr, inner);
|
||||||
if (p.default_expr) |d| self.visit(d, inner);
|
if (p.default_expr) |d| self.visit(d, inner);
|
||||||
@@ -412,6 +414,7 @@ const ResolvePass = struct {
|
|||||||
.struct_decl => |*sd| {
|
.struct_decl => |*sd| {
|
||||||
var frame = Frame{ .params = sd.type_params, .owner = node, .parent = here.scope };
|
var frame = Frame{ .params = sd.type_params, .owner = node, .parent = here.scope };
|
||||||
const inner = Ctx{ .source = here.source, .scope = &frame };
|
const inner = Ctx{ .source = here.source, .scope = &frame };
|
||||||
|
self.visitTypeParamConstraints(sd.type_params, inner);
|
||||||
self.visitAll(sd.field_types, inner);
|
self.visitAll(sd.field_types, inner);
|
||||||
self.visitAllOpt(sd.field_defaults, inner);
|
self.visitAllOpt(sd.field_defaults, inner);
|
||||||
self.visitAll(sd.methods, inner);
|
self.visitAll(sd.methods, inner);
|
||||||
@@ -420,6 +423,7 @@ const ResolvePass = struct {
|
|||||||
.protocol_decl => |*pd| {
|
.protocol_decl => |*pd| {
|
||||||
var frame = Frame{ .params = pd.type_params, .owner = node, .parent = here.scope };
|
var frame = Frame{ .params = pd.type_params, .owner = node, .parent = here.scope };
|
||||||
const inner = Ctx{ .source = here.source, .scope = &frame };
|
const inner = Ctx{ .source = here.source, .scope = &frame };
|
||||||
|
self.visitTypeParamConstraints(pd.type_params, inner);
|
||||||
for (pd.methods) |m| {
|
for (pd.methods) |m| {
|
||||||
self.visitAll(m.params, inner);
|
self.visitAll(m.params, inner);
|
||||||
if (m.return_type) |rt| self.visit(rt, inner);
|
if (m.return_type) |rt| self.visit(rt, inner);
|
||||||
@@ -429,6 +433,7 @@ const ResolvePass = struct {
|
|||||||
.impl_block => |*ib| {
|
.impl_block => |*ib| {
|
||||||
var frame = Frame{ .params = ib.target_type_params, .owner = node, .parent = here.scope };
|
var frame = Frame{ .params = ib.target_type_params, .owner = node, .parent = here.scope };
|
||||||
const inner = Ctx{ .source = here.source, .scope = &frame };
|
const inner = Ctx{ .source = here.source, .scope = &frame };
|
||||||
|
self.visitTypeParamConstraints(ib.target_type_params, inner);
|
||||||
if (ib.target_type_expr) |tt| self.visit(tt, inner);
|
if (ib.target_type_expr) |tt| self.visit(tt, inner);
|
||||||
self.visitAll(ib.protocol_type_args, inner);
|
self.visitAll(ib.protocol_type_args, inner);
|
||||||
self.visitAll(ib.methods, inner);
|
self.visitAll(ib.methods, inner);
|
||||||
@@ -466,6 +471,9 @@ const ResolvePass = struct {
|
|||||||
},
|
},
|
||||||
.pack_index_type_expr => |*p| self.recordPack(&self.out.type_refs, node, p.pack_name, p.index, here.scope),
|
.pack_index_type_expr => |*p| self.recordPack(&self.out.type_refs, node, p.pack_name, p.index, here.scope),
|
||||||
.comptime_pack_ref => |*p| self.recordPack(&self.out.value_refs, node, p.pack_name, null, here.scope),
|
.comptime_pack_ref => |*p| self.recordPack(&self.out.value_refs, node, p.pack_name, null, here.scope),
|
||||||
|
.error_type_expr => |*e| {
|
||||||
|
if (e.name) |name| self.recordAuthors(&self.out.type_refs, node, name, here.source);
|
||||||
|
},
|
||||||
.parameterized_type_expr => |*p| {
|
.parameterized_type_expr => |*p| {
|
||||||
// the head (generic-struct / type-fn / protocol) is S2.1b; the
|
// the head (generic-struct / type-fn / protocol) is S2.1b; the
|
||||||
// type args are ordinary references, collected now.
|
// type args are ordinary references, collected now.
|
||||||
@@ -625,7 +633,6 @@ const ResolvePass = struct {
|
|||||||
.import_decl,
|
.import_decl,
|
||||||
.namespace_decl,
|
.namespace_decl,
|
||||||
.error_set_decl,
|
.error_set_decl,
|
||||||
.error_type_expr,
|
|
||||||
.foreign_expr,
|
.foreign_expr,
|
||||||
.library_decl,
|
.library_decl,
|
||||||
.framework_decl,
|
.framework_decl,
|
||||||
@@ -643,6 +650,10 @@ const ResolvePass = struct {
|
|||||||
for (nodes) |n| if (n) |nn| self.visit(nn, ctx);
|
for (nodes) |n| if (n) |nn| self.visit(nn, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visitTypeParamConstraints(self: *ResolvePass, params: []const ast.StructTypeParam, ctx: Ctx) void {
|
||||||
|
for (params) |p| self.visit(p.constraint, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/// A type-position reference: a generic param in scope → symbolic template ref;
|
/// A type-position reference: a generic param in scope → symbolic template ref;
|
||||||
/// otherwise a user type, collected RAW. Builtins / undeclared names collect to
|
/// otherwise a user type, collected RAW. Builtins / undeclared names collect to
|
||||||
/// an empty set and are simply not recorded.
|
/// an empty set and are simply not recorded.
|
||||||
@@ -676,16 +687,29 @@ const ResolvePass = struct {
|
|||||||
fn recordAuthors(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, name: []const u8, from: []const u8) void {
|
fn recordAuthors(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, name: []const u8, from: []const u8) void {
|
||||||
const set = self.res.collectVisibleAuthors(name, from, .user_bare_flat);
|
const set = self.res.collectVisibleAuthors(name, from, .user_bare_flat);
|
||||||
if (set.distinctCount() == 0) return;
|
if (set.distinctCount() == 0) return;
|
||||||
table.put(node, .{ .authors = set }) catch @panic("resolve: OOM");
|
self.replaceRef(table, node, .{ .authors = set });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recordTemplate(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, m: GenericMatch) void {
|
fn recordTemplate(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, m: GenericMatch) void {
|
||||||
table.put(node, .{ .template = self.internTemplate(m) }) catch @panic("resolve: OOM");
|
self.replaceRef(table, node, .{ .template = self.internTemplate(m) });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recordPack(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, name: []const u8, index: ?u32, scope: ?*const Frame) void {
|
fn recordPack(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, name: []const u8, index: ?u32, scope: ?*const Frame) void {
|
||||||
const m = lookupGeneric(scope, name) orelse return;
|
const m = lookupGeneric(scope, name) orelse return;
|
||||||
table.put(node, .{ .pack = .{ .id = self.internPack(m), .index = index } }) catch @panic("resolve: OOM");
|
self.replaceRef(table, node, .{ .pack = .{ .id = self.internPack(m), .index = index } });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replaceRef(self: *ResolvePass, table: *NodeRefTable, node: *const ast.Node, ref: ResolvedRef) void {
|
||||||
|
const entry = table.getOrPut(node) catch @panic("resolve: OOM");
|
||||||
|
if (entry.found_existing) self.releaseRef(entry.value_ptr.*);
|
||||||
|
entry.value_ptr.* = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn releaseRef(self: *ResolvePass, ref: ResolvedRef) void {
|
||||||
|
switch (ref) {
|
||||||
|
.authors => |a| if (a.flat.len > 0) self.out.alloc.free(a.flat),
|
||||||
|
.template, .pack => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internTemplate(self: *ResolvePass, m: GenericMatch) TemplateParamId {
|
fn internTemplate(self: *ResolvePass, m: GenericMatch) TemplateParamId {
|
||||||
|
|||||||
Reference in New Issue
Block a user