Iter 6: deps hardening, multi-diagnose, DESIGN audit
- ail deps now filters builtins, fn params, and let/match-pattern bindings. New value_names() in ailang-check::builtins is the single source of truth shared with the typechecker install path. walk_term threads a scope set; qualified `prefix.def` refs pass through. - check_in_workspace returns Vec<CheckError>; check_workspace accumulates body diagnostics across defs and modules. Pass-1 (top-level symbol table) and per-module type-def setup stay fail-fast — corrupt env would taint later diagnostics. - DESIGN.md "What the MVP is NOT" was lying (ADTs, strings landed in Iter 2/3). Renamed to "What is not (yet) supported" and split into "not yet" + supported/smoke-tested. JOURNAL Iter 6 entry records the architecture self-check. Tests: 47 green (was 44). +2 deps filter tests in e2e, +1 multi-diagnose test in ailang-check workspace integration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-9
@@ -227,14 +227,32 @@ ail build <module> — full pipeline → binary
|
||||
hash.
|
||||
5. **CI pin** of the outputs in `tests/expected/`.
|
||||
|
||||
## What the MVP is NOT
|
||||
## What is not (yet) supported
|
||||
|
||||
- No ADTs / pattern matching (Phase 2).
|
||||
- No closures / higher-order functions (Phase 2).
|
||||
- No effect handlers (Phase 3).
|
||||
- No refinements / SMT (Phase 4).
|
||||
- No module system beyond imports (Phase 2).
|
||||
- No first-class strings. Only ints + bools + unit.
|
||||
Snapshot of the boundary at the end of Iter 6. Items move out of this list
|
||||
as iterations land; the JOURNAL records the exact iteration.
|
||||
|
||||
The MVP is successful when `examples/sum.ail.json` produces a binary that
|
||||
prints the sum 1..10 = 55.
|
||||
- No closures / higher-order functions. Will require a typed IR stage (TIR)
|
||||
before lowering.
|
||||
- No effect handlers — only the built-in IO and Diverge ops.
|
||||
- No refinements / SMT escalation.
|
||||
- No cross-module ADTs. ADTs are local to a module; ctor names must be
|
||||
unique within their module but may collide across modules.
|
||||
- No visibility rules in imports. Every top-level def of an imported module
|
||||
is reachable; there is no `pub` / `priv`.
|
||||
- No GC. The `Ctor` heap layout leaks. Acceptable for current example
|
||||
programs; required before any longer-running program.
|
||||
|
||||
What **is** supported (and used as the smoke test for the pipeline):
|
||||
|
||||
- Int, Bool, Unit, **Str** as primitive types.
|
||||
- `if`, `let`, function calls, recursion.
|
||||
- Effects on function signatures, with `do op(args)` for direct effect
|
||||
ops (`io/print_int`, `io/print_bool`, `io/print_str`).
|
||||
- **ADTs + flat pattern matching** (Iter 3). Sub-patterns of a Ctor
|
||||
pattern are restricted to `Var` / `Wild`.
|
||||
- **Imports + qualified cross-module references** via dotted names
|
||||
(Iter 5).
|
||||
|
||||
Pipeline regression smoke test: `examples/sum.ail.json` produces a
|
||||
binary that prints 55.
|
||||
|
||||
@@ -345,3 +345,88 @@ Architect agent invoked. Findings:
|
||||
|
||||
Order: 1 first (doc triviality), then 2 before 3 (deps is a tooling-
|
||||
truth fix, multi-diag is a structural extension).
|
||||
|
||||
## 2026-05-07 — Iter 6 done: deps hardening + multi-diagnose + DESIGN audit
|
||||
|
||||
Three things landed together. All small, all KISS — no architecture move,
|
||||
just paying off recorded debt.
|
||||
|
||||
**1. `ail deps` filters builtins, params, and let/match bindings (#22).**
|
||||
|
||||
Before: `sum -> +, -, ==, n, sum` and `ws_lib.add -> ws_lib.+`,
|
||||
`ws_lib.a`, `ws_lib.b`. After: `sum -> sum`, `ws_lib.add -> ws_lib.add`
|
||||
gone (no real deps; only the cross-module call from `ws_main` remains).
|
||||
|
||||
Implementation:
|
||||
- New helper `ailang_check::builtins::value_names()`: derives the
|
||||
Var-level builtin names (`+ - * / % == != < <= > >= not`) from
|
||||
`list()`, so the install-list and the deps-filter share one source of
|
||||
truth.
|
||||
- `walk_term` in `crates/ail/src/main.rs` now threads a `scope` set:
|
||||
fn-params seed it; `Let` adds the bound name for the body only;
|
||||
`Match` arms add their pattern variables (`bind_pattern` helper, MVP
|
||||
rule "ctor sub-patterns are Var/Wild") and roll them back after. Var
|
||||
refs that hit `scope` or `builtins` are dropped; qualified names
|
||||
(`prefix.def`) are passed through unconditionally — the typechecker
|
||||
forbids dots in def names, so no shadowing risk.
|
||||
- Tests added: `deps_filters_builtins_params_locals`,
|
||||
`deps_workspace_filters_builtins_and_params`. The Iter 5d test
|
||||
(`deps_workspace_includes_cross_module`) keeps passing — the only
|
||||
edge it asserted is the legitimate one.
|
||||
|
||||
**2. `check_module` / `check_workspace` are multi-diagnose (#20).**
|
||||
|
||||
`check_in_workspace` returns `Vec<CheckError>` instead of `Result<()>`.
|
||||
Pass-1 (top-level symbol table) stays fail-fast — corrupt globals would
|
||||
taint every later diagnostic. Type-def installation is fail-fast within
|
||||
a module (env corruption) but the outer module loop continues. The
|
||||
body-check loop is the multi-diagnose layer: each def is checked against
|
||||
the assembled env, errors accumulate, the next def is attempted.
|
||||
|
||||
Test: `body_errors_accumulate_across_defs` — one module with two
|
||||
independent body errors (arity mismatch + unknown var) yields two
|
||||
diagnostics with the right `def` field. The legacy single-error `check`
|
||||
keeps working by `.into_iter().next()`-ing the Vec, so internal snapshot
|
||||
tests in `crates/ailang-check/src/lib.rs` are unchanged.
|
||||
|
||||
Out of scope: intra-def collection. A single fn body with three type
|
||||
errors still reports one. The "see all broken defs at once" goal is met;
|
||||
intra-def will require unification deferral and isn't due now.
|
||||
|
||||
**3. DESIGN.md `What the MVP is NOT` audit (#24).**
|
||||
|
||||
The section was lying: it claimed "No ADTs / pattern matching" (delivered
|
||||
Iter 3) and "Only ints + bools + unit" (strings landed Iter 2). Renamed
|
||||
to `What is not (yet) supported`, restructured into "not yet" + "what is
|
||||
supported (smoke-tested)". New invariant: this section is meant to be
|
||||
the truth at the **end of the latest iteration**, not a 2026-05-07-day-0
|
||||
scope statement.
|
||||
|
||||
**Architecture check (the user-asked self-questioning):**
|
||||
|
||||
- *Would I use this language now?* For non-recursive arithmetic + ADT
|
||||
programs over int/bool/str: yes, comfortably. For anything that needs
|
||||
mapping, folding, generic data structures: no, closures are the
|
||||
blocker. That's the next big sprint, not Iter 7.
|
||||
- *Consistency:* DESIGN.md, JOURNAL.md, code, and CLI output now agree
|
||||
on what the language can do. The "What is not (yet) supported" block
|
||||
is the canonical truth surface.
|
||||
- *Visualisation:* `ail deps --workspace --json` is now a clean
|
||||
cross-module call graph (no builtin noise). Good enough for an
|
||||
external graph renderer to consume; a built-in DOT/ASCII renderer is
|
||||
*possible future tooling*, not "we need it now". KISS.
|
||||
- *Documentation:* the agents/ directory is the sub-prompt layer, the
|
||||
JOURNAL is the iteration log, DESIGN.md is the contract. No new doc
|
||||
axes needed at this scale.
|
||||
|
||||
**Tests:** 47 green (previously 44). +2 deps tests in `e2e.rs`, +1
|
||||
multi-diag test in `crates/ailang-check/tests/workspace.rs`.
|
||||
|
||||
**Plan iteration 7:**
|
||||
|
||||
Closures + higher-order functions. This is the big jump that
|
||||
DESIGN.md / Day 0 has been pointing at: it requires a typed IR (TIR)
|
||||
stage, closure conversion in lowering, and a heap-aware ABI. The
|
||||
multi-diag refactor in Iter 6 was scoped intentionally minimal — when
|
||||
TIR lands, intra-def diagnostics become structurally cheap and Task #20
|
||||
gets revisited.
|
||||
|
||||
Reference in New Issue
Block a user