fix(stdlib/E5): source-aware same-name VALUE consts (own-wins / ambiguous / cross-module expr-chains)

Re-land the value-const analog of the E1-E4 type work, reconciled onto the
current source-keyed resolver and hardened. A same-name VALUE const declared in
multiple flat-imported modules is now resolved per declaring source, not the
global last-wins `module_const_map`.

- imports.zig: `isPerSourceDecl` retains every non-function `const_decl`
  per-source (value consts + type aliases), so each same-name author reaches
  registration as a distinct author of its own module. Functions and var_decls
  keep first-wins.
- lower.zig:
  * `selectModuleConst` over `module_consts_by_source` — own-wins; exactly one
    flat-visible resolves; >=2 flat-visible bare -> loud ambiguous (consistent
    with the 0755 type / 0724 fn / 0782 generic ambiguities). Rewires every
    consumer: `comptimeIntNamed`, the runtime-id read, the global-init read,
    and the float-name path (`lookupFloatName` / `nameIsFloatTyped`).
  * `SourceConstCtx` + `foldSourceConstInt`/`Float` + `sourceConstIsFloatTyped`
    fold a selected const's RHS with nested same-name leaves re-selected in
    their own author source, so VALUE and array-DIMENSION results are coherent.
  * `pinConstAuthorSource` pins each fold level to the SELECTED const's author
    (F1), including multi-level cross-module chains.
  * cycle guard keyed on (name, author-source), not name alone (F3), so
    same-name nested consts across modules do not trip a false cycle.
  * `emitModuleConst` takes the author source and pins while folding/lowering.
  Registration-time struct/inline-type field dimensions route through the now
  source-aware stateful reader; the type-alias dimension path resolves each
  alias against its own author's consts.
- program_index.zig: expose `isFloatConstType` / `isCountableConstType` for the
  source-aware folds.

examples: 0786 own-wins, 0787 ambiguous (exit 1), 0788 expr-chain value+dim
coherent, 0789 leaf-author-pin, 0790 cross-module cycle-guard (F3), 0791
multi-level cross-module chain, 0792 struct-field registration-time dim.
Single-author corpus byte-identical (524 prior markers green); 531 total.
This commit is contained in:
agra
2026-06-08 21:29:31 +03:00
parent 37c3b1e1f4
commit 5df4ac61a7
25 changed files with 450 additions and 86 deletions

View File

@@ -314,8 +314,8 @@ pub const ResolvedModule = struct {
try self.scope.put(name, {});
if (seen_list.contains(name)) {
// A cross-module name collision: drop from the global list
// (first-wins) UNLESS this is a per-source decl (a named type or
// a type-alias const), which must reach registration as a
// (first-wins) UNLESS this is a per-source decl (a type, alias,
// or non-function const), which must reach registration as a
// distinct author of its own module (issues 0104/0105).
append_to_global = isPerSourceDecl(decl);
} else {
@@ -352,14 +352,14 @@ pub const ResolvedModule = struct {
if (decl.data.declName()) |name| {
if (seen_list.contains(name)) {
// First-wins on a cross-module name collision — EXCEPT a
// per-source decl (a named type or a type-alias const), each
// of which must reach registration as a distinct same-name
// author of its own module (issues 0104/0105). FUNCTIONS and
// VALUE consts keep first-wins (issue 0102 — the shadowed
// function stays reachable via its qualified name /
// SelectedFunc; same-name value consts are deferred to step
// E5). Node identity (above) still de-dups a diamond import of
// the SAME decl.
// per-source decl (a named type, or any non-function const:
// type alias + value const), each of which must reach
// registration as a distinct same-name author of its own
// module (issues 0104/0105 types, step E5 value consts). Only
// FUNCTIONS keep first-wins (issue 0102 — the shadowed author
// stays reachable via its qualified name / SelectedFunc).
// Node identity (above) still de-dups a diamond import of the
// SAME decl.
if (!isPerSourceDecl(decl)) continue;
} else {
try seen_list.put(name, {});
@@ -372,43 +372,19 @@ pub const ResolvedModule = struct {
/// A decl that must register PER-SOURCE: each same-name author across modules
/// registers against its OWN module rather than collapsing to a single
/// first-wins winner. NAMED types and TYPE-introducing `const_decl`s (type
/// aliases + inline type decls, source-keyed via the alias cache) are
/// per-source — that is what closes issues 0104/0105 for types and aliases.
/// Everything else keeps the first-wins name-merge:
/// - FUNCTIONS (issue 0102 — the shadowed author stays reachable via its
/// qualified name / SelectedFunc),
/// - VALUE `const_decl`s (literal / value-expression RHS): a same-name
/// value const keeps the pre-E2 first-wins read; cross-module same-name
/// value-const support is a separate concern (step E5), NOT part of the
/// 0105 type close,
/// - and `var_decl`s, including a `#foreign` extern global declared in two
/// files (e.g. `__stdinp : *void #foreign;`) that MUST resolve to the ONE
/// libSystem symbol, not split into a duplicate `__stdinp.1`.
/// first-wins winner. NAMED types and every non-function `const_decl` (type
/// aliases + inline type decls + VALUE consts, source-keyed via the alias /
/// const caches) are per-source — that is what closes issues 0104/0105 for
/// types/aliases and supports same-name value consts (step E5). Everything
/// else keeps the first-wins name-merge: FUNCTIONS (issue 0102 — the shadowed
/// author stays reachable via its qualified name / SelectedFunc), and crucially
/// `var_decl`s, including a `#foreign` extern global declared in two files
/// (e.g. `__stdinp : *void #foreign;`) that MUST resolve to the ONE libSystem
/// symbol, not split into a duplicate `__stdinp.1`.
fn isPerSourceDecl(decl: *const Node) bool {
return switch (decl.data) {
.struct_decl, .enum_decl, .union_decl, .error_set_decl, .protocol_decl, .foreign_class_decl => true,
// A `const_decl` is per-source ONLY when its RHS introduces a TYPE
// (alias / inline type decl). A VALUE const — literal or value
// expression — and a function const keep the first-wins merge.
.const_decl => |cd| switch (cd.value.data) {
.fn_decl,
.int_literal,
.float_literal,
.bool_literal,
.string_literal,
.null_literal,
.undef_literal,
.enum_literal,
.struct_literal,
.array_literal,
.tuple_literal,
.binary_op,
.unary_op,
.chained_comparison,
=> false,
else => true,
},
.const_decl => |cd| cd.value.data != .fn_decl,
else => false,
};
}