# 0102 — flat-import same-name function collision (per-source binding) **RESOLVED.** Two **flat** imports (bare `#import "a.sx"` / a flat directory import, NOT a namespaced `ns :: #import`) that each author a top-level free function with the **same short name** collided in IR lowering. The flat/ directory merge keeps exactly **one** author per name in the merged decl list (first-wins), and every bare-name consumer site — call dispatch, default-arg expansion, function-value capture, free-function UFCS, comptime `#run` — read that one **name-keyed** winner. So when module `b.sx` authored its own `greet` but `a.sx` was imported first, `b.sx`'s own bare `greet()` silently bound `a.sx`'s author. Unlike issue 0100 (which crashed on a param-count assert when the AST/FuncId split across modules), this miscompiled **silently**: the wrong same-name author ran, with no diagnostic. The defect had two faces, both rooted in name-keyed identity across a flat collision: 1. **Lowering** keyed function bodies by short name (`fn_ast_map` / `resolveFuncByName` are first-wins), so a shadowed author never got its own FuncId or body — there was nothing to bind even if a consumer wanted the per-source author. 2. **Resolution** at every bare-name consumer site re-looked-up the winner by name, so even once shadow authors had distinct FuncIds, the consumer sites kept binding the first-wins winner. ## Fix — four sub-steps (`src/imports.zig`, `src/ir/lower.zig`) - **0102a — retain dup authors + identity indexes.** The flat/directory merge keeps first-wins in the merged scope (unchanged), but now *also* retains every dropped same-name author in `program_index.module_fns` (`path → name → *FnDecl`) plus a `flat_import_graph` (`file → flat-import edges`). Resolution is untouched at this step — the indexes just make the shadowed authors addressable. - **0102b — identity-addressable function lowering.** `fn_decl_fids` (`*const ast.FnDecl → FuncId`) lets a body be declared + lowered against a **specific** `*FnDecl` (`lowerFunctionBodyInto` / `bareAuthorFuncId`) instead of a name. A shadow author gets a fresh same-name FuncId in its own module's visibility context; the winner keeps the name-keyed slot. `scanDecls` keys `fn_decl_fids` by the stable `module_fns` `*FnDecl`. - **0102c — THE resolver + call path + param typing.** `resolveBareCallee(name, caller_file) -> .func(ResolvedAuthor) | .ambiguous | .none` (`src/ir/lower.zig`). It returns `.none` whenever the outcome would equal first-wins (single author, or own-author == winner), so every single-author / local / parameter / std / qualified / extern / generic / builtin name resolves byte-for-byte as before. Only a genuine flat collision reroutes: own-author wins; else the caller's flat-reachable authors — `≥2` distinct → `.ambiguous` (loud "qualify the call" diagnostic), exactly one differing from the winner → bind it. Routed the **primary call path** and the call's **parameter target typing** (so a `*T`-param shadow gets implicit address-of, not a value bit-cast to a pointer → segfault). - **0102d — the four remaining bare-name sites.** Routed the SAME resolver through every other site that resolved a bare callee/function-name by first-wins, each gated exactly as the call path (plain top-level identifier, no scope-mangle / UFCS alias / local shadow; act on `.func` / `.ambiguous`, fall through on `.none`): 1. **Default-argument expansion** (`expandCallDefaults`): omitted trailing args fill from the RESOLVED author's defaults, not the winner's. 2. **Function-value conversion** (`closure(fn)` and the bare-fn-as-value `func_ref` / fn-ptr / closure-coercion path): captures the resolved author's FuncId. The winner's body is lazily lowered ONLY on the `.none` fallback — a rerouted value never uses the winner, so taking a shadow as a value must not pre-lower (and possibly mis-diagnose) the winner's body. 3. **Free-function UFCS** (`recv.fn()` → `fn(recv, …)`): dispatches the resolved author for the receiver's source. 4. **Comptime `#run`** of a bare call: `lowerMainAndComptime` now sets `current_source_file` per decl, so a `NAME :: #run f()` in an imported module resolves `f` from THAT module's flat imports (own-author wins) rather than the main file's perspective (where two flat authors made it spuriously `.ambiguous` and failed the build). ## Regression tests `examples/0722`–`0735` (each a focused multi-file flat-collision scene that fails on pre-fix code and passes after): - `0722-modules-flat-same-name-own` — own-author wins on the call path. - `0723-modules-flat-vs-namespaced` — a flat author + a namespaced same-name author don't collide. - `0724-modules-flat-same-name-ambiguous` — `≥2` flat authors, bare call → loud diagnostic. - `0725-modules-flat-dir-same-name` — flat **directory** import collision. - `0726-modules-flat-same-name-variadic` — per-source variadic packing. - `0728-modules-flat-same-name-paramtype` — per-source parameter target typing (value vs pointer param). - `0729-modules-flat-same-name-extern` — same-name `extern` authors are NOT rerouted (non-plain authors keep first-wins). - `0730-modules-flat-same-name-default-arg` — per-source default-arg expansion. - `0731-modules-flat-same-name-closure` — per-source `closure(fn)` + bare fn-value capture. - `0732-modules-flat-same-name-ufcs` — per-source free-function UFCS dispatch. - `0733-modules-flat-same-name-comptime-run` — per-source comptime `#run` callee. - `0734-modules-flat-same-name-ufcs-ambiguous` — `≥2` flat authors, UFCS call → loud diagnostic (pre-fix: silently bound the winner). - `0735-modules-flat-same-name-fn-value-winner` — the first-wins winner's body is independently broken and never used; a shadow taken as a function value binds the shadow and runs while the winner is NOT lowered (pre-fix: the fn-value site eagerly lowered the winner before the resolver rerouted, surfacing the winner's error for a function the value never touches).