You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(codegen): thread imported TopConst across module boundaries (closes#107) (#109)
Closes#107.
Before this commit, `Codegen.gen_imports` and
`Module_loader.flatten_imports` both pattern-matched only `TopFn` in
the imported module's `prog_decls`, silently dropping public `TopConst`
items. A `use M::{some_const}` that the typechecker accepted would
then fail at WASM codegen with `Codegen.UnboundVariable some_const`.
## Changes
### `lib/codegen.ml` — `gen_imports`
- Match `TopFn` and `TopConst` in declaration order; first match wins
(same-name fn+const in one module would be a resolver error).
- For an imported `TopFn`: unchanged (emits the WASM
`(import "<mod>" "<fn>" ...)` and registers the positive function
index).
- For an imported `TopConst`: compile the initialiser against the
importer's context via `gen_expr`, append a fresh global, register
`(local_name, -(global_idx + 1))` in `func_indices` — same
negative-sentinel encoding as locally-declared consts, so use-site
lookup is uniform.
- Glob expansion (`use M::*`) likewise includes public consts.
WASM module-linking for globals isn't standard yet, so the const value
is inlined per importer. Identity is by value, which matches
AffineScript's immutability guarantee.
### `lib/module_loader.ml` — `flatten_imports`
- Local-decl name suppression set now covers fns AND consts.
- Public consts get inlined into the importer's `prog_decls` alongside
public fns, with the same alias-renaming machinery for `use M::{c as
d}`.
- Non-WASM back-ends (Rust, JS, OCaml, Lua, …) see imported consts as
if they were locally declared, so they inherit the fix without
per-target changes.
### Tests — `test/test_e2e.ml`
Two regression tests in the `E2E Xmod Other Codegens` group:
- `flatten_imports inlines imported public consts (#107)` — parses
`cross_const_caller.affine`, runs the loader, asserts the imported
`input_marker` const and `marker_plus` fn both appear in the
flattened `prog_decls`.
- `WASM gen_imports threads imported consts (#107)` — compiles
`PortNames.affine` then `cross_const_caller.affine`; asserts the
caller emits at least one global initialised to the const's value
(256). The earlier failure mode would have errored out at
`compile_fixture_to_wasm` before the assertion.
Two new fixtures:
- `test/e2e/fixtures/PortNames.affine` — exports `pub const
input_marker` + a fn that uses it.
- `test/e2e/fixtures/cross_const_caller.affine` — imports and uses
both.
### Docs
- `docs/specs/SPEC.adoc` §8.4 — drops the "do not yet flow" bullet for
consts; the cross-module flow table now lists const-as-inlined-value
as supported.
- `docs/specs/codegen-environment.adoc` §5 — split into 5.1 (TopFn),
5.2 (TopConst inlining algorithm + identity-by-value note), 5.3
(glob expansion). §10 records #107 as closed and points at the
regression tests.
## Inline-record-escape note
`TopConst` is declared with an inline record. The OCaml compiler
rejects `| TopConst tc when ...` followed by using `tc` as a value
("the type of the inlined record could escape"), so the match
destructures the needed fields directly: `| TopConst { tc_name;
tc_value; _ } when tc_name.name = orig_name -> Some (`Const tc_value)`.
The glob path destructures `{ tc_vis; tc_name; _ }`. Reconstruction in
the alias-rename path uses inline-literal construction, which is
allowed.
0 commit comments