ir: whole-program passes pin the source context per decl (fix 0122)
convergeClosureShapeSets, checkErrorFlow, and the unknown-type loop ran under whatever current_source_file the previous phase left behind — closure-literal annotations resolved (and reject/unknown-type diagnostics rendered) against an arbitrary module. Latent while std.sx was a single file (the ambient happened to be the main file); the re-export facade restructure exposed it. Each walk now pins setCurrentSourceFile per decl / per fn (body.source_file is already stamped by resolveImports). Coverage: examples 0129/1047/1049/1052/ 1053/1056 against the facade std.sx. Gates: zbt 426/426, suite 588/588.
This commit is contained in:
@@ -191,8 +191,16 @@ pub const ErrorAnalysis = struct {
|
||||
/// by all occurrences of its value-signature shape. A `try slot(x)` against
|
||||
/// any matching-shape slot then widens against this union.
|
||||
pub fn convergeClosureShapeSets(self: ErrorAnalysis) void {
|
||||
// Pin the visibility context to each fn's DEFINING module
|
||||
// (body.source_file, stamped by resolveImports) — a closure literal's
|
||||
// param/return annotations must resolve where the fn is written, not
|
||||
// against whatever module the previous pipeline phase happened to
|
||||
// leave as the ambient context (issue 0122).
|
||||
const saved = self.l.current_source_file;
|
||||
defer self.l.setCurrentSourceFile(saved);
|
||||
var it = self.l.program_index.fn_ast_map.iterator();
|
||||
while (it.next()) |e| {
|
||||
self.l.setCurrentSourceFile(e.value_ptr.*.body.source_file orelse saved);
|
||||
self.collectClosureShapes(e.value_ptr.*.body);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,12 +71,19 @@ pub const ErrorFlow = struct {
|
||||
/// this conservative check would over-reject), so they are skipped.
|
||||
pub fn checkErrorFlow(self: ErrorFlow, decls: []const *const Node) void {
|
||||
if (self.l.diagnostics == null) return;
|
||||
const saved_file = self.l.current_source_file;
|
||||
defer self.l.setCurrentSourceFile(saved_file);
|
||||
for (decls) |decl| {
|
||||
if (self.l.main_file) |mf| {
|
||||
if (decl.source_file) |sf| {
|
||||
if (!std.mem.eql(u8, sf, mf)) continue;
|
||||
}
|
||||
}
|
||||
// Pin the visibility context (and diagnostic rendering) to the
|
||||
// decl's own module — the flow walk resolves types via
|
||||
// inferExprType, and the ambient file the previous phase left
|
||||
// behind is arbitrary (issue 0122).
|
||||
if (decl.source_file) |sf| self.l.setCurrentSourceFile(sf);
|
||||
switch (decl.data) {
|
||||
.fn_decl => |fd| self.analyzeFnBody(fd.body),
|
||||
.const_decl => |cd| {
|
||||
|
||||
@@ -65,12 +65,17 @@ pub const UnknownTypeChecker = struct {
|
||||
var declared = std.StringHashMap(void).init(self.alloc);
|
||||
defer declared.deinit();
|
||||
self.collectDeclaredTypeNames(decls, &declared);
|
||||
const saved_file = self.diagnostics.current_source_file;
|
||||
defer self.diagnostics.current_source_file = saved_file;
|
||||
for (decls) |decl| {
|
||||
if (self.main_file) |mf| {
|
||||
if (decl.source_file) |sf| {
|
||||
if (!std.mem.eql(u8, sf, mf)) continue;
|
||||
}
|
||||
}
|
||||
// Render against the decl's own module, not the ambient file the
|
||||
// previous phase left behind (issue 0122).
|
||||
if (decl.source_file) |sf| self.diagnostics.current_source_file = sf;
|
||||
switch (decl.data) {
|
||||
.fn_decl => self.checkFnSignatureTypes(&decl.data.fn_decl, &declared),
|
||||
.struct_decl => |sd| self.checkStructFieldTypes(&sd, &declared),
|
||||
|
||||
Reference in New Issue
Block a user