refactor(imports): retain dup same-name fn authors + build identity indexes [0102a]
First of four fix-0102 sub-steps. Purely additive: retains data that the flat/directory merge currently first-wins-drops and builds two identity indexes for later bare-name disambiguation (fix-0102c). No resolution change — the existing first-wins bare path still wins; suite unchanged. - mergeFlat + directory merge: stop dropping a same-name FUNCTION authored by a different module/file. Non-function decls keep first-wins dedup; node identity dedup is untouched. - flat_import_graph: a flat-only subset of import_graph, recording an edge only for a bare `#import` (imp.name == null), never a namespaced `ns :: #import`. Threaded through resolveImports/resolveDirectoryImport and into ProgramIndex. - module_fns (path -> name -> *const FnDecl): per-module authored-function index mirroring module_scopes, built in core.zig from the main module + cache. Same-name cross-module authors stay distinct under their own paths. - imports.test.zig: asserts both a.sx/b.sx greet authors are retained in module_fns and in the global flat list, and that flat_import_graph excludes the namespaced edge while import_graph includes it. Gate (this worktree): zig build, zig build test (398/398), bash tests/run_examples.sh (457 passed) all green.
This commit is contained in:
16
src/core.zig
16
src/core.zig
@@ -26,6 +26,12 @@ pub const Compilation = struct {
|
|||||||
import_sources: std.StringHashMap([:0]const u8),
|
import_sources: std.StringHashMap([:0]const u8),
|
||||||
module_scopes: std.StringHashMap(std.StringHashMap(void)),
|
module_scopes: std.StringHashMap(std.StringHashMap(void)),
|
||||||
import_graph: std.StringHashMap(std.StringHashMap(void)),
|
import_graph: std.StringHashMap(std.StringHashMap(void)),
|
||||||
|
/// Flat-only subset of `import_graph` (bare `#import` edges, no namespaced
|
||||||
|
/// `ns :: #import`). Borrowed by `ProgramIndex.flat_import_graph`.
|
||||||
|
flat_import_graph: std.StringHashMap(std.StringHashMap(void)),
|
||||||
|
/// Per-module authored-function index (`path → name → *const FnDecl`).
|
||||||
|
/// Borrowed by `ProgramIndex.module_fns`.
|
||||||
|
module_fns: imports.ModuleFns,
|
||||||
ir_emitter: ?ir.LLVMEmitter = null,
|
ir_emitter: ?ir.LLVMEmitter = null,
|
||||||
/// Lowered IR module, kept alive past `generateCode` so post-link
|
/// Lowered IR module, kept alive past `generateCode` so post-link
|
||||||
/// callbacks can re-enter the interpreter to invoke sx functions
|
/// callbacks can re-enter the interpreter to invoke sx functions
|
||||||
@@ -52,6 +58,8 @@ pub const Compilation = struct {
|
|||||||
.import_sources = std.StringHashMap([:0]const u8).init(allocator),
|
.import_sources = std.StringHashMap([:0]const u8).init(allocator),
|
||||||
.module_scopes = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
.module_scopes = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
||||||
.import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
.import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
||||||
|
.flat_import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
||||||
|
.module_fns = imports.ModuleFns.init(allocator),
|
||||||
.target_config = target_config,
|
.target_config = target_config,
|
||||||
.stdlib_paths = stdlib_paths,
|
.stdlib_paths = stdlib_paths,
|
||||||
};
|
};
|
||||||
@@ -101,6 +109,7 @@ pub const Compilation = struct {
|
|||||||
&self.diagnostics,
|
&self.diagnostics,
|
||||||
self.stdlib_paths,
|
self.stdlib_paths,
|
||||||
&self.import_graph,
|
&self.import_graph,
|
||||||
|
&self.flat_import_graph,
|
||||||
self.comptimeContext(),
|
self.comptimeContext(),
|
||||||
) catch return error.CompileError;
|
) catch return error.CompileError;
|
||||||
|
|
||||||
@@ -111,6 +120,11 @@ pub const Compilation = struct {
|
|||||||
self.module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope) catch {};
|
self.module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-module authored-function index, built from the SAME modules as
|
||||||
|
// `module_scopes` (main + every cache entry). Keyed by path; same-name
|
||||||
|
// cross-module authors stay distinct under their own paths.
|
||||||
|
imports.buildModuleFns(self.allocator, self.file_path, mod, &cache, &self.module_fns) catch {};
|
||||||
|
|
||||||
// Store main file source in import_sources so error reporting can find it
|
// Store main file source in import_sources so error reporting can find it
|
||||||
self.import_sources.put(self.file_path, self.source) catch {};
|
self.import_sources.put(self.file_path, self.source) catch {};
|
||||||
|
|
||||||
@@ -276,6 +290,8 @@ pub const Compilation = struct {
|
|||||||
lowering.diagnostics = &self.diagnostics;
|
lowering.diagnostics = &self.diagnostics;
|
||||||
lowering.program_index.module_scopes = &self.module_scopes;
|
lowering.program_index.module_scopes = &self.module_scopes;
|
||||||
lowering.program_index.import_graph = &self.import_graph;
|
lowering.program_index.import_graph = &self.import_graph;
|
||||||
|
lowering.program_index.flat_import_graph = &self.flat_import_graph;
|
||||||
|
lowering.program_index.module_fns = &self.module_fns;
|
||||||
lowering.lowerRoot(root);
|
lowering.lowerRoot(root);
|
||||||
if (self.diagnostics.hasErrors()) return error.CompileError;
|
if (self.diagnostics.hasErrors()) return error.CompileError;
|
||||||
|
|
||||||
|
|||||||
109
src/imports.test.zig
Normal file
109
src/imports.test.zig
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Tests for imports.zig — flat-import name-resolution data retention (fix-0102a).
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const ast = @import("ast.zig");
|
||||||
|
const parser = @import("parser.zig");
|
||||||
|
const imports = @import("imports.zig");
|
||||||
|
|
||||||
|
var g_test_threaded: ?std.Io.Threaded = null;
|
||||||
|
fn testIo() std.Io {
|
||||||
|
if (g_test_threaded == null) {
|
||||||
|
g_test_threaded = std.Io.Threaded.init(std.heap.page_allocator, .{});
|
||||||
|
}
|
||||||
|
return g_test_threaded.?.io();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two flat-imported modules each author `greet`; a third is namespaced. The
|
||||||
|
// step retains BOTH `greet` authors under their own paths in `module_fns`, and
|
||||||
|
// records the namespaced import in `import_graph` but NOT in `flat_import_graph`.
|
||||||
|
test "imports: module_fns retains same-name cross-module fns; flat_import_graph excludes namespaced edge" {
|
||||||
|
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 = "a.sx", .data = "greet :: () -> s64 { 1 }\n" });
|
||||||
|
try tmp.dir.writeFile(io, .{ .sub_path = "b.sx", .data = "greet :: () -> s64 { 2 }\n" });
|
||||||
|
try tmp.dir.writeFile(io, .{ .sub_path = "nsmod.sx", .data = "helper :: () -> s64 { 3 }\n" });
|
||||||
|
const main_src =
|
||||||
|
\\#import "a.sx";
|
||||||
|
\\#import "b.sx";
|
||||||
|
\\ns :: #import "nsmod.sx";
|
||||||
|
\\main :: () -> s32 { 0 }
|
||||||
|
\\
|
||||||
|
;
|
||||||
|
try tmp.dir.writeFile(io, .{ .sub_path = "main.sx", .data = main_src });
|
||||||
|
|
||||||
|
var dirbuf: [4096]u8 = undefined;
|
||||||
|
const dirlen = try tmp.dir.realPath(io, &dirbuf);
|
||||||
|
const absdir = dirbuf[0..dirlen];
|
||||||
|
|
||||||
|
const main_path = try std.fmt.allocPrint(alloc, "{s}/main.sx", .{absdir});
|
||||||
|
const a_path = try std.fmt.allocPrint(alloc, "{s}/a.sx", .{absdir});
|
||||||
|
const b_path = try std.fmt.allocPrint(alloc, "{s}/b.sx", .{absdir});
|
||||||
|
const ns_path = try std.fmt.allocPrint(alloc, "{s}/nsmod.sx", .{absdir});
|
||||||
|
|
||||||
|
const main_bytes = try std.Io.Dir.readFileAlloc(.cwd(), io, main_path, alloc, .limited(1 << 20));
|
||||||
|
const main_source = try alloc.dupeZ(u8, main_bytes);
|
||||||
|
var p = parser.Parser.init(alloc, main_source);
|
||||||
|
const root = p.parse() catch return error.ParseFailed;
|
||||||
|
|
||||||
|
var chain = std.StringHashMap(void).init(alloc);
|
||||||
|
var cache = imports.ModuleCache.init(alloc);
|
||||||
|
var import_graph = std.StringHashMap(std.StringHashMap(void)).init(alloc);
|
||||||
|
var flat_import_graph = std.StringHashMap(std.StringHashMap(void)).init(alloc);
|
||||||
|
const stdlib_paths = [_][]const u8{};
|
||||||
|
|
||||||
|
const mod = try imports.resolveImports(
|
||||||
|
alloc,
|
||||||
|
io,
|
||||||
|
root,
|
||||||
|
absdir,
|
||||||
|
main_path,
|
||||||
|
&chain,
|
||||||
|
&cache,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
&stdlib_paths,
|
||||||
|
&import_graph,
|
||||||
|
&flat_import_graph,
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
|
||||||
|
var module_fns = imports.ModuleFns.init(alloc);
|
||||||
|
try imports.buildModuleFns(alloc, main_path, mod, &cache, &module_fns);
|
||||||
|
|
||||||
|
// mergeFlat no longer first-wins-drops the second `greet`: the global flat
|
||||||
|
// decl list the lowering pass walks now carries BOTH authors.
|
||||||
|
var greet_count: usize = 0;
|
||||||
|
for (mod.decls) |decl| {
|
||||||
|
const name = decl.data.declName() orelse continue;
|
||||||
|
if (std.mem.eql(u8, name, "greet")) greet_count += 1;
|
||||||
|
}
|
||||||
|
try std.testing.expectEqual(@as(usize, 2), greet_count);
|
||||||
|
|
||||||
|
// module_fns retains BOTH authors of `greet`, keyed by their own paths.
|
||||||
|
const a_fns = module_fns.get(a_path) orelse return error.MissingAFns;
|
||||||
|
const b_fns = module_fns.get(b_path) orelse return error.MissingBFns;
|
||||||
|
const a_greet = a_fns.get("greet") orelse return error.MissingAGreet;
|
||||||
|
const b_greet = b_fns.get("greet") orelse return error.MissingBGreet;
|
||||||
|
// Distinct authoring decls — not the same node deduped down to one.
|
||||||
|
try std.testing.expect(a_greet != b_greet);
|
||||||
|
|
||||||
|
// flat_import_graph carries the two bare `#import` edges, NOT the
|
||||||
|
// namespaced `ns :: #import` edge.
|
||||||
|
const flat = flat_import_graph.get(main_path) orelse return error.MissingFlatEdges;
|
||||||
|
try std.testing.expect(flat.contains(a_path));
|
||||||
|
try std.testing.expect(flat.contains(b_path));
|
||||||
|
try std.testing.expect(!flat.contains(ns_path));
|
||||||
|
|
||||||
|
// The full import_graph DOES record the namespaced edge (the contrast that
|
||||||
|
// makes the flat-graph exclusion meaningful).
|
||||||
|
const full = import_graph.get(main_path) orelse return error.MissingFullEdges;
|
||||||
|
try std.testing.expect(full.contains(a_path));
|
||||||
|
try std.testing.expect(full.contains(b_path));
|
||||||
|
try std.testing.expect(full.contains(ns_path));
|
||||||
|
}
|
||||||
109
src/imports.zig
109
src/imports.zig
@@ -5,6 +5,32 @@ const errors = @import("errors.zig");
|
|||||||
const c_import = @import("c_import.zig");
|
const c_import = @import("c_import.zig");
|
||||||
const Node = ast.Node;
|
const Node = ast.Node;
|
||||||
|
|
||||||
|
/// True iff `decl` authors a top-level FUNCTION — either a bare `fn_decl`
|
||||||
|
/// (`f :: (…) -> T { … }`) or a `const_decl` whose value is a function
|
||||||
|
/// (`f :: (…) { … }` parsed as a const-bound fn). Used by the flat / directory
|
||||||
|
/// merge to retain a same-name function authored by a DIFFERENT module instead
|
||||||
|
/// of first-wins dropping it (fix-0102a): a flat importer that pulls two modules
|
||||||
|
/// each authoring `f` needs BOTH decls reachable downstream. Non-function decls
|
||||||
|
/// keep first-wins dedup.
|
||||||
|
fn declAuthorsFn(decl: *const Node) bool {
|
||||||
|
return switch (decl.data) {
|
||||||
|
.fn_decl => true,
|
||||||
|
.const_decl => |cd| cd.value.data == .fn_decl,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `*const ast.FnDecl` a function-authoring decl carries, or null when the
|
||||||
|
/// decl is not a function (mirrors `declAuthorsFn`). Drives the per-module
|
||||||
|
/// `module_fns` identity index (fix-0102a).
|
||||||
|
fn fnDeclOf(decl: *const Node) ?*const ast.FnDecl {
|
||||||
|
return switch (decl.data) {
|
||||||
|
.fn_decl => &decl.data.fn_decl,
|
||||||
|
.const_decl => |cd| if (cd.value.data == .fn_decl) &cd.value.data.fn_decl else null,
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Comptime evaluation context for the inline-if hoisting pass below.
|
/// Comptime evaluation context for the inline-if hoisting pass below.
|
||||||
/// Mirrors the values `injectComptimeConstants` will later push into the
|
/// Mirrors the values `injectComptimeConstants` will later push into the
|
||||||
/// lowering's `comptime_constants` map (OS / ARCH / POINTER_SIZE), but
|
/// lowering's `comptime_constants` map (OS / ARCH / POINTER_SIZE), but
|
||||||
@@ -334,8 +360,13 @@ pub const ResolvedModule = struct {
|
|||||||
for (other.decls) |decl| {
|
for (other.decls) |decl| {
|
||||||
if (seen_nodes.contains(decl)) continue;
|
if (seen_nodes.contains(decl)) continue;
|
||||||
if (decl.data.declName()) |name| {
|
if (decl.data.declName()) |name| {
|
||||||
if (seen_list.contains(name)) continue;
|
if (seen_list.contains(name)) {
|
||||||
try seen_list.put(name, {});
|
// First-wins dedup for non-functions; retain a same-name
|
||||||
|
// FUNCTION authored by a different module (fix-0102a).
|
||||||
|
if (!declAuthorsFn(decl)) continue;
|
||||||
|
} else {
|
||||||
|
try seen_list.put(name, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try seen_nodes.put(decl, {});
|
try seen_nodes.put(decl, {});
|
||||||
try list.append(allocator, decl);
|
try list.append(allocator, decl);
|
||||||
@@ -392,6 +423,43 @@ pub const ResolvedModule = struct {
|
|||||||
/// Module cache: maps resolved file paths to their ResolvedModules.
|
/// Module cache: maps resolved file paths to their ResolvedModules.
|
||||||
pub const ModuleCache = std.StringHashMap(ResolvedModule);
|
pub const ModuleCache = std.StringHashMap(ResolvedModule);
|
||||||
|
|
||||||
|
/// Per-module function identity index: function NAME → the `*const FnDecl` that
|
||||||
|
/// module AUTHORS. Mirrors a single module's slice of `module_scopes`.
|
||||||
|
pub const FnIndex = std.StringHashMap(*const ast.FnDecl);
|
||||||
|
|
||||||
|
/// `path → name → *const FnDecl`, mirroring `module_scopes`. One entry per
|
||||||
|
/// resolved module keyed by its path (a directory's combined module keyed by
|
||||||
|
/// `dir_path`); each entry indexes only what that module AUTHORS. Two modules
|
||||||
|
/// each authoring `f` are retained under their own paths — the identity index
|
||||||
|
/// fix-0102c's bare-name disambiguation consults to bind a flat call to the
|
||||||
|
/// right author.
|
||||||
|
pub const ModuleFns = std.StringHashMap(FnIndex);
|
||||||
|
|
||||||
|
/// Index a single module's authored functions (`own_decls`) into `out[path]`.
|
||||||
|
/// First-wins WITHIN a module mirrors the scan pass; cross-module same-name
|
||||||
|
/// authors live under their own `path` keys.
|
||||||
|
fn indexModuleFns(allocator: std.mem.Allocator, out: *ModuleFns, path: []const u8, own_decls: []const *Node) !void {
|
||||||
|
const gop = try out.getOrPut(path);
|
||||||
|
if (!gop.found_existing) gop.value_ptr.* = FnIndex.init(allocator);
|
||||||
|
for (own_decls) |decl| {
|
||||||
|
const fd = fnDeclOf(decl) orelse continue;
|
||||||
|
const name = decl.data.declName() orelse continue;
|
||||||
|
if (gop.value_ptr.contains(name)) continue;
|
||||||
|
try gop.value_ptr.put(name, fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the per-module function index from a resolved program: the main module
|
||||||
|
/// (keyed by `main_path`) plus every cached module (keyed by its own path).
|
||||||
|
/// Mirrors how `core.zig` fills `module_scopes` from `mod.scope` + the cache.
|
||||||
|
pub fn buildModuleFns(allocator: std.mem.Allocator, main_path: []const u8, main_mod: ResolvedModule, cache: *const ModuleCache, out: *ModuleFns) !void {
|
||||||
|
try indexModuleFns(allocator, out, main_path, main_mod.own_decls);
|
||||||
|
var it = cache.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
try indexModuleFns(allocator, out, entry.key_ptr.*, entry.value_ptr.own_decls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolveImports(
|
pub fn resolveImports(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
io: std.Io,
|
io: std.Io,
|
||||||
@@ -404,6 +472,7 @@ pub fn resolveImports(
|
|||||||
diagnostics: ?*errors.DiagnosticList,
|
diagnostics: ?*errors.DiagnosticList,
|
||||||
stdlib_paths: []const []const u8,
|
stdlib_paths: []const []const u8,
|
||||||
import_graph: ?*std.StringHashMap(std.StringHashMap(void)),
|
import_graph: ?*std.StringHashMap(std.StringHashMap(void)),
|
||||||
|
flat_import_graph: ?*std.StringHashMap(std.StringHashMap(void)),
|
||||||
comptime_ctx: ComptimeContext,
|
comptime_ctx: ComptimeContext,
|
||||||
) !ResolvedModule {
|
) !ResolvedModule {
|
||||||
// Record this file's edge set so `param_impl_map` lookups can filter
|
// Record this file's edge set so `param_impl_map` lookups can filter
|
||||||
@@ -414,6 +483,15 @@ pub fn resolveImports(
|
|||||||
try g.put(file_path, std.StringHashMap(void).init(allocator));
|
try g.put(file_path, std.StringHashMap(void).init(allocator));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// FLAT-only edge set: identical to `import_graph` but records ONLY bare
|
||||||
|
// `#import "…"` edges (`imp.name == null`), never a namespaced
|
||||||
|
// `ns :: #import "…"`. fix-0102c's bare-name disambiguation walks this to
|
||||||
|
// decide which same-name authors a flat importer can actually reach.
|
||||||
|
if (flat_import_graph) |g| {
|
||||||
|
if (!g.contains(file_path)) {
|
||||||
|
try g.put(file_path, std.StringHashMap(void).init(allocator));
|
||||||
|
}
|
||||||
|
}
|
||||||
var mod = ResolvedModule{
|
var mod = ResolvedModule{
|
||||||
.path = file_path,
|
.path = file_path,
|
||||||
.decls = &.{},
|
.decls = &.{},
|
||||||
@@ -536,6 +614,17 @@ pub fn resolveImports(
|
|||||||
set.put(resolved_path, {}) catch {};
|
set.put(resolved_path, {}) catch {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The same edge, FLAT-only: recorded only for a bare `#import`
|
||||||
|
// (`imp.name == null`), excluding a namespaced `ns :: #import`. Covers
|
||||||
|
// both a flat file import and a flat directory import (`resolved_path`
|
||||||
|
// is the directory in the latter case).
|
||||||
|
if (imp.name == null) {
|
||||||
|
if (flat_import_graph) |g| {
|
||||||
|
if (g.getPtr(file_path)) |set| {
|
||||||
|
set.put(resolved_path, {}) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Circular import check — only along the current chain
|
// Circular import check — only along the current chain
|
||||||
if (chain.contains(resolved_path)) continue;
|
if (chain.contains(resolved_path)) continue;
|
||||||
@@ -563,7 +652,7 @@ pub fn resolveImports(
|
|||||||
// Push onto chain before recursing, pop after
|
// Push onto chain before recursing, pop after
|
||||||
try chain.put(resolved_path, {});
|
try chain.put(resolved_path, {});
|
||||||
const imp_dir = dirName(resolved_path);
|
const imp_dir = dirName(resolved_path);
|
||||||
const result = try resolveImports(allocator, io, imp_root, imp_dir, resolved_path, chain, cache, source_map, diagnostics, stdlib_paths, import_graph, comptime_ctx);
|
const result = try resolveImports(allocator, io, imp_root, imp_dir, resolved_path, chain, cache, source_map, diagnostics, stdlib_paths, import_graph, flat_import_graph, comptime_ctx);
|
||||||
_ = chain.remove(resolved_path);
|
_ = chain.remove(resolved_path);
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
@@ -571,7 +660,7 @@ pub fn resolveImports(
|
|||||||
break :blk result;
|
break :blk result;
|
||||||
} else |_| {
|
} else |_| {
|
||||||
// File read failed — try as directory import
|
// File read failed — try as directory import
|
||||||
const result = resolveDirectoryImport(allocator, io, resolved_path, chain, cache, source_map, diagnostics, decl.span, stdlib_paths, import_graph, comptime_ctx) catch {
|
const result = resolveDirectoryImport(allocator, io, resolved_path, chain, cache, source_map, diagnostics, decl.span, stdlib_paths, import_graph, flat_import_graph, comptime_ctx) catch {
|
||||||
if (diagnostics) |diags| {
|
if (diagnostics) |diags| {
|
||||||
diags.addFmt(.err, decl.span, "cannot read import '{s}' (not a file or directory)", .{resolved_path});
|
diags.addFmt(.err, decl.span, "cannot read import '{s}' (not a file or directory)", .{resolved_path});
|
||||||
}
|
}
|
||||||
@@ -605,6 +694,7 @@ fn resolveDirectoryImport(
|
|||||||
span: ast.Span,
|
span: ast.Span,
|
||||||
stdlib_paths: []const []const u8,
|
stdlib_paths: []const []const u8,
|
||||||
import_graph: ?*std.StringHashMap(std.StringHashMap(void)),
|
import_graph: ?*std.StringHashMap(std.StringHashMap(void)),
|
||||||
|
flat_import_graph: ?*std.StringHashMap(std.StringHashMap(void)),
|
||||||
comptime_ctx: ComptimeContext,
|
comptime_ctx: ComptimeContext,
|
||||||
) anyerror!ResolvedModule {
|
) anyerror!ResolvedModule {
|
||||||
// Open the directory with iteration capability
|
// Open the directory with iteration capability
|
||||||
@@ -679,7 +769,7 @@ fn resolveDirectoryImport(
|
|||||||
};
|
};
|
||||||
|
|
||||||
try chain.put(file_path, {});
|
try chain.put(file_path, {});
|
||||||
const result = try resolveImports(allocator, io, imp_root, dir_path, file_path, chain, cache, source_map, diagnostics, stdlib_paths, import_graph, comptime_ctx);
|
const result = try resolveImports(allocator, io, imp_root, dir_path, file_path, chain, cache, source_map, diagnostics, stdlib_paths, import_graph, flat_import_graph, comptime_ctx);
|
||||||
_ = chain.remove(file_path);
|
_ = chain.remove(file_path);
|
||||||
|
|
||||||
try cache.put(file_path, result);
|
try cache.put(file_path, result);
|
||||||
@@ -697,8 +787,13 @@ fn resolveDirectoryImport(
|
|||||||
for (file_mod.decls) |decl| {
|
for (file_mod.decls) |decl| {
|
||||||
if (seen_nodes.contains(decl)) continue;
|
if (seen_nodes.contains(decl)) continue;
|
||||||
if (decl.data.declName()) |name| {
|
if (decl.data.declName()) |name| {
|
||||||
if (seen_in_list.contains(name)) continue;
|
if (seen_in_list.contains(name)) {
|
||||||
try seen_in_list.put(name, {});
|
// First-wins dedup for non-functions; retain a same-name
|
||||||
|
// FUNCTION authored by a different file (fix-0102a).
|
||||||
|
if (!declAuthorsFn(decl)) continue;
|
||||||
|
} else {
|
||||||
|
try seen_in_list.put(name, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try seen_nodes.put(decl, {});
|
try seen_nodes.put(decl, {});
|
||||||
try decl_list.append(allocator, decl);
|
try decl_list.append(allocator, decl);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ast = @import("../ast.zig");
|
const ast = @import("../ast.zig");
|
||||||
|
const imports = @import("../imports.zig");
|
||||||
const types = @import("types.zig");
|
const types = @import("types.zig");
|
||||||
const inst = @import("inst.zig");
|
const inst = @import("inst.zig");
|
||||||
const errors = @import("../errors.zig");
|
const errors = @import("../errors.zig");
|
||||||
@@ -570,9 +571,10 @@ pub const GlobalInfo = struct { id: inst.GlobalId, ty: TypeId };
|
|||||||
/// `self.program_index.<field>`; later phases hand collaborator modules a
|
/// `self.program_index.<field>`; later phases hand collaborator modules a
|
||||||
/// `*ProgramIndex` instead of `*Lowering`.
|
/// `*ProgramIndex` instead of `*Lowering`.
|
||||||
///
|
///
|
||||||
/// OWNS the declaration maps below. BORROWS `module_scopes` / `import_graph`
|
/// OWNS the declaration maps below. BORROWS `module_scopes` / `import_graph` /
|
||||||
/// (pointers into maps owned by the compilation driver, `core.zig`) — those
|
/// `flat_import_graph` / `module_fns` (pointers into maps owned by the
|
||||||
/// are read-only views and are never freed here.
|
/// compilation driver, `core.zig`) — those are read-only views and are never
|
||||||
|
/// freed here.
|
||||||
///
|
///
|
||||||
/// Per-map allocators are preserved exactly as they were on `Lowering`:
|
/// Per-map allocators are preserved exactly as they were on `Lowering`:
|
||||||
/// `import_flags` / `fn_ast_map` / `global_names` use the lowering allocator
|
/// `import_flags` / `fn_ast_map` / `global_names` use the lowering allocator
|
||||||
@@ -587,6 +589,16 @@ pub const ProgramIndex = struct {
|
|||||||
/// Module path → set of directly imported paths (param_impl visibility
|
/// Module path → set of directly imported paths (param_impl visibility
|
||||||
/// filter). Borrowed view.
|
/// filter). Borrowed view.
|
||||||
import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
||||||
|
/// Module path → set of directly FLAT-imported paths — the subset of
|
||||||
|
/// `import_graph` edges from a bare `#import` (never a namespaced
|
||||||
|
/// `ns :: #import`). fix-0102c's bare-name disambiguation walks this to
|
||||||
|
/// decide which same-name authors a flat importer can reach. Borrowed view.
|
||||||
|
flat_import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
||||||
|
/// Module path → (function name → authoring `*const FnDecl`), mirroring
|
||||||
|
/// `module_scopes`. Retains every same-name author under its own path so
|
||||||
|
/// fix-0102c can resolve a flat call to the right module's function.
|
||||||
|
/// Borrowed view.
|
||||||
|
module_fns: ?*imports.ModuleFns = null,
|
||||||
|
|
||||||
// ── Declaration maps ──
|
// ── Declaration maps ──
|
||||||
/// Function name → AST decl.
|
/// Function name → AST decl.
|
||||||
@@ -627,7 +639,8 @@ pub const ProgramIndex = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *ProgramIndex) void {
|
pub fn deinit(self: *ProgramIndex) void {
|
||||||
// Owned maps only — module_scopes / import_graph are borrowed.
|
// Owned maps only — module_scopes / import_graph / flat_import_graph /
|
||||||
|
// module_fns are borrowed.
|
||||||
self.import_flags.deinit();
|
self.import_flags.deinit();
|
||||||
self.fn_ast_map.deinit();
|
self.fn_ast_map.deinit();
|
||||||
self.qualified_fn_source.deinit();
|
self.qualified_fn_source.deinit();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pub const trace_runtime_tests = @import("runtime_trace.test.zig");
|
|||||||
pub const sema = @import("sema.zig");
|
pub const sema = @import("sema.zig");
|
||||||
pub const sema_tests = @import("sema.test.zig");
|
pub const sema_tests = @import("sema.test.zig");
|
||||||
pub const imports = @import("imports.zig");
|
pub const imports = @import("imports.zig");
|
||||||
|
pub const imports_tests = @import("imports.test.zig");
|
||||||
pub const core = @import("core.zig");
|
pub const core = @import("core.zig");
|
||||||
pub const c_import = @import("c_import.zig");
|
pub const c_import = @import("c_import.zig");
|
||||||
pub const ir = @import("ir/ir.zig");
|
pub const ir = @import("ir/ir.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user