v5 Semantics: import/export resolution

Lesson, slides, and applied problem sets.

View Slides

Lesson

v5 Semantics: modules as a typed interface boundary

v5 turns source into a multi-module package model with explicit compile-time visibility.

Every module now has:

  • a public export surface
  • imported aliases
  • deterministic link-time rules

These are semantic contracts that must be validated before compile/link.


1) Module graph invariants

Identity rules

  • module names must be unique
  • main must exist
  • main must be final module in declaration order

Dependency direction

import must reference a module declared earlier.

Why this strictness:

  • deterministic one-pass linking
  • no forward-resolution cycles in this pack
  • stable compiler behavior in educational setting

Typical diagnostics

  • duplicate module: <name>
  • main module must be last
  • unknown module: <name>
  • import of future module: <name>

2) Two-phase semantic analysis strategy

A stable implementation uses two passes per module:

  1. Export surface collection pass
    • gather exported names and their shapes
    • build module export map for later resolution
  2. Body validation pass
    • resolve imports into local scope as aliases
    • validate statements and expressions against scope/module maps

This avoids false negatives where a body references an export declared later in the same module body and keeps member access checks simple.


3) Import alias scoping and collisions

Imported module aliases become scoped entries in the current module scope.

Rules:

  • explicit and implicit aliasing supported
  • duplicates in same scope are redeclaration errors
  • aliases are used for all member qualification

No special runtime behavior for aliases at this stage; this is purely semantic binding.


4) Member access contract

For m.name in semantic analysis:

  • m must be resolvable in current scope
  • binding for m must denote module alias
  • name must exist in that module’s export set

Failure classes:

  • unknown module: <alias>
  • invalid module access (left side is not an identifier alias)
  • unknown export: <module>.<name>

This makes dotted access fully typed by surface and deterministic.


5) Compatibility with previous semantics

v5 layers on v4 and does not replace it:

  • undefined identifier checks
  • redeclare checks
  • const assignment checks
  • function arity compatibility
  • control-flow checks for return, break, continue

If older rules still fire, module rules should still be present and consistent.


6) Why module checks belong here, not later

If import/export errors are deferred to link/runtime:

  • error messages become less local to source
  • stage ownership is violated
  • debugging cost rises steeply

Doing these checks in analyzer yields:

  • deterministic early failures
  • cleaner capstone semantics
  • better pedagogical loop (source -> analyzer -> compile)

7) Practice checkpoints (almost solved)

1) Module namespace leak + `module util { export fn id(x: number) -> number { return x; } } module main { import util; return id(1); }`

Expected:

  • valid module graph
  • unqualified id is unresolved
  • must use util.id(1)

2) Future module import

`module main { import util; } module util { export fn id(x:number)->number { return x; } }`

Expected: import of future module: util

3) Unknown module

module main { import missing as m; }

Expected: unknown module: missing

4) Unknown export

`module util { export fn id(x:number)->number { return x; } } module main { import util; util.nope(); }`

Expected: unknown export: util.nope

5) Alias collision

module main { import util; import util as u; }

Expected: redeclare/alias collision at analyzer level.

6) Alias target check

module main { let m = 1; m.fn(); }

Expected: invalid module access because left side is not alias.


Module Items

  • Semantic Analysis v5

    Validate v5 programs with imports, exports, and module access.

    hard Upgrade to Pro to access hard problems
Join Discord