Description
The module system has fundamental bugs that make multi-directory project layouts unusable. These were found through comprehensive stress testing using a realistic project structure (EZDB) with main/, src/, src/engine/, src/models/, src/util/, src/http/, pkg/ directories.
The bugs fall into three categories: path resolution, namespace/naming, and deduplication.
Path Resolution Bugs
Bug 1: Transitive import paths resolve relative to the entry file, not the importing file
When file A imports file B, and B has import "../models/user.ez", that relative path is resolved from A's directory instead of B's directory.
Example layout:
project/
├── main/
│ └── app.ez ← entry point
└── src/
├── models/
│ └── user.ez
└── http/
└── router.ez ← has: import "../models/user.ez"
Running ez main/app.ez where app.ez imports ../src/http/router.ez:
- Expected:
router.ez's import "../models/user.ez" resolves to src/models/user.ez
- Actual: Resolves to
main/../models/user.ez → models/user.ez (not found)
This breaks any file that imports relative to its own location when it's pulled in transitively from a different directory.
Bug 2: Cross-module dependencies from within a directory import fail
When a file inside a directory module imports something outside that directory, the path resolves from the entry file's location instead of the file's own location.
src/engine/input.ez: import "../util/validate.ez" ← should resolve to src/util/validate.ez
When the entry file is tests/test.ez and imports ../src/engine/, the transitive import in input.ez resolves to tests/../util/validate.ez which doesn't exist.
Namespace & Naming Bugs
Bug 3: Directory import module name resolves to empty string
Importing a directory produces W1002: module '' is imported but never used. The module name should be derived from the directory name (e.g., engine for import "../src/engine/").
Bug 4: Directory-imported functions get underscore prefix instead of directory namespace
When importing ../src/engine/, functions from files within that directory are prefixed with _ instead of engine_. For example, create_game() from core.ez becomes _create_game instead of engine_create_game. This means engine.create_game() resolves to void — the namespace is completely broken.
Visible in warnings:
warning[W1003]: function '_create_game' is declared but never called
warning[W1003]: function '_default_config' is declared but never called
warning[W1003]: function '_zero_vec' is declared but never called
Bug 5: Intra-module struct name mangling is corrupted
When a file within a directory module imports a sibling file (e.g., audio.ez imports ./core.ez), struct names get mangled with the file prefix. Game becomes core_Game, and then field access on core_Game fails:
error[E3010]: struct 'core_Game' has no field 'title'
The struct should either be accessible as Game within the same module or the field lookup should work with the mangled name.
Bug 6: Multiple directory imports collide on empty module name
Importing two different directories in the same file:
import "../src/engine/"
import "../src/models/"
Both resolve to module name "", so the second import triggers:
error[E6001]: module name '' is already imported
Each directory should get its own namespace derived from its directory name.
Bug 7: Name collision across files in same directory uses wrong prefix
When two files in the same directory define a function with the same name, the error reports the wrong name:
error[E4004]: function '_init' already declared
The function should be collision_init (directory-namespaced), not _init.
Deduplication Bugs
Bug 8: Diamond dependencies cause false E6001 errors
When the entry file imports module X directly, and also imports module Y which transitively imports the same module X, the compiler treats the transitive copy as a duplicate:
import "../src/util/validate.ez"
import "../src/engine/" // engine/input.ez also imports validate.ez
Produces:
error[E6001]: module name 'validate' is already imported
The compiler should recognize both paths resolve to the same file and deduplicate silently.
Bug 9: Directory import + file import of same content collides
import "../src/models/" // pulls in user.ez and session.ez
import "../src/models/user.ez" // also imports user.ez directly
Produces E6001: module name 'user' is already imported. The compiler should deduplicate by resolved absolute path.
Bug 10: Self-referential directory import not handled
A file inside engine/ that imports ../engine/ (its own directory) doesn't infinite loop (good), but also doesn't produce a clear error. It should either be silently ignored (the file is already part of the module) or produce a specific error like "cannot import own module directory".
Expected Behavior
Once fixed, the module system should support this workflow:
project/
├── main/
│ └── main.ez ← entry point
├── src/
│ ├── engine/
│ │ ├── core.ez ← defines Game struct, create_game(), start()
│ │ ├── renderer.ez ← defines RenderConfig, default_config()
│ │ ├── physics.ez ← defines Vec2, zero_vec(), add_vec()
│ │ ├── audio.ez ← imports ./core.ez, uses Game
│ │ └── input.ez ← imports ../util/validate.ez
│ ├── models/
│ │ ├── user.ez
│ │ └── session.ez ← imports ./user.ez
│ └── util/
│ ├── validate.ez
│ └── format.ez ← imports ../models/user.ez
└── pkg/
└── shared/
└── constants.ez
// main/main.ez
import "../src/engine/" // directory import — all files as one namespace
import "../src/models/user.ez" // file import
import "../pkg/shared/constants.ez" // file import from separate tree
do main() {
mut g = engine.create_game("My Game") // from engine/core.ez
mut cfg = engine.default_config() // from engine/renderer.ez
mut v = engine.zero_vec() // from engine/physics.ez
mut u = user.new_user("Alice", "[email protected]", 30)
println(constants.APP_NAME)
}
Key requirements:
- Relative imports resolve from the file that contains the
import statement
- Directory imports register all contents under the directory name as namespace
- Files within a directory module can import siblings with
./sibling.ez
- Files within a directory module can import outside with
../other/file.ez
- Diamond dependencies deduplicate by resolved absolute path
- Directory + file import of same content deduplicates silently
- Multiple directory imports each get their own namespace
- Name collisions across files in the same directory produce clear errors with correct names
- Self-referential directory imports are handled gracefully
Affected Code
ezc/src/main.c — import resolution, path construction, directory scanning, module name derivation
- Possibly
ezc/src/typechecker/typechecker.c — namespace/prefix registration for directory-imported symbols
Description
The module system has fundamental bugs that make multi-directory project layouts unusable. These were found through comprehensive stress testing using a realistic project structure (EZDB) with
main/,src/,src/engine/,src/models/,src/util/,src/http/,pkg/directories.The bugs fall into three categories: path resolution, namespace/naming, and deduplication.
Path Resolution Bugs
Bug 1: Transitive import paths resolve relative to the entry file, not the importing file
When file A imports file B, and B has
import "../models/user.ez", that relative path is resolved from A's directory instead of B's directory.Example layout:
Running
ez main/app.ezwhereapp.ezimports../src/http/router.ez:router.ez'simport "../models/user.ez"resolves tosrc/models/user.ezmain/../models/user.ez→models/user.ez(not found)This breaks any file that imports relative to its own location when it's pulled in transitively from a different directory.
Bug 2: Cross-module dependencies from within a directory import fail
When a file inside a directory module imports something outside that directory, the path resolves from the entry file's location instead of the file's own location.
When the entry file is
tests/test.ezand imports../src/engine/, the transitive import ininput.ezresolves totests/../util/validate.ezwhich doesn't exist.Namespace & Naming Bugs
Bug 3: Directory import module name resolves to empty string
Importing a directory produces
W1002: module '' is imported but never used. The module name should be derived from the directory name (e.g.,engineforimport "../src/engine/").Bug 4: Directory-imported functions get underscore prefix instead of directory namespace
When importing
../src/engine/, functions from files within that directory are prefixed with_instead ofengine_. For example,create_game()fromcore.ezbecomes_create_gameinstead ofengine_create_game. This meansengine.create_game()resolves to void — the namespace is completely broken.Visible in warnings:
Bug 5: Intra-module struct name mangling is corrupted
When a file within a directory module imports a sibling file (e.g.,
audio.ezimports./core.ez), struct names get mangled with the file prefix.Gamebecomescore_Game, and then field access oncore_Gamefails:The struct should either be accessible as
Gamewithin the same module or the field lookup should work with the mangled name.Bug 6: Multiple directory imports collide on empty module name
Importing two different directories in the same file:
Both resolve to module name
"", so the second import triggers:Each directory should get its own namespace derived from its directory name.
Bug 7: Name collision across files in same directory uses wrong prefix
When two files in the same directory define a function with the same name, the error reports the wrong name:
The function should be
collision_init(directory-namespaced), not_init.Deduplication Bugs
Bug 8: Diamond dependencies cause false E6001 errors
When the entry file imports module X directly, and also imports module Y which transitively imports the same module X, the compiler treats the transitive copy as a duplicate:
Produces:
The compiler should recognize both paths resolve to the same file and deduplicate silently.
Bug 9: Directory import + file import of same content collides
Produces
E6001: module name 'user' is already imported. The compiler should deduplicate by resolved absolute path.Bug 10: Self-referential directory import not handled
A file inside
engine/that imports../engine/(its own directory) doesn't infinite loop (good), but also doesn't produce a clear error. It should either be silently ignored (the file is already part of the module) or produce a specific error like "cannot import own module directory".Expected Behavior
Once fixed, the module system should support this workflow:
Key requirements:
importstatement./sibling.ez../other/file.ezAffected Code
ezc/src/main.c— import resolution, path construction, directory scanning, module name derivationezc/src/typechecker/typechecker.c— namespace/prefix registration for directory-imported symbols