176821c2e7
The 3020-line docs/DESIGN.md is replaced by the design/ ledger:
design/INDEX.md (sole addressable spine, typed Contracts+Models tables,
polymorphic links — prose file OR authoritative source //!), 14
design/contracts/*.md test-linked invariants + 3 source-link-only
contracts (mangling/env-construction/qualified-xref, no prose file —
code is SoT), 5 design/models/*.md whitepapers, and
docs/journals/2026-05-19-design-decision-records.md (the
relitigation-guard archive — every why/rejected/does-not-do/rollback/
empirical ### moved out at ###-granularity). Clean cut: git rm
docs/DESIGN.md, no stub.
RED-first crates/ailang-core/tests/design_index_pin.rs — the 4-clause
anti-regrowth spine (DESIGN.md-gone / every-INDEX-link-resolves /
every-contract-names-a-resolvable-ratifier /
contracts-carry-no-decision-record-prose) — demonstrably RED before,
GREEN after. Build-atomic by task ordering: design_schema_drift.rs's
include_str! (the only compile-time consumer) retargeted to
design/contracts/data-model.md BEFORE the deletion; its
## Data model/## Pipeline slicer dropped (a simplification the split
enables). 2 NoInstance diagnostics + 2 lockstep E2Es retargeted to
design/contracts/{float-semantics,typeclasses}.md. ~12 agent reading
lists + 5 SKILL bodies + CLAUDE.md + skills/README.md + ~25
code/C/.ail/spec comment xrefs retargeted; OQ7 dangling 'Iter 13b'
cite deleted (no forward target — a pointer would be fiction).
honesty-rule.md rewritten so the rule names the new home
(rationale->journals), resolving the recon-found internal
contradiction; the two docs_honesty_pin.rs:70,72 pinned phrases kept
verbatim+contiguous.
Boss-verified independently: cargo test --workspace 646 passed /
0 failed; design_index_pin 4/4; acceptance grep CLEAN of live
DESIGN.md refs (residuals = only the spec-mandated clause-4
deletion-enforcer). 2 DONE_WITH_CONCERNS routed to the mandatory
milestone-close audit: (a) str-abi.md:23 '(iter str-concat,
2026-05-13)' provenance stamp trips advisory architect_sweeps Sweep-1
— Boss-confirmed byte-identical to DESIGN.md@deeffb1:2062-2065, a
faithfully-migrated PRE-EXISTING anchor (regexes verbatim, only path
retargeted), NOT split-introduced — RATIFY-or-tidy at audit; (b) a
now stale-direction intra-prose 'see Str ABI below' cross-ref in
float-semantics.md — audit-adjudication candidate. Plan defect noted:
Task 9 Step 4's verbatim acceptance grep used a ^./ anchor not
matching the system's grep -rIn output; substance re-verified CLEAN.
Spec grounding-check PASS x2. Journals INDEX + decision-records
pointer appended (Boss-only).
73 lines
3.9 KiB
Markdown
73 lines
3.9 KiB
Markdown
# Pipeline and CLI
|
|
|
|
## Pipeline
|
|
|
|
```
|
|
.ail.json ─┐
|
|
├─ load + validate schema
|
|
├─ resolve names + assign hashes
|
|
├─ desugar (AST → AST)
|
|
├─ typecheck (HM, effect rows; mode-strict per Decision 10)
|
|
├─ lift_letrecs (post-typecheck AST → AST)
|
|
├─ lower to MIR (SSA-like, named SSA values)
|
|
├─ emit LLVM IR (.ll)
|
|
└─ clang -O2 *.ll -o binary
|
|
--alloc=rc → emits inc/dec (@ailang_rc_inc / _dec; canonical, default)
|
|
--alloc=gc → links libgc (@GC_malloc; parity oracle)
|
|
```
|
|
|
|
Two allocator backends share the same MIR. `--alloc=rc` is the
|
|
canonical backend committed to in Decision 10 and the CLI default.
|
|
The typechecker enforces
|
|
`(own)` / `(borrow)` modes, codegen emits `ailang_rc_inc` / `_dec`
|
|
calls at the points dictated by linearity, and `Term::Clone` /
|
|
`Term::ReuseAs` materialise into actual rc-bumps and in-place
|
|
rewrites respectively. `--alloc=gc` selects the transitional Boehm
|
|
backend; `--alloc=rc` is the canonical backend (Decision 10) and the
|
|
CLI default.
|
|
|
|
The **desugar** pass (`ailang-core::desugar::desugar_module`) runs
|
|
before typecheck and codegen in every entry point of `ailang-check`
|
|
and `ailang-codegen`. It is a pure AST → AST rewriter — currently
|
|
only flattens nested constructor patterns, but is the chosen
|
|
home for any future surface-smoothing rewrites that should not bloat
|
|
the core AST or the backends. **Critical invariant:** `CheckedModule.symbols`
|
|
in the `check` entry point continues to hash from the *original*
|
|
on-disk module, not the desugared one, so `ail diff` and `ail manifest`
|
|
report identities that match the canonical JSON the user is editing.
|
|
|
|
The **lift_letrecs** pass (`ailang-check::lift_letrecs`)
|
|
runs **after** typecheck and **before** codegen, but only on the
|
|
`build` / `run` paths — the `check` subcommand stops at typecheck
|
|
and never sees a lifted module. It eliminates every `Term::LetRec`
|
|
that the desugar pass left in place (the case where at least one
|
|
capture is `Term::Let`-bound, so its type is only knowable after
|
|
inference). The output is a module with synthetic `<hint>$lr_N`
|
|
top-level fns appended, ready for codegen. Synthetic FnDefs added
|
|
by this pass do **not** appear in `CheckedModule.symbols` — same
|
|
invariant as the desugar-pass lifts.
|
|
|
|
## CLI
|
|
|
|
```
|
|
ail check <module.ail.json> — loads, validates, typechecks
|
|
ail manifest <module.ail.json> — table: name :: type !effects [hash]
|
|
ail describe <module> <name> — detail of a definition (form-A body)
|
|
ail render <module.ail.json> — JSON-AST → form-A text (exact inverse of `parse`)
|
|
ail parse <module.ail> — form-A text → canonical JSON-AST
|
|
ail prose <module.ail.json> — JSON-AST → form-B (lossy human prose, no parser)
|
|
ail merge-prose <m.ail.json> <m.prose.txt>
|
|
— compose the LLM-mediator prompt for the prose round-trip
|
|
(see docs/PROSE_ROUNDTRIP.md)
|
|
ail deps <module.ail.json> — list cross-module references
|
|
ail diff <a.ail.json> <b.ail.json> — content-addressed def-level diff
|
|
ail workspace <entry.ail.json> — list all modules transitively reachable from entry
|
|
(`--json` for machine output;
|
|
`manifest --workspace` and `diff --workspace`
|
|
extend single-module subcommands to workspaces)
|
|
ail builtins — list built-in fns and effect ops
|
|
ail emit-ir <module> [--emit=staticlib] — writes .ll (staticlib: a main-free kernel's IR, no @main)
|
|
ail build <module> [--emit=staticlib] — full pipeline → binary (staticlib: lib<entry>.a + libailang_rt.a)
|
|
ail run <module> — build + execute (tempdir), passthrough exit code
|
|
```
|