Record the flat-namespace limit found by the dependency run
Every input file's items go into one Nim module, and the type tables are keyed by bare name while `fns` is keyed by `(module, name)`. Across 356 dependency crates that is 14,714 distinct type names of which 1,575 are defined more than once -- `Output` 8,963 times, `Error` 3,372. Found where `color_quant`'s `type Color = Quad<i32>` replaced `anstyle`'s `enum Color`, after which anstyle's own `match` was a pattern against `Quad`. Written down rather than fixed: the remedy is to key the type tables the way `fns` is already keyed and qualify emitted names on collision, which reshapes the lowering rather than patching it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d553b29 parent: 8194456 modified
DESIGN.md +39 -0 | @@ -1268,3 +1268,42 @@ this, because handling it needs a whole-program view: the disambiguation has | ||
| 1268 | 1268 | to be consistent across every use, and which of a colliding pair gets renamed |
| 1269 | 1269 | cannot be decided one identifier at a time. Untouched for now, and recorded so |
| 1270 | 1270 | it is not rediscovered as a mystery. |
| 1271 | + | |
| 1272 | +### The flat namespace does not survive a dependency graph | |
| 1273 | + | |
| 1274 | +rustnim puts every input file's items into one Nim module. With a handful of | |
| 1275 | +files that is the right call — it is what makes cross-file calls work without | |
| 1276 | +modelling Rust's module system. With 357 crates it breaks, and the way it | |
| 1277 | +breaks is worth being precise about. | |
| 1278 | + | |
| 1279 | +`self.fns` is keyed by `(module, name)`. `self.structs`, `self.enums`, | |
| 1280 | +`self.aliases` and `self.methods` are keyed by the bare name. So a type in one | |
| 1281 | +crate silently replaces a same-named type in another. The case that found it: | |
| 1282 | + | |
| 1283 | +```rust | |
| 1284 | +// anstyle | |
| 1285 | +pub enum Color { Ansi(AnsiColor), Ansi256(..), Rgb(..) } | |
| 1286 | +// color_quant | |
| 1287 | +type Color = Quad<i32>; | |
| 1288 | +``` | |
| 1289 | + | |
| 1290 | +`color_quant`'s alias wins, and `anstyle`'s `match self { Color::Ansi(c) => .. }` | |
| 1291 | +is then a pattern against `Quad`. rustnim says so and stops, which is the | |
| 1292 | +founding rule working — but it stops on a name clash, not on anything about | |
| 1293 | +the program. | |
| 1294 | + | |
| 1295 | +Counting across the 356 dependency crates: **14,714 distinct type names, of | |
| 1296 | +which 1,575 are defined in more than one place.** `Output` appears 8,963 | |
| 1297 | +times, `Error` 3,372, `Item` 1,838. This is not a tail to chip away at; it is | |
| 1298 | +the normal shape of a Rust dependency graph, where every crate is its own | |
| 1299 | +namespace. | |
| 1300 | + | |
| 1301 | +Separately, 29 of those names collide only under *Nim's* identifier equality | |
| 1302 | +(case-insensitive after the first character, underscore-insensitive) — the | |
| 1303 | +hazard recorded above, at graph scale. | |
| 1304 | + | |
| 1305 | +The fix is the one already used for functions: key the type tables by | |
| 1306 | +`(module, name)`, resolve by preferring the current module and then a unique | |
| 1307 | +global match, and qualify the emitted Nim name where a bare one would clash. | |
| 1308 | +That is a real change to the shape of the lowering, not an incremental patch, | |
| 1309 | +which is why it is written down here rather than attempted in passing. | |
| @@ -1268,3 +1268,42 @@ this, because handling it needs a whole-program view: the disambiguation has | |||
| 1268 | to be consistent across every use, and which of a colliding pair gets renamed | 1268 | to be consistent across every use, and which of a colliding pair gets renamed |
| 1269 | cannot be decided one identifier at a time. Untouched for now, and recorded so | 1269 | cannot be decided one identifier at a time. Untouched for now, and recorded so |
| 1270 | it is not rediscovered as a mystery. | 1270 | it is not rediscovered as a mystery. |
| 1271 | + | ||
| 1272 | +### The flat namespace does not survive a dependency graph | ||
| 1273 | + | ||
| 1274 | +rustnim puts every input file's items into one Nim module. With a handful of | ||
| 1275 | +files that is the right call — it is what makes cross-file calls work without | ||
| 1276 | +modelling Rust's module system. With 357 crates it breaks, and the way it | ||
| 1277 | +breaks is worth being precise about. | ||
| 1278 | + | ||
| 1279 | +`self.fns` is keyed by `(module, name)`. `self.structs`, `self.enums`, | ||
| 1280 | +`self.aliases` and `self.methods` are keyed by the bare name. So a type in one | ||
| 1281 | +crate silently replaces a same-named type in another. The case that found it: | ||
| 1282 | + | ||
| 1283 | +```rust | ||
| 1284 | +// anstyle | ||
| 1285 | +pub enum Color { Ansi(AnsiColor), Ansi256(..), Rgb(..) } | ||
| 1286 | +// color_quant | ||
| 1287 | +type Color = Quad<i32>; | ||
| 1288 | +``` | ||
| 1289 | + | ||
| 1290 | +`color_quant`'s alias wins, and `anstyle`'s `match self { Color::Ansi(c) => .. }` | ||
| 1291 | +is then a pattern against `Quad`. rustnim says so and stops, which is the | ||
| 1292 | +founding rule working — but it stops on a name clash, not on anything about | ||
| 1293 | +the program. | ||
| 1294 | + | ||
| 1295 | +Counting across the 356 dependency crates: **14,714 distinct type names, of | ||
| 1296 | +which 1,575 are defined in more than one place.** `Output` appears 8,963 | ||
| 1297 | +times, `Error` 3,372, `Item` 1,838. This is not a tail to chip away at; it is | ||
| 1298 | +the normal shape of a Rust dependency graph, where every crate is its own | ||
| 1299 | +namespace. | ||
| 1300 | + | ||
| 1301 | +Separately, 29 of those names collide only under *Nim's* identifier equality | ||
| 1302 | +(case-insensitive after the first character, underscore-insensitive) — the | ||
| 1303 | +hazard recorded above, at graph scale. | ||
| 1304 | + | ||
| 1305 | +The fix is the one already used for functions: key the type tables by | ||
| 1306 | +`(module, name)`, resolve by preferring the current module and then a unique | ||
| 1307 | +global match, and qualify the emitted Nim name where a bare one would clash. | ||
| 1308 | +That is a real change to the shape of the lowering, not an incremental patch, | ||
| 1309 | +which is why it is written down here rather than attempted in passing. | ||