convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
All 176 files in the four accumulating directories now use a zero-padded 4-digit counter prefix that reflects creation order (`NNNN-slug.md`). The counter is assigned per directory in strict git-log creation order; ties broken alphabetically by original name. The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is dropped — the date is recoverable from git log and the counter carries the ordering. A file's counter is stable for the life of the file: never reassigned, never reused, never compacted. Deleted files retire their counter; subsequent files do not fill the gap. This is the property that lets cross-references stay literal — refs use the full filename including the counter (`design/contracts/0007-honesty-rule.md`) so they grep cleanly and resolve directly without a glob step. 313 cross-references updated across .md/.rs/.toml/.c/.json files (test pins, include_str! paths, design-INDEX entries, baseline notes, runtime C comments, inter-contract markdown links incl. bare basename and `../models/foo.md` forms). CLAUDE.md gets a new "File-naming convention" section spelling out the rule and rationale. skills/brainstorm/SKILL.md and skills/planner/SKILL.md updated so new spec/plan creation produces counter-prefixed names from the start. The full test suite (cargo test --workspace) passes.
This commit is contained in:
@@ -18,7 +18,7 @@ the same time, leaving only diagnostic helpers (`type_to_string`,
|
||||
The textual surface is **not** a replacement for the JSON-AST. It is
|
||||
one projection among potentially many. Concretely:
|
||||
|
||||
- The [JSON-AST](data-model.md) keeps its role as the canonical, hashable, content-
|
||||
- The [JSON-AST](0002-data-model.md) keeps its role as the canonical, hashable, content-
|
||||
addressed representation of a module. All hashing, content-
|
||||
addressing, cross-module references, and typecheck/codegen input
|
||||
flow through the JSON-AST. **No new hashable form is introduced.**
|
||||
@@ -55,7 +55,7 @@ the AST and remain projection-agnostic.
|
||||
shape. The full bijection between `.ail.json` and `.ail` (both
|
||||
directions, BLAKE3-stable hashing, Float-bits-hex encoding,
|
||||
workspace-CI enforcement points) is anchored as the top-level
|
||||
[Roundtrip Invariant](roundtrip-invariant.md) — this constraint
|
||||
[Roundtrip Invariant](0009-roundtrip-invariant.md) — this constraint
|
||||
records that the surface-design choice must satisfy that
|
||||
invariant; the invariant itself lives at top level because the
|
||||
property is load-bearing on the language identity, not on this
|
||||
@@ -28,10 +28,10 @@ contract is what makes growing the schema cheap.
|
||||
are real surface forms. Class and type cross-module references
|
||||
(canonical-form rule, qualified `<module>.<Class>` /
|
||||
`<module>.<TypeName>`) follow the scoping rule in
|
||||
[memory model](memory-model.md); the `class`/`instance` schema
|
||||
[memory model](0008-memory-model.md); the `class`/`instance` schema
|
||||
narrative — defaults, superclasses, diagnostics — lives in
|
||||
[typeclasses](typeclasses.md). Exported `fn` defs interact with
|
||||
[embedding ABI](embedding-abi.md).
|
||||
[typeclasses](0013-typeclasses.md). Exported `fn` defs interact with
|
||||
[embedding ABI](0003-embedding-abi.md).
|
||||
|
||||
```jsonc
|
||||
// fn (the unit that gets a content hash)
|
||||
@@ -65,7 +65,7 @@ narrative — defaults, superclasses, diagnostics — lives in
|
||||
"drop-iterative": true // opt-in; omitted when false (hash-stable when omitted)
|
||||
}
|
||||
|
||||
// class (typeclass declaration; narrative in contracts/typeclasses.md)
|
||||
// class (typeclass declaration; narrative in contracts/0013-typeclasses.md)
|
||||
{ "kind": "class",
|
||||
"name": "<id>", // class name (e.g. "Show")
|
||||
"param": "<id>", // single class parameter, kind *
|
||||
@@ -80,7 +80,7 @@ narrative — defaults, superclasses, diagnostics — lives in
|
||||
"doc": "<optional string>"
|
||||
}
|
||||
|
||||
// instance (typeclass instance; narrative in contracts/typeclasses.md)
|
||||
// instance (typeclass instance; narrative in contracts/0013-typeclasses.md)
|
||||
{ "kind": "instance",
|
||||
"class": "<id>", // class being instantiated; canonical form (bare for same-module, "<module>.<Class>" for cross-module)
|
||||
"type": Type, // concrete type expression (never the class param)
|
||||
@@ -165,7 +165,7 @@ narrative — defaults, superclasses, diagnostics — lives in
|
||||
// non-`recur` branch. Strictly additive (no `skip_serializing_if`;
|
||||
// pre-existing fixtures hash bit-identically — none carry the tag).
|
||||
// No totality claim — an infinite loop is legal. See
|
||||
// `docs/specs/2026-05-17-loop-recur.md`.
|
||||
// `docs/specs/0034-loop-recur.md`.
|
||||
{ "t": "loop",
|
||||
"binders": [ { "name": "<id>", "type": Type, "init": Term }, ... ],
|
||||
"body": Term }
|
||||
@@ -179,7 +179,7 @@ narrative — defaults, superclasses, diagnostics — lives in
|
||||
```
|
||||
|
||||
In the MVP, `do` is only a direct call to a built-in effect op (no
|
||||
handler); the effect system is described in [effects](../models/effects.md).
|
||||
handler); the effect system is described in [effects](../models/0002-effects.md).
|
||||
A `lam` term constructs an anonymous function value; free
|
||||
variables of its body are captured from the enclosing scope.
|
||||
|
||||
@@ -188,7 +188,7 @@ ordinary local scope plus a positional `loop_stack`, and codegen
|
||||
lowers them as entry-block allocas. Capturing a `loop` binder into a
|
||||
lambda body is rejected at typecheck via
|
||||
`CheckError::LoopBinderCapturedByLambda`. See
|
||||
`docs/specs/2026-05-17-loop-recur.md`.
|
||||
`docs/specs/0034-loop-recur.md`.
|
||||
|
||||
**`Literal`**:
|
||||
|
||||
@@ -215,7 +215,7 @@ Patterns are linear: each pattern variable may appear at most once.
|
||||
|
||||
The `Type::Con.name` canonical-form rule (bare for same-module /
|
||||
primitives, qualified `<module>.<TypeName>` for cross-module) lives
|
||||
in [memory model](memory-model.md); `Type::Fn`'s parameter-mode
|
||||
in [memory model](0008-memory-model.md); `Type::Fn`'s parameter-mode
|
||||
metadata is defined and gated there as well.
|
||||
|
||||
```jsonc
|
||||
@@ -228,7 +228,7 @@ metadata is defined and gated there as well.
|
||||
// in the typechecker (unify, occurs, apply) keeps working.
|
||||
// `paramModes` omitted when every entry is "implicit"; `retMode`
|
||||
// omitted when "implicit" (hash-stable when omitted). Full mode
|
||||
// contract lives in contracts/memory-model.md.
|
||||
// contract lives in contracts/0008-memory-model.md.
|
||||
{ "k": "fn",
|
||||
"params": [Type...],
|
||||
"paramModes": [ParamMode...],
|
||||
@@ -239,7 +239,7 @@ metadata is defined and gated there as well.
|
||||
{ "k": "var", "name": "<id>" }
|
||||
|
||||
// Top-level polymorphism only. `constraints` carries class
|
||||
// constraints (narrative in contracts/typeclasses.md); omitted when
|
||||
// constraints (narrative in contracts/0013-typeclasses.md); omitted when
|
||||
// empty (hash-stable when omitted).
|
||||
{ "k": "forall",
|
||||
"vars": ["<id>"...],
|
||||
@@ -248,7 +248,7 @@ metadata is defined and gated there as well.
|
||||
```
|
||||
|
||||
**`ParamMode`** (full contract in
|
||||
[memory model](memory-model.md)):
|
||||
[memory model](0008-memory-model.md)):
|
||||
|
||||
```
|
||||
"implicit" — unannotated / back-compat. Treated as `own` by the typechecker.
|
||||
@@ -260,8 +260,8 @@ metadata is defined and gated there as well.
|
||||
unannotated fixtures continue to serialize without the mode wrapper and keep their
|
||||
canonical-JSON hash. The full mode contract (codegen consequences,
|
||||
the over-strict-mode lint, the `Suppress` mechanism) lives in
|
||||
[memory model](memory-model.md); the four language-design
|
||||
[memory model](0008-memory-model.md); the four language-design
|
||||
preconditions that make RC sound live in
|
||||
[language constraints](language-constraints.md).
|
||||
[language constraints](0015-language-constraints.md).
|
||||
|
||||
Ratified by: `crates/ailang-core/tests/design_schema_drift.rs`.
|
||||
@@ -44,12 +44,12 @@ The staticlib swarm artefact is **RC-only**:
|
||||
stub is leak-only and not swarm-safe; `--alloc=gc` no longer
|
||||
exists as a CLI value — see the Boehm-retirement iter). The
|
||||
value/record
|
||||
layout is **frozen as of M3** (see [Frozen value layout](frozen-value-layout.md)); the
|
||||
layout is **frozen as of M3** (see [Frozen value layout](0006-frozen-value-layout.md)); the
|
||||
ctx-threaded C signature is the M2 shape.
|
||||
|
||||
Export parameters are written **bare**: a scalar type carries no
|
||||
`own`/`borrow` mode (a single-constructor record export parameter,
|
||||
by contrast, carries `own`/`borrow` — see [memory model](memory-model.md)
|
||||
by contrast, carries `own`/`borrow` — see [memory model](0008-memory-model.md)
|
||||
for the full ownership contract; the
|
||||
frozen value layout below specifies how that contract lands at the C ABI). The
|
||||
canonical M1 export shape:
|
||||
+2
-2
@@ -30,7 +30,7 @@ A proposed feature ships only if all three hold:
|
||||
question is whether the construct re-opens a class of mistakes
|
||||
(iterated-mutable-state reasoning, silently-unbounded recursion,
|
||||
reference cycles) that a foundational invariant — see
|
||||
[language constraints](language-constraints.md) — closes. If it does,
|
||||
[language constraints](0015-language-constraints.md) — closes. If it does,
|
||||
it is cut even when 1 and 2 hold — *or* it must be reshaped until
|
||||
the bug class is structurally impossible rather than merely
|
||||
discouraged. A documentation note is not a reshape; the
|
||||
@@ -44,7 +44,7 @@ A proposed feature ships only if all three hold:
|
||||
built without a documented-unenforced precondition*, pass all
|
||||
three — and the fact that the 2026-05 attempt could not (it
|
||||
forced a silent-divergence precondition; see
|
||||
`docs/specs/2026-05-16-iteration-discipline-revert.md`) is itself
|
||||
`docs/specs/0032-iteration-discipline-revert.md`) is itself
|
||||
the bug-class-reintroduction discriminator working as intended.
|
||||
|
||||
This is the positive complement to the CLAUDE.md rule that
|
||||
@@ -12,7 +12,7 @@ no `f32` variant. The runtime / codegen contract:
|
||||
fmul / fdiv double`, `fneg double`. Float comparison goes through
|
||||
the named prelude fns `float_eq` / `float_ne` / `float_lt` /
|
||||
`float_le` / `float_gt` / `float_ge` (Float has no Eq/Ord instance
|
||||
— see [Prelude (built-in) classes](prelude-classes.md)); each fn
|
||||
— see [Prelude (built-in) classes](0017-prelude-classes.md)); each fn
|
||||
lowers to a single `fcmp` instruction (`oeq` / `une` / `olt` /
|
||||
`ole` / `ogt` / `oge` respectively) via the codegen intercept
|
||||
`try_emit_primitive_instance_body`, with the `alwaysinline`
|
||||
@@ -73,7 +73,7 @@ no `f32` variant. The runtime / codegen contract:
|
||||
|
||||
The same libc-`%g` rendering applies to `show 1.5` / `show nan` /
|
||||
`show inf` via `instance Show Float` (which calls `float_to_str`
|
||||
internally — see [Prelude (built-in) classes](prelude-classes.md) for the Show ship).
|
||||
internally — see [Prelude (built-in) classes](0017-prelude-classes.md) for the Show ship).
|
||||
The NaN-spelling caveat above is observable via `do print x` for
|
||||
Float-typed `x`; the rendering is libc-version-dependent and
|
||||
target-libc-specific. AILang does NOT canonicalise Float textual
|
||||
@@ -91,15 +91,15 @@ use case appears.
|
||||
**Form-A serialisation:** Float literals carry the IEEE-754
|
||||
bit pattern as a 16-character lowercase hex string in the canonical
|
||||
JSON: `{"kind":"float","bits":"<16-hex>"}` (see
|
||||
[Data model](data-model.md) for the literal schema). Routing through the
|
||||
[Data model](0002-data-model.md) for the literal schema). Routing through the
|
||||
JSON *string* path (not `serde_json::Number`) preserves bit
|
||||
stability across `serde_json` versions and lets NaN / ±Inf
|
||||
round-trip through Form-A — JSON numbers cannot represent them.
|
||||
This bits-hex encoding is what keeps Floats inside the
|
||||
[Roundtrip Invariant](roundtrip-invariant.md).
|
||||
[Roundtrip Invariant](0009-roundtrip-invariant.md).
|
||||
|
||||
**Pattern matching:** `Pattern::Lit` on `Literal::Float` (see
|
||||
[Data model](data-model.md) for the pattern schema) is hard-
|
||||
[Data model](0002-data-model.md) for the pattern schema) is hard-
|
||||
rejected at typecheck (`CheckError::FloatPatternNotAllowed`). IEEE
|
||||
semantics make Float patterns semantically dubious — NaN never
|
||||
matches via `float_eq` (its `fcmp oeq` lowering is `false` for
|
||||
@@ -109,7 +109,7 @@ Use the explicit comparison fns (`float_lt`, `float_gt`, ...) and
|
||||
|
||||
`float_to_str` (Float → Str) and `int_to_str` (Int → Str) are
|
||||
fully wired through checker, codegen, and runtime. Both allocate
|
||||
a fresh heap-Str slab at the call site (see [Str ABI](str-abi.md)
|
||||
a fresh heap-Str slab at the call site (see [Str ABI](0011-str-abi.md)
|
||||
for the dual realisation) and carry `ret_mode: Own` so the let-binder
|
||||
for the call result is RC-tracked and the slab is freed at scope
|
||||
close.
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
For a single-constructor `data T` whose `n` fields are each `Int`
|
||||
or `Float`, a value of `T` crossing the
|
||||
[embedding C boundary](embedding-abi.md) is a
|
||||
[embedding C boundary](0003-embedding-abi.md) is a
|
||||
bare payload pointer `p`:
|
||||
|
||||
| bytes | content |
|
||||
@@ -29,7 +29,7 @@ offsets above. A raw `malloc` is a contract violation:
|
||||
header at `p - 8` initialised to `1`.
|
||||
|
||||
**Ownership follows the declared mode** (the
|
||||
[Mode metadata is load-bearing for codegen](memory-model.md)
|
||||
[Mode metadata is load-bearing for codegen](0008-memory-model.md)
|
||||
contract, as the C ABI): a `(own (con
|
||||
T))` parameter transfers ownership in — the kernel consumes it
|
||||
(Iter-B drop-at-return); the host MUST NOT touch or `dec` it after
|
||||
@@ -3,16 +3,16 @@
|
||||
The four language-design constraints that make RC sound without a
|
||||
cycle-collector backstop (strict evaluation, no recursive value
|
||||
bindings, no shared mutable refs, acyclic ADTs) live in
|
||||
[language constraints](language-constraints.md); this file covers
|
||||
[language constraints](0015-language-constraints.md); this file covers
|
||||
the schema additions, advisory diagnostics, and codegen contract
|
||||
that build on them.
|
||||
|
||||
## Schema additions
|
||||
|
||||
**Parameter modes on `Type::Fn`** (see [Data model](data-model.md)
|
||||
**Parameter modes on `Type::Fn`** (see [Data model](0002-data-model.md)
|
||||
for the schema-level definition of `Type::Fn`).
|
||||
|
||||
The form-A surface (see [authoring surface](authoring-surface.md))
|
||||
The form-A surface (see [authoring surface](0001-authoring-surface.md))
|
||||
for fn signatures gains mode wrappers:
|
||||
|
||||
```
|
||||
@@ -22,7 +22,7 @@ for fn signatures gains mode wrappers:
|
||||
|
||||
Internally, this is *not* a new `Type` variant. Modes are
|
||||
metadata on `Type::Fn` — `paramModes` and `retMode` fields run
|
||||
parallel to `params` and `ret` (see [Data model](data-model.md) for the JSON
|
||||
parallel to `params` and `ret` (see [Data model](0002-data-model.md) for the JSON
|
||||
schema). The substantive reasons for per-position metadata over a
|
||||
`Type::Borrow` / `Type::Own` variant approach:
|
||||
|
||||
@@ -33,7 +33,7 @@ schema). The substantive reasons for per-position metadata over a
|
||||
`(con List (borrow Int))` — syntactically possible, semantically
|
||||
meaningless (you cannot separately own/borrow a list element
|
||||
from the list it lives in). The
|
||||
[canonical-schema principle](data-model.md) is "schema = data,
|
||||
[canonical-schema principle](0002-data-model.md) is "schema = data,
|
||||
schema permits exactly what is meaningful"; per-position
|
||||
metadata is the option that holds that line.
|
||||
- **Compositional clarity.** A `Type` value's identity should
|
||||
@@ -85,7 +85,7 @@ Three schema fields carry class references in this form:
|
||||
`TypeDef.name`).
|
||||
|
||||
Method dispatch is type-driven (see
|
||||
[Method dispatch](method-dispatch.md)):
|
||||
[Method dispatch](0016-method-dispatch.md)):
|
||||
synth resolves a `Term::Var { name: "show" }` by consulting
|
||||
the workspace's method-to-candidate-class index, filtering by
|
||||
argument type (concrete) or by declared constraint (rigid-var), and
|
||||
@@ -150,7 +150,7 @@ until the fixture intentionally adopts the feature.
|
||||
**`FnDef.suppress`.** The `suppress` field on `FnDef` carries a
|
||||
list of advisory-diagnostic suppress entries; each entry has a
|
||||
`code` (the diagnostic being suppressed) and a `because` (a
|
||||
mandatory non-empty reason). See [Data model](data-model.md) for
|
||||
mandatory non-empty reason). See [Data model](0002-data-model.md) for
|
||||
the canonical schema.
|
||||
|
||||
Form-A surface: `(suppress (code "...") (because "..."))` clause
|
||||
+4
-4
@@ -4,8 +4,8 @@
|
||||
|
||||
Every well-formed AILang module has a canonical `.ail.json`
|
||||
representation (the hashable, content-addressed
|
||||
[JSON-AST](data-model.md)) and a hand-authored `.ail` representation
|
||||
(Form A, the [authoring projection](authoring-surface.md)). The
|
||||
[JSON-AST](0002-data-model.md)) and a hand-authored `.ail` representation
|
||||
(Form A, the [authoring projection](0001-authoring-surface.md)). The
|
||||
JSON-AST is derived from `.ail` in-process by consumers that need
|
||||
it; the round-trip is now the *property* of that derivation, not
|
||||
the byte-level agreement of two on-disk forms.
|
||||
@@ -47,7 +47,7 @@ approximation. The canonical encoding is
|
||||
`{"kind":"float","bits":"<16-lowercase-hex>"}` and the surface
|
||||
emits the same bits-hex string. NaN, ±Inf, signed zero, and subnormals all
|
||||
round-trip exactly because the JSON-number path is bypassed (see
|
||||
[Float semantics](float-semantics.md), "Form-A serialisation").
|
||||
[Float semantics](0005-float-semantics.md), "Form-A serialisation").
|
||||
Floats are not an exception to the invariant — the bits-hex
|
||||
encoding is the mechanism that keeps them *inside* it.
|
||||
|
||||
@@ -85,7 +85,7 @@ never by relaxing the test.
|
||||
### Why this is anchored at top level
|
||||
|
||||
The invariant is a property of the language identity, not of any
|
||||
one surface-design choice. The [authoring surface](authoring-surface.md)
|
||||
one surface-design choice. The [authoring surface](0001-authoring-surface.md)
|
||||
introduces the `.ail` surface and lists round-trip-as-property as
|
||||
one of its constraints, but the property is load-bearing for every
|
||||
downstream concern that treats the two forms as exchangeable:
|
||||
@@ -5,12 +5,12 @@
|
||||
Snapshot of the current boundary.
|
||||
|
||||
- No effect handlers — only the built-in `IO` op (`io/print_str`);
|
||||
the effect system is described in [effects](../models/effects.md).
|
||||
the effect system is described in [effects](../models/0002-effects.md).
|
||||
`Diverge` is a reserved effect name with no op and no codegen.
|
||||
- No refinements / SMT escalation.
|
||||
- No HM inference inside bodies. Top-level def types are explicit;
|
||||
polymorphism is opt-in via `Type::Forall { vars, body }` (see
|
||||
[Data model](data-model.md)). Inside a body, lambdas check
|
||||
[Data model](0002-data-model.md)). Inside a body, lambdas check
|
||||
monomorphically against their declared type.
|
||||
- Polymorphic fns must be **directly called** at the use site.
|
||||
Passing a polymorphic fn as a value (`let f = id in f(42)`) is
|
||||
@@ -20,11 +20,11 @@ Snapshot of the current boundary.
|
||||
- No recursive `let` for non-fn values. Plain `let x = … in …` only
|
||||
sees `x` inside the body, not inside its own RHS — recursive value
|
||||
bindings would break the
|
||||
[acyclicity invariant](language-constraints.md). Recursive
|
||||
[acyclicity invariant](0015-language-constraints.md). Recursive
|
||||
*fn* bindings are supported via `Term::LetRec` (`{ "t": "letrec",
|
||||
... }`); the desugar pass lifts most occurrences to a synthetic
|
||||
top-level fn, with `lift_letrecs` finishing the residue after
|
||||
typecheck (see [pipeline](../models/pipeline.md)).
|
||||
typecheck (see [pipeline](../models/0003-pipeline.md)).
|
||||
- No visibility rules in imports. Every top-level def of an imported module
|
||||
is reachable; there is no `pub` / `priv`.
|
||||
|
||||
@@ -34,7 +34,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
- `if`, `let`, function calls, recursion.
|
||||
- Effects on function signatures, with `do op(args)` for direct effect
|
||||
ops (`io/print_str`). The polymorphic `print` (see
|
||||
[prelude classes](prelude-classes.md)) is the canonical output
|
||||
[prelude classes](0017-prelude-classes.md)) is the canonical output
|
||||
path for non-Str values.
|
||||
- **Builtins.** Arithmetic operators (`+`, `-`, `*`, `/`) of type
|
||||
`forall a. (a, a) -> a` (codegen-restricted to `{Int, Float}`);
|
||||
@@ -47,7 +47,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
`float_to_int_truncate : (Float) -> Int` (saturating, NaN → 0),
|
||||
`float_to_str : (Float) -> Str`,
|
||||
`int_to_str : (Int) -> Str` (both allocate a heap-Str slab at
|
||||
call time and return it with `ret_mode: Own`; see [Str ABI](str-abi.md) for
|
||||
call time and return it with `ret_mode: Own`; see [Str ABI](0011-str-abi.md) for
|
||||
the dual heap-/static-Str realisation); inspection
|
||||
`is_nan : (Float) -> Bool` (LLVM
|
||||
`fcmp uno`); Float bit-pattern constants `nan : Float`,
|
||||
@@ -69,7 +69,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
ctor; every primitive instance body carries the
|
||||
`alwaysinline` attribute so the call folds at every use site.
|
||||
Float has no Eq / Ord instance (see
|
||||
[Float semantics](float-semantics.md)); explicit comparison
|
||||
[Float semantics](0005-float-semantics.md)); explicit comparison
|
||||
is via the named fns `float_eq` / `float_ne` / `float_lt` /
|
||||
`float_le` / `float_gt` / `float_ge`, each lowering to a
|
||||
single `fcmp` with the matching predicate. `float_ne` uses
|
||||
@@ -92,7 +92,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
`Pattern::Lit` (top-level or sub-) to a `Term::If` on `eq` (the
|
||||
class method `prelude.Eq.eq`) before typecheck/codegen — see
|
||||
[desugar](../../crates/ailang-core/src/desugar.rs) and
|
||||
[Pipeline](../models/pipeline.md).
|
||||
[Pipeline](../models/0003-pipeline.md).
|
||||
- Literal patterns at top level and inside Ctor sub-patterns (via desugar).
|
||||
`(pat-lit 0)` and `(pat-ctor Cons (pat-lit 0) _)` both
|
||||
parse and lower; the rewrite is to `Term::If { cond = (eq sv lit) }`,
|
||||
@@ -100,7 +100,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
primitive Eq instance is authorable. The primitive instances
|
||||
cover `Int` / `Bool` / `Str` / `Unit`; `Float` lit-patterns are
|
||||
hard-rejected at typecheck (`CheckError::FloatPatternNotAllowed`,
|
||||
see [float-semantics](float-semantics.md)). `(pat-lit "hi")` over
|
||||
see [float-semantics](0005-float-semantics.md)). `(pat-lit "hi")` over
|
||||
a `Str` scrutinee is exercised by `examples/eq_demo.ail.json`.
|
||||
- **Imports + qualified cross-module references** via dotted names.
|
||||
Extends to **types and constructors**: a foreign
|
||||
@@ -109,7 +109,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
inside that scrutinee. Std-library demos (`examples/std_*_demo.ail.json`)
|
||||
exercise this end-to-end.
|
||||
- **AI-authoring text surface, form (A)** (see
|
||||
[authoring surface](authoring-surface.md)). The `ailang-surface`
|
||||
[authoring surface](0001-authoring-surface.md)). The `ailang-surface`
|
||||
crate parses `.ail` form-A text into a canonical
|
||||
`ailang-core::ast::Module` and prints any module back as form-A
|
||||
text. `ail render` and `ail describe` use it as the
|
||||
@@ -117,7 +117,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
identity (text → AST → JSON → AST → text) is gated by
|
||||
`ailang-surface/tests/round_trip.rs` over every shipped fixture.
|
||||
- **Memory management via reference counting + uniqueness inference**
|
||||
(see [RC + uniqueness](../models/rc-uniqueness.md)), with
|
||||
(see [RC + uniqueness](../models/0004-rc-uniqueness.md)), with
|
||||
**per-fn arena via stack `alloca` for non-escaping allocations**
|
||||
layered on top. Every ADT box, lambda env, and closure pair
|
||||
allocates either via `@ailang_rc_alloc` (escaping; RC-managed
|
||||
@@ -125,7 +125,7 @@ What **is** supported (and used as the smoke test for the pipeline):
|
||||
`alloca` (non-escaping; freed at fn return). The decision is
|
||||
made by an escape-analysis pre-pass over the fn body — see the
|
||||
"Per-fn arena via stack `alloca`" subsection of
|
||||
[RC + uniqueness](../models/rc-uniqueness.md). The per-fn-arena
|
||||
[RC + uniqueness](../models/0004-rc-uniqueness.md). The per-fn-arena
|
||||
path is exercised end-to-end by
|
||||
`examples/escape_local_demo.ail.json`.
|
||||
- **First-class function references.** A top-level fn name (or
|
||||
@@ -13,14 +13,14 @@ and backed by a `runtime/str.c` C helper.
|
||||
|
||||
- `int_to_str : (borrow Int) -> Str` — decimal rendering
|
||||
of an `Int`. Backs `Show Int` in the
|
||||
[prelude](prelude-classes.md).
|
||||
[prelude](0017-prelude-classes.md).
|
||||
- `bool_to_str : (borrow Bool) -> Str` — `"true"`/`"false"`.
|
||||
Backs `Show Bool` in the [prelude](prelude-classes.md).
|
||||
Backs `Show Bool` in the [prelude](0017-prelude-classes.md).
|
||||
- `float_to_str : (borrow Float) -> Str` —
|
||||
type-installed; codegen is reserved and not yet shipped.
|
||||
- `str_clone : (borrow Str) -> Str` — allocates a fresh
|
||||
heap-Str copy of the input's bytes. Backs `Show Str` in the
|
||||
[prelude](prelude-classes.md).
|
||||
[prelude](0017-prelude-classes.md).
|
||||
- `str_concat : (borrow Str, borrow Str) -> Str` — combines two `Str`
|
||||
values into a single owned `Str`.
|
||||
General-purpose; commonly used in Show bodies for labelled output
|
||||
@@ -34,7 +34,7 @@ instance body that wants to combine fragments.
|
||||
|
||||
Primitive output for `Str` values goes through `io/print_str`
|
||||
directly; values of other primitive types route through the
|
||||
polymorphic `print` helper (see [prelude classes](prelude-classes.md)),
|
||||
polymorphic `print` helper (see [prelude classes](0017-prelude-classes.md)),
|
||||
which feeds the heap-Str result of `show x` into `io/print_str`.
|
||||
|
||||
Equality on `Str` dispatches via `prelude.Eq.eq` (Str instance,
|
||||
@@ -70,7 +70,7 @@ implicit trailing newline (Gitea #29; the earlier `@puts` lowering
|
||||
appended a newline per call).
|
||||
|
||||
The heap-Str realisation participates in standard RC (see
|
||||
[memory model](memory-model.md)): the
|
||||
[memory model](0008-memory-model.md)): the
|
||||
`rc_header` slot eight bytes before the `len` field is managed
|
||||
by `ailang_rc_alloc` / `ailang_rc_inc` / `ailang_rc_dec` exactly
|
||||
like any other RC-allocated cell. The static-Str realisation has
|
||||
@@ -13,7 +13,7 @@ Solution: explicit, verified tail calls.
|
||||
|
||||
- **AST.** `Term::App { fn, args, tail: bool }` and
|
||||
`Term::Do { op, args, tail: bool }` (see
|
||||
[Data model](data-model.md)) gain a `tail` flag,
|
||||
[Data model](0002-data-model.md)) gain a `tail` flag,
|
||||
serde-defaulting to `false` so existing fixtures load with
|
||||
`tail: false` and their hashes stay bit-identical.
|
||||
- **Typecheck.** A new pass `verify_tail_positions(fn_body)`
|
||||
@@ -39,7 +39,7 @@ Solution: explicit, verified tail calls.
|
||||
`tail: true` set on the resulting term. EBNF gains 2 lines;
|
||||
total production count goes from ~28 to ~30, still inside
|
||||
the constraint-1 budget (see
|
||||
[authoring surface](authoring-surface.md)).
|
||||
[authoring surface](0001-authoring-surface.md)).
|
||||
|
||||
**What this does NOT promise.** Per the tail-call survey of existing fixtures,
|
||||
many existing recursive calls are *not* in tail position
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
The schema, registry, and diagnostic contract for the typeclass
|
||||
layer. The resolution/monomorphisation whitepaper lives at
|
||||
[models/typeclasses](../models/typeclasses.md); the call-site
|
||||
lookup rule at [method dispatch](method-dispatch.md); the built-in
|
||||
classes the prelude ships at [prelude classes](prelude-classes.md).
|
||||
[models/typeclasses](../models/0005-typeclasses.md); the call-site
|
||||
lookup rule at [method dispatch](0016-method-dispatch.md); the built-in
|
||||
classes the prelude ships at [prelude classes](0017-prelude-classes.md).
|
||||
|
||||
## Form-A schema (the JSON authoring surface)
|
||||
|
||||
Three additive schema extensions; no existing module becomes invalid.
|
||||
The canonical schema for `ClassDef`, `InstanceDef`, and the
|
||||
`FnDef.type` constraints field lives in
|
||||
[Data model](data-model.md); this section is the typeclass-layer
|
||||
[Data model](0002-data-model.md); this section is the typeclass-layer
|
||||
narrative.
|
||||
|
||||
**`ClassDef`** — top-level definition kind, declares a class:
|
||||
@@ -75,7 +75,7 @@ exactly as for any free function.
|
||||
## Cross-module references in synthesised bodies
|
||||
|
||||
The unified mono pass (see
|
||||
[models/typeclasses](../models/typeclasses.md) for the
|
||||
[models/typeclasses](../models/0005-typeclasses.md) for the
|
||||
resolution/monomorphisation algorithm) synthesises mono symbols for
|
||||
polymorphic free fns and class-method instances in the symbol's
|
||||
owner module — e.g. `print__Int` lives in `prelude` (because `print`
|
||||
@@ -84,7 +84,7 @@ is defined in the prelude), but a user-ADT call site
|
||||
whose body references `show_user_adt.show__IntBox` (the user
|
||||
instance lives in the user-defining module per the
|
||||
orphan-free-coherence rule documented in
|
||||
[models/typeclasses](../models/typeclasses.md)). The synthesised
|
||||
[models/typeclasses](../models/0005-typeclasses.md)). The synthesised
|
||||
body crosses a module boundary the source template did not.
|
||||
|
||||
Three invariants make this work:
|
||||
@@ -218,7 +218,7 @@ wording is fixed; the categories and their triggers are:
|
||||
Cross-class method sharing is structurally legal; ambiguity surfaces
|
||||
at the call site via `AmbiguousMethodResolution` or, for class-fn
|
||||
name overlap, via the `class-method-shadowed-by-fn` warning. See
|
||||
[Method dispatch](method-dispatch.md).
|
||||
[Method dispatch](0016-method-dispatch.md).
|
||||
|
||||
**Class-schema diagnostics** (validation of class declarations):
|
||||
|
||||
@@ -259,6 +259,6 @@ There is no `AmbiguousInstance` diagnostic at the registry level —
|
||||
coherence (`DuplicateInstance` at registry build, W2) makes
|
||||
per-`(class, type)` ambiguity structurally impossible. Cross-class
|
||||
method ambiguity is a separate concern resolved at the call site
|
||||
via `AmbiguousMethodResolution` (see [Method dispatch](method-dispatch.md)).
|
||||
via `AmbiguousMethodResolution` (see [Method dispatch](0016-method-dispatch.md)).
|
||||
|
||||
Ratified by: `crates/ail/tests/show_no_instance_e2e.rs`.
|
||||
@@ -5,7 +5,7 @@
|
||||
1. **Snapshot tests** for the pretty-printer and IR emit. The diff makes
|
||||
regressions visible immediately.
|
||||
2. **Property tests** for the JSON ↔ pretty-print
|
||||
[roundtrip](roundtrip-invariant.md).
|
||||
[roundtrip](0009-roundtrip-invariant.md).
|
||||
3. **End-to-end tests** for `examples/` with expected program output.
|
||||
4. **Hash stability**: a test ensures the same def always produces the same
|
||||
hash.
|
||||
+4
-4
@@ -5,11 +5,11 @@
|
||||
The four constraints below are necessary preconditions for RC to
|
||||
be sound and complete *without* a cycle-collector backstop. They
|
||||
are not new — AILang already satisfies all four — but the
|
||||
[memory model](memory-model.md) makes them load-bearing rather
|
||||
[memory model](0008-memory-model.md) makes them load-bearing rather
|
||||
than incidental:
|
||||
|
||||
1. **Strict evaluation.** Every `Term::App` (see
|
||||
[Data model](data-model.md)) argument is fully
|
||||
[Data model](0002-data-model.md)) argument is fully
|
||||
evaluated before the call. No laziness, no thunks. (Already
|
||||
true.)
|
||||
2. **No recursive value bindings.** `(let x EXPR ...)` evaluates
|
||||
@@ -31,11 +31,11 @@ proposal proves the cycle is collectible by an extension (e.g.
|
||||
linear ownership).
|
||||
|
||||
These constraints are the precondition for the
|
||||
[memory model](memory-model.md): a sound RC implementation without
|
||||
[memory model](0008-memory-model.md): a sound RC implementation without
|
||||
a cycle-collector backstop requires the reference graph to be a
|
||||
DAG, which constraint 4 establishes from 1–3. They are also the
|
||||
load-bearing structural reason behind several rejections under the
|
||||
[feature-acceptance](feature-acceptance.md) bug-class-reintroduction
|
||||
[feature-acceptance](0004-feature-acceptance.md) bug-class-reintroduction
|
||||
discriminator (notably iterated-mutable-state reasoning).
|
||||
|
||||
Ratified by: `crates/ailang-check/src/uniqueness.rs` (in-source mod tests).
|
||||
@@ -3,8 +3,8 @@
|
||||
## Method dispatch
|
||||
|
||||
The class-method resolution rule that pairs with the
|
||||
[typeclasses](typeclasses.md) contract; the `Show`/`Eq`/`Ord` instances
|
||||
the rule dispatches against live in [prelude classes](prelude-classes.md).
|
||||
[typeclasses](0013-typeclasses.md) contract; the `Show`/`Eq`/`Ord` instances
|
||||
the rule dispatches against live in [prelude classes](0017-prelude-classes.md).
|
||||
|
||||
Dispatch is two-mode:
|
||||
|
||||
@@ -12,7 +12,7 @@ Dispatch is two-mode:
|
||||
constraint set): the constraint names the class via the qualified
|
||||
`Constraint.class` field (canonical form: bare for same-module,
|
||||
`<module>.<Class>` for cross-module; see
|
||||
[memory model](memory-model.md) for the canonical-form rule).
|
||||
[memory model](0008-memory-model.md) for the canonical-form rule).
|
||||
Synth's residual carries the class name directly;
|
||||
constraint-discharge at fn-body-end matches against the workspace
|
||||
registry by `(class, type_hash)` key.
|
||||
@@ -3,7 +3,7 @@
|
||||
## Prelude (built-in) classes
|
||||
|
||||
The prelude ships the `Ordering` ADT, the `Eq` and `Ord`
|
||||
[classes](typeclasses.md), primitive `Eq Int/Bool/Str/Unit` and
|
||||
[classes](0013-typeclasses.md), primitive `Eq Int/Bool/Str/Unit` and
|
||||
`Ord Int/Bool/Str` instances, and the five polymorphic free-fn
|
||||
helpers `ne`/`lt`/`le`/`gt`/`ge`. The primitive `Eq` / `Ord`
|
||||
instance bodies are placeholder lambdas in `examples/prelude.ail`;
|
||||
@@ -35,12 +35,12 @@ maintenance; the symmetric extensions are mechanical when the
|
||||
first such workload appears.
|
||||
|
||||
Float has neither `Eq` nor `Ord` instance per [Float semantics](
|
||||
float-semantics.md); a polymorphic helper invoked at Float fires
|
||||
0005-float-semantics.md); a polymorphic helper invoked at Float fires
|
||||
`NoInstance` at typecheck with a Float-aware diagnostic that names
|
||||
the explicit `float_eq` / `float_lt` alternatives and
|
||||
cross-references this section. The lookup machinery that turns a
|
||||
`show x` call site into the right monomorphic instance is
|
||||
documented in [method dispatch](method-dispatch.md).
|
||||
documented in [method dispatch](0016-method-dispatch.md).
|
||||
|
||||
Float comparison is a separate surface: the six monomorphic prelude
|
||||
fns `float_eq` / `float_ne` / `float_lt` / `float_le` / `float_gt`
|
||||
@@ -57,15 +57,15 @@ primitive `Show Int`, `Show Bool`, `Show Str`, `Show Float`
|
||||
instances. Float **is** included in Show (unlike Eq/Ord) — IEEE-754
|
||||
makes structural equality and total ordering semantically dubious,
|
||||
but textual representation of a Float is well-defined modulo the
|
||||
NaN-spelling caveat in [Float semantics](float-semantics.md). Each
|
||||
NaN-spelling caveat in [Float semantics](0005-float-semantics.md). Each
|
||||
`Show <T>` instance body is a single-application lambda invoking the
|
||||
corresponding runtime primitive (`int_to_str`, `bool_to_str`,
|
||||
`str_clone`, `float_to_str`; see [Str ABI](str-abi.md) for the
|
||||
`str_clone`, `float_to_str`; see [Str ABI](0011-str-abi.md) for the
|
||||
heap-Str primitives); no codegen intercept is required. The
|
||||
polymorphic helper `print : forall a. Show a => a -> () !IO` has body
|
||||
`\x -> let s = show x in do io/print_str s` (explicit let-binder for
|
||||
heap-Str RC discipline per the Str carve-out in
|
||||
[Str ABI](str-abi.md)). The let-binder is
|
||||
[Str ABI](0011-str-abi.md)). The let-binder is
|
||||
structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`.
|
||||
Routing through `print` is the path for non-`Str` primitives;
|
||||
`io/print_str` is the only built-in direct-output effect-op.
|
||||
Reference in New Issue
Block a user