Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
9.4 KiB
0106 — namespaced-import internal names are silently bare-visible (over-permissive isNameVisible)
RESOLVED (flow stdlib/B attempt-3 — root fix, no exemption). Two coupled changes:
- Tightened bare visibility to the flat edge set.
isNameVisible/isCImportVisiblenow route through the unifiedisVisiblepredicate overuser_bare_flat/c_import_bare(both join overflat_import_graph, notimport_graph). A namespaced-only import's internal name is no longer bare-visible — face #1 now errors'<name>' is not visible; #import the module that declares it.- Pin the defining-module context during pack/comptime monomorphization. The flat tightening alone broke
std.print/log.*: a library metaprogram's body (#insert build_format(fmt)comptime call + the#insert "out(result);"inserted statement) was lowered under the CALL SITE'scurrent_source_file, so its bare names (build_format,out,emit) were policed against the consumer's imports. Root cause:monomorphizePackFn(bareformat) andlowerComptimeCall(namespacedstd.print/log.*, reached via the field-accesshasComptimeParamsbranch) lower the metaprogram body without pinning the source context — unlike a normal function, which lowers vialowerFunctionBodyIntopinningfunc.source_file. Fix: both paths now save/set/restorecurrent_source_fileto the body's DEFINING module before lowering the body (the call-site ARGS are lowered first, in the caller's context, which is correct). The defining path is stamped onto each function body node byresolveImports(stampFnBodySource, mirroring how a declared function carriesFunction.source_file). So the metaprogram's barebuild_format/out/emitresolve instd.sx/log.sxnaturally — and a USER's#insert <expr>is still checked in the USER's context, so a bare reach into a namespaced-only import there errors. No#insertexemption (attempt-2'sin_insert_expansionflag is deleted): the fix is the absence of an exemption, not a narrower one.- Substituted caller
$-args resolve in the CALLER's context (attempt-5). The point-2 defining-module pin covers the metaprogram body's OWN code only. A caller-provided comptime$-arg (e.g. a caller-owned helper passed to an imported metaprogram) is spliced into the body bysubstituteComptimeNodes; those nodes are CALLER-authored and must resolve in the caller's visibility context, not the callee's. Fix: the$-arg node is stamped with the caller'ssource_fileat thecpnbuild site (lowerComptimeCall/monomorphizePackFn,stampCallerSource), andlowerExprswitchescurrent_source_fileto a node'ssource_filewhen present — so the substituted subtree resolves against the caller while the surrounding callee code keeps the defining-module pin. Regression:examples/0738-modules-comptime-arg-caller-context.sx(caller-owned helper as a comptime-only$-arg through a namespaced import; fail-before "'caller_name' is not visible" → pass-after "hello world").Root cause:
isNameVisiblewalkedimport_graph(flat AND namespaced edges) where a bare name should join only overflat_import_graph; and the pack / comptime monomorphizers lowered the metaprogram body under the wrong source context. Regressions:examples/0736-modules-namespaced-only-bare-not-visible.sx(+0736-…/a.sx) — face #1 pinned (exit 1 + the stderr);examples/0737-modules-insert-bare-not-visible.sx(+0737-…/a.sx) — a USER#insert secret()into a namespaced-only import errors (fail-before exit 0 on the attempt-2 exemption / pass-after exit 1). Face #2 restored WITHOUT an exemption:examples/0015 / 0700 / 0718 / 1030pass again (run_examples471 → 474, incl. the attempt-5 caller-context regression0738). Fix insrc/ir/lower.zig(monomorphizePackFn+lowerComptimeCallsource-context pin; exemption removed) +src/imports.zig(stampFnBodySource) +src/ir/resolver.zig(VisibilityModemodes, landed in attempt-1).
Symptom. A bare reference to a top-level name authored in a module that the
consumer imports only namespaced (ns :: #import "m.sx") is silently
visible from the consumer. Observed: it compiles + runs. Expected: an error —
the name is reachable only as ns.name. Root: Lowering.isNameVisible walks
program_index.import_graph, which records BOTH flat (#import) and namespaced
(ns :: #import) edges; bare-name visibility should join only over FLAT edges
(flat_import_graph). This is the latent 0102-family visibility bug the Phase B
caller-mode audit (unified-resolver R5) was told to surface.
This directly gates flow stdlib/B: that step requires migrating
isNameVisible/isCImportVisible to the resolver's user_bare_flat/
c_import_bare modes (which walk flat_import_graph) byte-identically.
Switching the edge set drops run_examples from 471 → 467 (see face #2), so the
byte-identical requirement cannot hold until this bug is fixed.
Reproduction — face #1 (user-facing over-permissiveness)
// m.sx
secret :: () -> i64 { 7 }
// main.sx
m :: #import "m.sx";
main :: () -> i32 {
x := secret(); // bare; `secret` is only namespaced-imported as `m.secret`
0
}
- Observed (current master): compiles, runs, exit
0— baresecretwrongly resolves tom's author. - Expected:
error: 'secret' is not visible; #import the module that declares it(it is reachable only asm.secret).
(With the Phase-B edge-set change applied — isNameVisible over
flat_import_graph — this repro correctly errors, confirming the diagnosis.)
Reproduction — face #2 (library comptime entanglement: why a naive fix breaks std)
// main.sx
std :: #import "modules/std.sx";
main :: () -> i32 {
std.print("hello\n"); // legit qualified call
0
}
print in std.sx is a comptime metaprogram:
print :: ($fmt: string, ..$args) {
#insert build_format(fmt); // comptime call to a std-internal fn
#insert "out(result);"; // inserts a bare call to a std-internal fn
}
The comptime call to build_format and the inserted out(result) are bare
names authored in std.sx, but they are visibility-checked in the
consumer's current_source_file context (comptime / #insert expansion
happens at the call site). Today they pass only because import_graph[consumer]
contains the namespaced std edge. Tightening bare visibility to
flat_import_graph makes them error
('build_format' / 'out' is not visible). The same shape breaks log (emit).
Affected examples when the edge set is switched to flat:
0015-basic-demo, 0700-modules-import, 0718-modules-cli-exit-json,
1030-errors-log-and-comptime (467/471).
Root cause (suspected area)
Lowering.isNameVisible/isCImportVisible—src/ir/lower.zig(~1768-1840 after the Phase-B refactor;visibleOverEdges/nameVisibleOverEdges). The cross-module join usesimport_graph(flat and namespaced edges) where it should useflat_import_graphfor a bare name.- Comptime /
#insertexpansion context: the inserted/comptime-evaluated bare calls of a namespaced module's function are policed against the consumer's imports, not the defining module's own scope. The existing visibility check already exempts UFCS-alias rewrites and mangled local names as "compiler indirections" (lower.zigcall site ~7284, identifier site ~3237); inserted / comptime-generated bare calls are the same kind of indirection and are not yet exempt — or, equivalently, the expansion should restore the defining module'scurrent_source_file.
Investigation prompt (paste into a fresh session)
Fix issue 0106:
isNameVisibleover-permits bare references to a namespaced-only import's internal names. Two coupled changes, in this order:
- Library-internal context. Ensure a namespaced/comptime-expanded function's body — including
#inserted statements and comptime calls likebuild_formatinsidestd.print— is visibility-checked in its defining module's context, OR exempt compiler-generated /#inserted bare calls from the visibility check (mirror the UFCS-alias / mangled-name exemptions atsrc/ir/lower.zig~7284 and ~3237). Verify with the face-#2 repro and examples0015 / 0700 / 0718 / 1030.- Tighten bare visibility to flat. Change
isNameVisible(and thec_import_barefall-through ofisCImportVisible) to join overflat_import_graphinstead ofimport_graph— i.e. route them through the resolver'suser_bare_flat/c_import_baremodes (this is exactly Phase B of the unified-resolver R5 plan;src/ir/resolver.zigalready defines the modes andLowering.visibleOverEdgesalready takes a.flat/.allselector). Verify the face-#1 repro now errors.Acceptance: the face-#1 repro errors ("not visible");
bash tests/run_examples.shis back to 471okwith bare visibility on the flat edge set; add the face-#1 repro as a pinned regression (issues/0106-…with anexpected/marker, or promote toexamples/07xx-modules-…). Suspected files:src/ir/lower.zig(visibility + comptime/#insert expansion context),src/ir/resolver.zig(user_bare_flat/c_import_bare).