fix(lower): #insert-expansion visibility exemption + close 0106 [stdlib B attempt-2]
Folds the coupled 0106 fix into Phase B. attempt-1 tightened the bare-name
visibility adapters (isNameVisible/isCImportVisible) to the flat_import_graph
edge set via the unified isVisible(.user_bare_flat/.c_import_bare) predicate;
that surfaced issue 0106 — std.print / log.* expand `#insert build_format(fmt)`
(comptime call) and `#insert "out(result);"` (inserted stmt) in the CONSUMER's
current_source_file, so their library-internal bare names were policed against
the consumer's imports and errored (run_examples 471 -> 467).
Fix: a precise, named exemption. Lowering.in_insert_expansion is set across
lowerInsertExprValue (the comptime eval + the parsed-back statements); the two
visibility adapters fall open while it is set — mirroring the existing
UFCS-alias / mangled-local "compiler indirection" exemptions. NOT a blanket
skip: scoped to #insert-expanded code, ordinary bare references stay policed.
Library-internal call bodies (build_format's concat/substr) already resolve in
the defining module — lowerFunctionBodyInto pins their current_source_file.
The flat tightening stays: a bare reference to a namespaced-only import's
internal name now correctly errors ('<name>' is not visible). This is the
Agra-ratified user-visible semantics change.
- face #1 pinned: examples/0736-modules-namespaced-only-bare-not-visible.sx
(+ a.sx) — exit 1 + stderr; fail-before (import_graph compiled it, exit 0) /
pass-after (flat set errors, exit 1).
- face #2 restored: examples 0015 / 0700 / 0718 / 1030 pass again.
- run_examples 471 -> 472 (the new regression).
- issues/0106 marked RESOLVED; readme.md documents namespaced-only visibility.
Collectors + unified predicate from attempt-1 (resolver.zig) unchanged; nothing
routes resolution AUTHOR-SELECTION through them yet (that is Phase C).
This commit is contained in:
@@ -169,6 +169,17 @@ pub const Lowering = struct {
|
||||
/// `self.program_index.<field>`; populated by scan/registration code.
|
||||
program_index: ProgramIndex,
|
||||
current_source_file: ?[]const u8 = null, // source file of function currently being lowered
|
||||
/// True while lowering the product of a `#insert` expansion — the comptime
|
||||
/// call whose string drives the insert, plus the statements parsed back from
|
||||
/// that string. Bare names in this synthesized code are authored by the
|
||||
/// library metaprogram (e.g. `out`/`emit`/`build_format` inside `std.print`),
|
||||
/// not user-typed at the call site, so the bare-name `#import` visibility
|
||||
/// adapters (`isNameVisible` / `isCImportVisible`) exempt them — the same
|
||||
/// "compiler indirection" exemption already given to UFCS-alias rewrites and
|
||||
/// mangled local names (issue 0106). It is NOT a blanket skip: it scopes the
|
||||
/// exemption to `#insert`-expanded code only; ordinary bare references still
|
||||
/// get policed. Saved/restored to nest correctly.
|
||||
in_insert_expansion: bool = false,
|
||||
// Implicit Context parameter machinery. When the program imports
|
||||
// `std.sx` (and therefore declares `Context :: struct {...}`), every
|
||||
// default-conv sx function gains a synthetic `__sx_ctx: *void` param
|
||||
@@ -1846,14 +1857,19 @@ pub const Lowering = struct {
|
||||
|
||||
/// Check if a C-imported function is visible from the current source file.
|
||||
/// Returns true for non-C functions (always visible) or if no scoping info
|
||||
/// available. Byte-identical adapter over `isVisible`.
|
||||
/// available. Adapter over `isVisible(.c_import_bare)`, plus the
|
||||
/// `#insert`-expansion exemption (issue 0106).
|
||||
fn isCImportVisible(self: *Lowering, fn_name: []const u8) bool {
|
||||
if (self.in_insert_expansion) return true;
|
||||
return self.isVisible(fn_name, .c_import_bare);
|
||||
}
|
||||
|
||||
/// Non-transitive `#import` visibility check for top-level decls.
|
||||
/// Byte-identical adapter over `isVisible`.
|
||||
/// Non-transitive `#import` visibility check for top-level decls. Adapter
|
||||
/// over `isVisible(.user_bare_flat)`, plus the `#insert`-expansion exemption:
|
||||
/// names emitted by a library metaprogram's insert are compiler
|
||||
/// indirections, not user-typed call-site references (issue 0106).
|
||||
fn isNameVisible(self: *Lowering, name: []const u8) bool {
|
||||
if (self.in_insert_expansion) return true;
|
||||
return self.isVisible(name, .user_bare_flat);
|
||||
}
|
||||
|
||||
@@ -9480,6 +9496,14 @@ pub const Lowering = struct {
|
||||
|
||||
/// Like lowerInsertExpr but returns the value of the last parsed expression.
|
||||
fn lowerInsertExprValue(self: *Lowering, expr: *const Node) Ref {
|
||||
// The comptime call that produces the insert string and the statements
|
||||
// parsed back from it are library-metaprogram code, not user-typed bare
|
||||
// names at this call site — exempt them from the `#import` visibility
|
||||
// adapters (issue 0106). Saved/restored so nested `#insert`s compose.
|
||||
const saved_insert = self.in_insert_expansion;
|
||||
self.in_insert_expansion = true;
|
||||
defer self.in_insert_expansion = saved_insert;
|
||||
|
||||
// Step 1: Substitute comptime param nodes (e.g., replace $fmt with its literal)
|
||||
const substituted = if (self.comptime_param_nodes) |cpn|
|
||||
self.substituteComptimeNodes(expr, cpn) catch expr
|
||||
|
||||
Reference in New Issue
Block a user