fix(lower): pin defining-module context for pack/comptime metaprograms; drop #insert exemption [stdlib B attempt-3]

ROOT FIX for issue 0106's library-metaprogram half — no exemption.

attempt-2 masked the 0106 fallout with an `in_insert_expansion` flag that
made the visibility adapters fall open during ANY `#insert` expansion,
including a USER's `#insert <expr>` — so a bare reach into a namespaced-only
import from user `#insert` code wrongly compiled (Adi's blocker). The flag
was the wrong shape. This removes it and fixes the real cause.

Root cause: a metaprogram's body (`std.print` / `std.format` / `log.*`,
whose `#insert build_format(fmt)` + `#insert "out(result);"` reference
std-internal bare names) was lowered under the CALL SITE's
`current_source_file`, so those names were policed against the consumer's
imports. Normal functions get this right via `lowerFunctionBodyInto`, which
pins `func.source_file`; the two monomorphizers don't:
  - `monomorphizePackFn`   — bare `print(...)` / `format(...)` (pack path).
  - `lowerComptimeCall`    — namespaced `std.print` / `log.warn` (reached via
                             the field-access `hasComptimeParams` branch).

Fix: both paths now save/set/restore `current_source_file` to the body's
DEFINING module around the BODY lowering only (call-site args stay in the
caller's context). The defining path is stamped onto each function body node
by `resolveImports` (`stampFnBodySource`), mirroring `Function.source_file`.
So library internals resolve in std.sx/log.sx naturally, while a USER's
`#insert <expr>` is still checked in the user's context.

- Exemption GONE: `in_insert_expansion` flag + both adapter fall-open checks
  deleted; `isNameVisible`/`isCImportVisible` are byte-identical adapters.
- New pinned regression: examples/0737-modules-insert-bare-not-visible.sx
  (+ a.sx) — a USER `#insert secret()` into a namespaced-only import errors
  ('secret' is not visible). fail-before exit 0 on the attempt-2 binary /
  pass-after exit 1.
- face #1 (0736) still errors; face #2 (0015/0700/0718/1030) pass again WITH
  NO exemption — the metaprogram body resolves in its own module.
- run_examples 472 -> 473; zig build test 412/412; m3te ios-sim build exit 0.
- issues/0106 RESOLVED banner updated (root cause + no-exemption fix).
This commit is contained in:
agra
2026-06-07 06:09:28 +03:00
parent 6f2bf84293
commit b62223edaf
8 changed files with 106 additions and 48 deletions

View File

@@ -591,6 +591,25 @@ fn reportDuplicateName(diagnostics: ?*errors.DiagnosticList, added: bool, name:
diags.addFmt(.err, span, "duplicate top-level declaration '{s}'", .{name});
}
/// Stamp the DEFINING module path onto a function body node, so a later
/// pack/comptime monomorphization can pin `current_source_file` to the body's
/// own module and resolve its bare names in that module's visibility context
/// (issue 0106) — mirroring how a normally-declared function carries
/// `Function.source_file`. Only top-level decl Nodes are otherwise stamped, so
/// the body Node would carry no source; a null body source after this means a
/// synthesized/sourceless decl (the monomorphizer then keeps its caller's
/// context, the legitimate fall-open).
fn stampFnBodySource(decl: *Node, file_path: []const u8) void {
switch (decl.data) {
.fn_decl => |fd| fd.body.source_file = file_path,
.const_decl => |cd| switch (cd.value.data) {
.fn_decl => |fd| fd.body.source_file = file_path,
else => {},
},
else => {},
}
}
/// `reportDuplicateName` keyed off a node whose `declName()` carries the name
/// (the regular authored-decl sites; an `import_decl` has no `declName`, so a
/// namespace alias must use `reportDuplicateName` with the alias directly).
@@ -746,6 +765,7 @@ pub fn resolveImports(
}
if (decl.data != .import_decl) {
decl.source_file = file_path;
stampFnBodySource(decl, file_path);
reportDuplicateDecl(diagnostics, try mod.addOwnDecl(allocator, &decl_list, &own_decl_list, &seen_in_list, decl), decl);
continue;
}