Skip to content

Commit b29a1dd

Browse files
hyperpolymathclaude
andcommitted
docs: tighten SPEC.md codegen-environment section, drop unmerged extern grammar
Review pass on the #89 spec changes against the current main: - 1.2 / 2.1 / 2.10: remove `extern`, `extern_type_decl`, and `extern_fn_decl`. `extern` is not yet a reserved keyword on main and is not parsed by the current lib/parser.mly; the AST has no `TopExternFn` / `TopExternType` variants. Documenting these forms here advertises features that arrive with a separate, unmerged PR (#92, fix/issue-42-extern-parsing). Spec re-introduces these sections when the implementation lands. - 2.9: fix the cross-reference (was "see §5.1" which is Primitive Types; correct target is §8). Rephrase to make clear that the initializer must reduce to a Wasm constant expression in the linear-memory backend. - 8.1: fix `gen_program` -> `generate_module` (the actual fold-over-decls entry point in lib/codegen.ml). Rewrite the section to: * separate the encoding (data layout) from the lookup (instruction selection) so the reader understands what's currently implemented vs. what issue #73 still needs; * call out that `ExprApp (ExprVar _, _)` currently emits `call k` unconditionally, so the sign-test decode path for the negative sentinel is the remaining piece tracked under #73 rather than being described as already implemented; * spell out per-case `gen_decl` behaviour (TopFn registers before generating its body; TopConst registers after appending the global). - 8.2: removed alongside the extern grammar — same rationale as 2.10. The lib/codegen.ml `func_indices` field doc-comment from the original PR was accurate and stays. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3089d71 commit b29a1dd

1 file changed

Lines changed: 46 additions & 38 deletions

File tree

docs/specs/SPEC.md

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ row_var = ".." lower_ident
2525
### 1.2 Keywords
2626

2727
```
28-
fn let const extern mut own ref type struct enum trait impl effect handle
28+
fn let const mut own ref type struct enum trait impl effect handle
2929
resume handler match if else while for return break continue in
3030
true false where total module use pub as unsafe assume transmute
3131
forget Nat Int Bool Float String Type Row
@@ -68,7 +68,7 @@ Special: \ (row restriction)
6868
```ebnf
6969
program = [module_decl] {import_decl} {top_level}
7070
top_level = fn_decl | type_decl | trait_decl | impl_block | effect_decl
71-
| const_decl | extern_type_decl | extern_fn_decl
71+
| const_decl
7272
```
7373

7474
### 2.2 Type Declarations
@@ -201,20 +201,15 @@ impl_block = "impl" [type_params] [trait_ref "for"] type_expr
201201
const_decl = [visibility] "const" LOWER_IDENT ":" type_expr "=" expr ";"
202202
```
203203

204-
Top-level `const` bindings are evaluated at compile time and emitted as
205-
immutable WebAssembly globals. Both function names and const names are
206-
registered in the codegen name environment (see §5.1).
204+
A top-level `const` binding compiles to an immutable WebAssembly global. The
205+
initializer expression must reduce to a Wasm constant expression (a literal
206+
or a constant arithmetic combination thereof); non-constant initializers are
207+
not yet supported by the linear-memory backend.
207208

208-
### 2.10 Extern Declarations
209-
210-
```ebnf
211-
extern_type_decl = "extern" "type" UPPER_IDENT ";"
212-
extern_fn_decl = "extern" "fn" LOWER_IDENT "(" [param_list] ")" ["->" type_expr] ";"
213-
```
214-
215-
`extern type` declares an opaque host-provided type. `extern fn` declares a
216-
function whose implementation is supplied by the host environment at link time
217-
(a WebAssembly import). Both are top-level only and carry no body.
209+
Both function names and const names are registered in the same codegen name
210+
environment so that later top-level declarations may refer to either kind of
211+
binding by name. See §8 (*Codegen Module Environment*) for the encoding and
212+
the current single-pass population order.
218213

219214
## 3. Type System
220215

@@ -554,33 +549,46 @@ contributors; the language semantics are fully specified in §2–4.
554549

555550
### 8.1 Name Environment (`func_indices`)
556551

557-
The codegen context maintains a single association list `func_indices :
558-
(string * int) list` that maps every top-level name visible at call sites to an
552+
The codegen context maintains a single association list
553+
554+
```ocaml
555+
func_indices : (string * int) list
556+
```
557+
558+
that maps every top-level name visible at later declaration sites to an
559559
integer key. Two distinct kinds of binding share this table:
560560

561561
| Source declaration | Key value | Meaning |
562562
|--------------------|-----------|---------|
563-
| `fn f(…) { … }` | `k ≥ 0` | WebAssembly function index (import + defined functions combined) |
564-
| `const C: T = e` | `-(g+1)` where `g` is the global index | Negative sentinel; caller must emit `global.get g` not `call k` |
565-
566-
The negative-index encoding lets call-site code distinguish constants from
567-
functions with a single sign test before emitting the appropriate instruction.
568-
569-
**Population order.** Both `TopFn` and `TopConst` are processed by `gen_decl`
570-
in declaration order (the single pass in `gen_program`). Each inserts its name
571-
into `func_indices` before any later declaration can reference it. Forward
572-
references to functions are therefore not supported in the current
573-
single-pass design.
574-
575-
### 8.2 Extern Bindings
576-
577-
`TopExternFn` declarations are added to the WebAssembly import section and
578-
their names are registered in `func_indices` with the resulting import function
579-
index. `TopExternType` declarations register an opaque type name and generate
580-
no code.
581-
582-
The WebAssembly module name for an `extern fn` import defaults to `"env"` unless
583-
overridden by a `@module("…")` pragma on the declaration (not yet implemented).
563+
| `fn f(…) { … }` | `k ≥ 0` | WebAssembly function index (imports + defined functions, combined) |
564+
| `const C: T = e` | `-(g + 1)`, where `g` is the global's index in the Wasm `globals` vector | Negative sentinel reserved for constants |
565+
566+
Sign-based partitioning is deliberate: `k ≥ 0` decodes directly as a Wasm
567+
`funcidx`, and `k < 0` recovers the global index as `g = -(k + 1)`. A
568+
single integer per name keeps the lookup uniform across both kinds of binding.
569+
570+
**Population.** Top-level declarations are visited in source order by
571+
`gen_decl`, which is folded over `prog.prog_decls` from `generate_module`.
572+
The two relevant cases are:
573+
574+
- `TopFn fd` — registers `(fd.fd_name.name, func_idx)` in `func_indices`
575+
*before* generating the function body, so the body may recursively refer
576+
to its own name.
577+
- `TopConst tc` — generates the global initializer, appends the global to
578+
`ctx.globals`, then registers `(tc.tc_name.name, -(global_idx + 1))` in
579+
`func_indices`.
580+
581+
Because population is strictly single-pass and in declaration order,
582+
forward references (to either functions or constants declared later in the
583+
file) are not supported by the current backend.
584+
585+
**Call-site lookup.** The `ExprApp (ExprVar id, _)` branch of `gen_expr`
586+
consults `func_indices` to translate a direct call into a Wasm `call k`
587+
instruction. Decoding the negative sentinel back to a `global.get`
588+
needed to make a bare `const` identifier usable inside another top-level
589+
declaration's body — is tracked as a known gap in issue #73. The encoding
590+
documented in this section is the data layout the fix relies on; the
591+
call-site decode path will land alongside that fix.
584592

585593
## Appendix: Grammar Reference
586594

0 commit comments

Comments
 (0)