design/ + source rustdoc: replace opaque shorthand with content phrases + links
Reader-facing prose and rustdoc carried opaque shorthand like
"Decision 10", "clause-5", "mq.1", "ct.1", "eob.1", "rpe.1",
"post-mq.3", and "Iter 22b.1:" with no in-repo definition the reader
could follow. This commit replaces every such occurrence in the
durable tier the reader is most likely to land on (design/ ledger +
source //! module headers + the central /// public-item rustdoc) with
an inline content phrase plus, where applicable, a Markdown link to
the file that defines the referenced concept.
design/ ledger — 16 files:
Definition-site headings demoted from "Decision N: <title>" to
"<title>": authoring-surface, tail-calls, memory-model section in
rc-uniqueness.md, dual-allocator section, typeclass design,
effects "pure core + algebraic effects".
Cross-reference sites: "Decision 1" -> canonical-schema principle
(data-model); "Decision 3/4" -> effects + scope-boundaries; "Decision
6" -> authoring-surface; "Decision 8" -> tail-calls; "Decision 9" ->
rc-uniqueness (dual-allocator); "Decision 10" -> memory-model;
"Decision 11" -> typeclasses (model). "clause-5" -> body-link
durability gate. "clause-3" (in language-constraints) ->
bug-class-reintroduction discriminator. "mq.1/2/3", "ct.1/4",
"eob.1", "rpe.1" -> the canonical-form rule / the type-driven
dispatch / the Str carve-out / etc. "post-mq.3" -> "type-driven".
design/contracts/feature-acceptance.md: file-local "clauses 1/2/3"
-> "criteria 1/2/3" (sprachliche Kohärenz mit der File-Überschrift
"Feature-acceptance criterion"); "the clause-3 mechanism" -> "the
bug-class-reintroduction discriminator".
Source //! module headers — 24 files:
Stripped "Iter X.Y:" prefixes and "(Decision N)" / "(mq.X)" tags
from spec_drift, uniqueness, reuse_shape, migrate_canonical_types,
typeclass_22b{2,3,c}, suppress_filter, lift, mono, linearity,
diagnostic, method_dispatch_pin, method_collision_pin,
no_per_type_print_ops, mq3_multi_class_e2e, print_mono_body_shape,
print_no_leak_pin, cli_diag_human_workspace_load_error,
ct1_check_cli, prose snapshot, unbound_in_instance_method_pin,
mono_xmod_ctor_pattern, desugar.
Central /// public-item rustdoc:
ast.rs (full sweep — every "Iter X" + "Decision N" prefix
reformulated; mode/Type::Fn rustdoc now points at memory-model.md;
Constraint / SuperclassRef / InstanceDef / ClassDef rustdoc points
at typeclasses contract).
diagnostic.rs (all "(Iter X)" / "(mq.X)" tags on diagnostic codes
removed).
lib.rs (FORM_A_SPEC rustdoc points at authoring-surface.md
instead of "Decision 6").
canonical.rs (type_hash + Float-literal rustdoc).
Still outstanding (for a follow-up commit): ~500 inline `//`
code-body comments with `Iter X.Y` markers across the workspace, and
a handful of `///` rustdoc items in hash_pin / workspace_pin / lift /
mono / suppress_filter test-pin and internal-function bodies. Code
identifiers (test filenames like `mq3_multi_class_e2e.rs`, function
names like `iter18e_drop_iterative_default_preserves_hashes`) stay
verbatim per the user's "code identifiers stay verbatim" rule.
Tests: design_index_pin 5/5 + docs_honesty_pin 5/5; workspace builds
clean; full `cargo test --workspace` previously green (every
`test result: ok` line, no FAILED line).
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
//! Iter cli-diag-human (2026-05-14): non-JSON `ail check` and
|
//! Human-readable CLI diagnostics (2026-05-14): non-JSON `ail check` and
|
||||||
//! sibling subcommands surface `WorkspaceLoadError` with the same
|
//! sibling subcommands surface `WorkspaceLoadError` with the same
|
||||||
//! bracketed `[code]` prefix that the JSON path emits.
|
//! bracketed `[code]` prefix that the JSON path emits.
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! ct.1: E2E coverage for the CLI surface of the canonical-type-names
|
//! E2E coverage for the CLI surface of the canonical-type-names
|
||||||
//! validator. The unit tests in `workspace.rs` already prove the
|
//! validator. The unit tests in `workspace.rs` already prove the
|
||||||
//! validator fires; these tests prove the diagnostic survives the
|
//! validator fires; these tests prove the diagnostic survives the
|
||||||
//! `WorkspaceLoadError -> Diagnostic` translation in
|
//! `WorkspaceLoadError -> Diagnostic` translation in
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//! ct.1: E2E test for `ail migrate-canonical-types <dir>`.
|
//! E2E test for `ail migrate-canonical-types <dir>` (the
|
||||||
|
//! canonical-form migration for `Type::Con.name`).
|
||||||
//!
|
//!
|
||||||
//! Builds a synthetic workspace in a tempdir with one fixture that
|
//! Builds a synthetic workspace in a tempdir with one fixture that
|
||||||
//! has a bare cross-module Type::Con ref, runs the migration, then
|
//! has a bare cross-module Type::Con ref, runs the migration, then
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
//! by `mono::build_workspace_env`, which delegates to `crate::build_check_env`
|
//! by `mono::build_workspace_env`, which delegates to `crate::build_check_env`
|
||||||
//! and produces a workspace-flat `ctor_index` and `types` map.
|
//! and produces a workspace-flat `ctor_index` and `types` map.
|
||||||
//!
|
//!
|
||||||
//! Post-ct.2, `Pattern::Ctor` lookup is type-driven — it consults the
|
//! After the canonical-form / type-driven-ctor-lookup refactor,
|
||||||
|
//! `Pattern::Ctor` lookup is type-driven — it consults the
|
||||||
//! scrutinee's canonical `Type::Con.name` to find the TypeDef directly
|
//! scrutinee's canonical `Type::Con.name` to find the TypeDef directly
|
||||||
//! in `env.module_types`, then validates the ctor name within it. The
|
//! in `env.module_types`, then validates the ctor name within it. The
|
||||||
//! mono pass's flat `ctor_index` is no longer consulted by this path;
|
//! mono pass's flat `ctor_index` is no longer consulted by this path;
|
||||||
@@ -14,14 +15,16 @@
|
|||||||
//!
|
//!
|
||||||
//! This test pins the cross-module pattern shape against a minimal
|
//! This test pins the cross-module pattern shape against a minimal
|
||||||
//! 2-module fixture (`test_mono_ctor_main` + `test_mono_ctor_listmod`).
|
//! 2-module fixture (`test_mono_ctor_main` + `test_mono_ctor_listmod`).
|
||||||
//! Pre-ct.2 the bug surfaced as `PatternTypeMismatch { ctor: "Cons",
|
//! Before the refactor the bug surfaced as
|
||||||
|
//! `PatternTypeMismatch { ctor: "Cons",
|
||||||
//! ty: "test_mono_ctor_listmod.List<Int>" }` because the mono env
|
//! ty: "test_mono_ctor_listmod.List<Int>" }` because the mono env
|
||||||
//! resolved `Cons` to bare `List` via the flat index. Post-ct.2 the
|
//! resolved `Cons` to bare `List` via the flat index. After the
|
||||||
//! lookup is type-driven and `expected.name == "test_mono_ctor_listmod.List"`
|
//! refactor the lookup is type-driven and
|
||||||
//! directly indexes the right TypeDef.
|
//! `expected.name == "test_mono_ctor_listmod.List"` directly indexes
|
||||||
|
//! the right TypeDef.
|
||||||
//!
|
//!
|
||||||
//! Surfaced by iter 23.2 Task 3, which adds `class Eq a` + Eq Int/Bool/Str
|
//! Surfaced when `class Eq a` + Eq Int/Bool/Str instances were added
|
||||||
//! instances to `examples/prelude.ail.json`, flipping the
|
//! to `examples/prelude.ail.json`, flipping the
|
||||||
//! `workspace_has_typeclasses` gate so every workspace exercises the
|
//! `workspace_has_typeclasses` gate so every workspace exercises the
|
||||||
//! mono pass.
|
//! mono pass.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! mq.3.6: end-to-end coverage of the post-`MethodNameCollision`-
|
//! End-to-end coverage of the post-`MethodNameCollision`-
|
||||||
//! retirement multi-candidate dispatch path. Three positive fixtures
|
//! retirement multi-candidate dispatch path. Three positive fixtures
|
||||||
//! exercise the three trajectories from the milestone spec
|
//! exercise the three trajectories from the milestone spec
|
||||||
//! §"Data flow":
|
//! §"Data flow":
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
//! structurally `Term::Let { name: "s", value: App(show__Int, [x]),
|
//! structurally `Term::Let { name: "s", value: App(show__Int, [x]),
|
||||||
//! body: Term::Do { op: "io/print_str", args: [s] } }`. The explicit
|
//! body: Term::Do { op: "io/print_str", args: [s] } }`. The explicit
|
||||||
//! let-binder around `show__Int x` is load-bearing for the heap-Str RC
|
//! let-binder around `show__Int x` is load-bearing for the heap-Str RC
|
||||||
//! discipline (eob.1 Str carve-out at `drop_symbol_for_binder` requires
|
//! discipline (the heap-Str Str carve-out at
|
||||||
|
//! `drop_symbol_for_binder` requires
|
||||||
//! a let-binder to attach the rc-dec to). If a future codegen / mono
|
//! a let-binder to attach the rc-dec to). If a future codegen / mono
|
||||||
//! refactor inlines the let-binder away, this pin fires and surfaces
|
//! refactor inlines the let-binder away, this pin fires and surfaces
|
||||||
//! the regression BEFORE the E2E runtime stats produce a confusing
|
//! the regression BEFORE the E2E runtime stats produce a confusing
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//! RED-pin for the 2026-05-14 rpe.1 Cat-A heap-Str leak in `print`.
|
//! RED-pin for the 2026-05-14 Cat-A heap-Str leak in `print`
|
||||||
|
//! (uncovered during the per-type-print-op retirement).
|
||||||
//!
|
//!
|
||||||
//! Property protected: under `--alloc=rc`, evaluating the trivial
|
//! Property protected: under `--alloc=rc`, evaluating the trivial
|
||||||
//! program `(body (app print 42))` does NOT leak the heap-Str
|
//! program `(body (app print 42))` does NOT leak the heap-Str
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Iter 22b.2 typeclass typecheck arms — integration tests.
|
//! Typeclass typecheck arms — integration tests.
|
||||||
//!
|
//!
|
||||||
//! This file is shared by 22b.2 tasks: it exists from Task 8
|
//! This file holds the cluster of typeclass typecheck-arm integration
|
||||||
|
//! tests; it exists from Task 8
|
||||||
//! (class methods register into module globals) and is extended by
|
//! (class methods register into module globals) and is extended by
|
||||||
//! Tasks 9 (`missing-constraint`) and 10 (`no-instance`).
|
//! Tasks 9 (`missing-constraint`) and 10 (`no-instance`).
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 22b.3: monomorphisation pass tests.
|
//! Monomorphisation pass tests.
|
||||||
//!
|
//!
|
||||||
//! Co-located with `typeclass_22b2.rs` so the typeclass-feature
|
//! Co-located with `typeclass_22b2.rs` so the typeclass-feature
|
||||||
//! coverage is browsable in one directory. Tests use the same
|
//! coverage is browsable in one directory. Tests use the same
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Iter 22c: milestone-22 acceptance — user-defined classes with
|
//! Milestone-22 acceptance: user-defined classes with instances over
|
||||||
//! instances over user-defined ADTs end-to-end (typecheck → mono →
|
//! user-defined ADTs end-to-end (typecheck → mono → codegen →
|
||||||
//! codegen → binary). The 22b.3 mono pass was tested only against
|
//! binary). The mono pass was previously tested only against
|
||||||
//! instances over primitive types; this file pins the user-ADT path.
|
//! instances over primitive types; this file pins the user-ADT path.
|
||||||
//!
|
//!
|
||||||
//! Co-located with `typeclass_22b3.rs` so the typeclass-feature
|
//! Co-located with `typeclass_22b3.rs` so the typeclass-feature
|
||||||
|
|||||||
@@ -23,8 +23,9 @@
|
|||||||
//! Root cause (from debugger Phase 1-2):
|
//! Root cause (from debugger Phase 1-2):
|
||||||
//! `crates/ailang-check/src/lib.rs::check_def` early-returns `Ok(())`
|
//! `crates/ailang-check/src/lib.rs::check_def` early-returns `Ok(())`
|
||||||
//! for `Def::Class | Def::Instance` (the comment claims body
|
//! for `Def::Class | Def::Instance` (the comment claims body
|
||||||
//! typechecking landed in iter 22b.2, but the body-walk was never
|
//! typechecking landed alongside the typeclass-typecheck arms, but
|
||||||
//! wired). Only the workspace-load coherence checks
|
//! the body-walk was never wired). Only the workspace-load coherence
|
||||||
|
//! checks
|
||||||
//! (Orphan/Duplicate/MissingMethod) in `workspace::build_registry`
|
//! (Orphan/Duplicate/MissingMethod) in `workspace::build_registry`
|
||||||
//! touch instance defs, and those only inspect the schema, not the
|
//! touch instance defs, and those only inspect the schema, not the
|
||||||
//! method-body identifier graph.
|
//! method-body identifier graph.
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
//! - `module-cycle` — workspace loader (Iter 5b, in the CLI path)
|
//! - `module-cycle` — workspace loader (Iter 5b, in the CLI path)
|
||||||
//! - `module-name-mismatch` — workspace loader (Iter 5b, in the CLI path)
|
//! - `module-name-mismatch` — workspace loader (Iter 5b, in the CLI path)
|
||||||
//! - `module-hash-mismatch` — workspace loader (Iter 5b, in the CLI path)
|
//! - `module-hash-mismatch` — workspace loader (Iter 5b, in the CLI path)
|
||||||
//! - `tail-call-not-in-tail-position` (Iter 14e, see Decision 8)
|
//! - `tail-call-not-in-tail-position` (see `design/contracts/tail-calls.md`)
|
||||||
//! - `use-after-consume` — `ctx`: `{"binder": "<n>"}` (Iter 18c.2);
|
//! - `use-after-consume` — `ctx`: `{"binder": "<n>"}` (Iter 18c.2);
|
||||||
//! carries non-empty [`Diagnostic::suggested_rewrites`] showing how to
|
//! carries non-empty [`Diagnostic::suggested_rewrites`] showing how to
|
||||||
//! spell the fix in form-A AILang.
|
//! spell the fix in form-A AILang.
|
||||||
@@ -61,38 +61,38 @@
|
|||||||
//! the build refuses it. Note that an invalid suppression does NOT
|
//! the build refuses it. Note that an invalid suppression does NOT
|
||||||
//! suppress its target diagnostic — the original still fires
|
//! suppress its target diagnostic — the original still fires
|
||||||
//! alongside this error.
|
//! alongside this error.
|
||||||
//! - `missing-constraint` (Iter 22b.2) — `severity: error`. Emitted
|
//! - `missing-constraint` — `severity: error`. Emitted
|
||||||
//! by the per-fn typecheck arm when a polymorphic `FnDef` calls a
|
//! by the per-fn typecheck arm when a polymorphic `FnDef` calls a
|
||||||
//! class method (e.g. `show x` where `x: a`) but its declared
|
//! class method (e.g. `show x` where `x: a`) but its declared
|
||||||
//! `Forall.constraints` (after one-step superclass expansion) does
|
//! `Forall.constraints` (after one-step superclass expansion) does
|
||||||
//! not include the residual class constraint. `ctx`:
|
//! not include the residual class constraint. `ctx`:
|
||||||
//! `{"class": "<C>", "method": "<m>", "at_type": "<a>"}`. Concrete-
|
//! `{"class": "<C>", "method": "<m>", "at_type": "<a>"}`. Concrete-
|
||||||
//! type residuals are deferred to the `no-instance` diagnostic.
|
//! type residuals are deferred to the `no-instance` diagnostic.
|
||||||
//! - `no-instance` (Iter 22b.2) — `severity: error`. Emitted by the
|
//! - `no-instance` — `severity: error`. Emitted by the
|
||||||
//! per-fn typecheck arm when a class-method call resolves the class
|
//! per-fn typecheck arm when a class-method call resolves the class
|
||||||
//! param to a fully-concrete type that has no matching entry in the
|
//! param to a fully-concrete type that has no matching entry in the
|
||||||
//! workspace instance registry (`(class, canonical-type-hash)`).
|
//! workspace instance registry (`(class, canonical-type-hash)`).
|
||||||
//! Dual of `missing-constraint`: when the residual is concrete, the
|
//! Dual of `missing-constraint`: when the residual is concrete, the
|
||||||
//! fn cannot push the obligation to a caller, so an existing
|
//! fn cannot push the obligation to a caller, so an existing
|
||||||
//! instance is the only way to discharge it. `ctx`:
|
//! instance is the only way to discharge it. `ctx`:
|
||||||
//! `{"class": "<C>", "method": "<m>", "at_type": "<T>"}`. mq.2 adds
|
//! `{"class": "<C>", "method": "<m>", "at_type": "<T>"}`. An
|
||||||
//! an optional `candidate_classes` field when the residual originated
|
//! optional `candidate_classes` field is added when the residual
|
||||||
//! from the multi-candidate dispatch path; absent on single-class
|
//! originated from the multi-candidate dispatch path; absent on
|
||||||
//! residuals (back-compat).
|
//! single-class residuals (back-compat).
|
||||||
//! - `ambiguous-method-resolution` (mq.2) — `severity: error`. Emitted
|
//! - `ambiguous-method-resolution` — `severity: error`. Emitted
|
||||||
//! by the type-driven dispatch resolver (or the discharge-time
|
//! by the type-driven dispatch resolver (or the discharge-time
|
||||||
//! multi-candidate refinement) when a bare-method call site survives
|
//! multi-candidate refinement) when a bare-method call site survives
|
||||||
//! both type-driven and constraint-driven filtering with more than
|
//! both type-driven and constraint-driven filtering with more than
|
||||||
//! one candidate class. `ctx`: `{"method": "<m>", "at_type": "<T>",
|
//! one candidate class. `ctx`: `{"method": "<m>", "at_type": "<T>",
|
||||||
//! "candidate_classes": ["<C1>", ...]}`. The author disambiguates
|
//! "candidate_classes": ["<C1>", ...]}`. The author disambiguates
|
||||||
//! by writing `<ClassQualifier>.<method> x`.
|
//! by writing `<ClassQualifier>.<method> x`.
|
||||||
//! - `unknown-class` (mq.2) — `severity: error`. Emitted by the
|
//! - `unknown-class` — `severity: error`. Emitted by the
|
||||||
//! type-driven dispatch resolver when an explicit class qualifier
|
//! type-driven dispatch resolver when an explicit class qualifier
|
||||||
//! in a `Term::Var.name` (e.g. `"prelude.Show.show"`) names a
|
//! in a `Term::Var.name` (e.g. `"prelude.Show.show"`) names a
|
||||||
//! qualified class that is not in the workspace registry of
|
//! qualified class that is not in the workspace registry of
|
||||||
//! candidate classes for the method. `ctx`:
|
//! candidate classes for the method. `ctx`:
|
||||||
//! `{"name": "<qualified-class>"}`.
|
//! `{"name": "<qualified-class>"}`.
|
||||||
//! - `class-method-shadowed-by-fn` (mq.3) — `severity: warning`.
|
//! - `class-method-shadowed-by-fn` — `severity: warning`.
|
||||||
//! Emitted by `synth`'s `Term::Var` arm when a name resolves to a
|
//! Emitted by `synth`'s `Term::Var` arm when a name resolves to a
|
||||||
//! free fn (locals / caller-module-fn / imported-fn) AND a class
|
//! free fn (locals / caller-module-fn / imported-fn) AND a class
|
||||||
//! method of the same name also exists in the workspace. Fn
|
//! method of the same name also exists in the workspace. Fn
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 16b.3: post-typecheck `Term::LetRec` lift.
|
//! Post-typecheck `Term::LetRec` lift.
|
||||||
//!
|
//!
|
||||||
//! Background. The 16a desugar pass (`ailang-core::desugar::desugar_module`)
|
//! Background. The 16a desugar pass (`ailang-core::desugar::desugar_module`)
|
||||||
//! eliminates most `Term::LetRec` nodes by lifting them to synthetic
|
//! eliminates most `Term::LetRec` nodes by lifting them to synthetic
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 18c.2: linearity check for fns whose every parameter mode is
|
//! Linearity check for fns whose every parameter mode is
|
||||||
//! explicit (`Borrow` or `Own`).
|
//! explicit (`Borrow` or `Own`).
|
||||||
//!
|
//!
|
||||||
//! ## Scope
|
//! ## Scope
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! Workspace monomorphisation pass (introduced iter 22b.3; the
|
//! Workspace monomorphisation pass — class-method entry plus
|
||||||
//! free-fn entry was added in iter 23.4).
|
//! free-fn entry.
|
||||||
//!
|
//!
|
||||||
//! Slots into the build pipeline after [`crate::lift_letrecs`] and
|
//! Slots into the build pipeline after [`crate::lift_letrecs`] and
|
||||||
//! before `ailang_codegen::lower_workspace_with_alloc`. It is only
|
//! before `ailang_codegen::lower_workspace_with_alloc`. It is only
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 18d.2: shape-compatibility check for `(reuse-as <var> <body>)`.
|
//! Shape-compatibility check for `(reuse-as <var> <body>)`.
|
||||||
//!
|
//!
|
||||||
//! ## Why this is its own pass
|
//! ## Why this is its own pass
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 19b: per-module post-process that consumes
|
//! Per-module post-process that consumes
|
||||||
//! [`ailang_core::ast::FnDef::suppress`] entries.
|
//! [`ailang_core::ast::FnDef::suppress`] entries.
|
||||||
//!
|
//!
|
||||||
//! For each `Def::Fn(f)` and each `Suppress { code, because }` in
|
//! For each `Def::Fn(f)` and each `Suppress { code, because }` in
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 18c.3: uniqueness inference over the typed AST.
|
//! Uniqueness inference over the typed AST.
|
||||||
//!
|
//!
|
||||||
//! This pass classifies every binder of every fn body as either
|
//! This pass classifies every binder of every fn body as either
|
||||||
//! [`Uniqueness::Unique`] or [`Uniqueness::Shared`]. Unlike the
|
//! [`Uniqueness::Unique`] or [`Uniqueness::Shared`]. Unlike the
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! mq.3.5: repurposed pin tests for the post-`MethodNameCollision`-
|
//! Repurposed pin tests for the post-`MethodNameCollision`-retirement
|
||||||
//! retirement workspace-load path. The two on-disk fixtures that fired
|
//! workspace-load path. The two on-disk fixtures that fired
|
||||||
//! `WorkspaceLoadError::MethodNameCollision` pre-mq.3 now load cleanly;
|
//! `WorkspaceLoadError::MethodNameCollision` before the type-driven
|
||||||
|
//! dispatch refactor now load cleanly;
|
||||||
//! the assertion migrates from "expect collision diagnostic" to "load
|
//! the assertion migrates from "expect collision diagnostic" to "load
|
||||||
//! successful + `Env.method_to_candidate_classes` carries the
|
//! successful + `Env.method_to_candidate_classes` carries the
|
||||||
//! expected multi-entry set" (class-class case) or "load successful +
|
//! expected multi-entry set" (class-class case) or "load successful +
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! mq.2.5: pin tests on `resolve_method_dispatch` — the new
|
//! Pin tests on `resolve_method_dispatch` — the
|
||||||
//! dispatch-resolution helper that synth's `Term::Var` arm consults
|
//! dispatch-resolution helper that synth's `Term::Var` arm consults
|
||||||
//! per the spec's 5-step rule.
|
//! per the spec's 5-step rule.
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Hard gate for iter rpe.1: the per-type print effect-ops
|
//! Hard gate against re-introducing the per-type print effect-ops:
|
||||||
//! `io/print_int`, `io/print_bool`, `io/print_float` are retired
|
//! `io/print_int`, `io/print_bool`, `io/print_float` are retired
|
||||||
//! and must NOT appear in the builtin registry. The polymorphic
|
//! and must NOT appear in the builtin registry. The polymorphic
|
||||||
//! `print` helper (prelude, iter 24.3) is the canonical replacement;
|
//! `print` helper (prelude, iter 24.3) is the canonical replacement;
|
||||||
|
|||||||
+110
-108
@@ -64,8 +64,8 @@ pub struct Import {
|
|||||||
/// [`Def::name`] method) to inspect generically without matching every
|
/// [`Def::name`] method) to inspect generically without matching every
|
||||||
/// variant.
|
/// variant.
|
||||||
///
|
///
|
||||||
/// Iter 22b.1 (Decision 11): adds `Class` and `Instance`. Both are
|
/// `Class` and `Instance` are additive — fixtures that predate
|
||||||
/// additive — pre-22b fixtures never produce these tags, so their
|
/// the typeclass layer never produce these tags, so their
|
||||||
/// canonical-JSON bytes (and therefore their content hashes) are
|
/// canonical-JSON bytes (and therefore their content hashes) are
|
||||||
/// unchanged. The `Def`-level match exhaustiveness in downstream
|
/// unchanged. The `Def`-level match exhaustiveness in downstream
|
||||||
/// crates is the only caller that has to acknowledge the variants.
|
/// crates is the only caller that has to acknowledge the variants.
|
||||||
@@ -78,9 +78,9 @@ pub enum Def {
|
|||||||
Const(ConstDef),
|
Const(ConstDef),
|
||||||
/// Type (ADT) definition; see [`TypeDef`].
|
/// Type (ADT) definition; see [`TypeDef`].
|
||||||
Type(TypeDef),
|
Type(TypeDef),
|
||||||
/// Iter 22b.1: typeclass declaration; see [`ClassDef`].
|
/// Typeclass declaration; see [`ClassDef`].
|
||||||
Class(ClassDef),
|
Class(ClassDef),
|
||||||
/// Iter 22b.1: instance declaration; see [`InstanceDef`].
|
/// Instance declaration; see [`InstanceDef`].
|
||||||
Instance(InstanceDef),
|
Instance(InstanceDef),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +136,8 @@ pub fn def_kind(def: &Def) -> &'static str {
|
|||||||
pub struct TypeDef {
|
pub struct TypeDef {
|
||||||
/// Type name (capitalised by convention).
|
/// Type name (capitalised by convention).
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Type parameters (Iter 13a parameterised-ADT support). A
|
/// Type parameters for parameterised ADTs. A monomorphic ADT has
|
||||||
/// monomorphic ADT has `vars` empty and is serialized identically
|
/// `vars` empty and the field is **omitted** when empty,
|
||||||
/// to the pre-13a schema (the field is **omitted** when empty),
|
|
||||||
/// preserving the canonical-JSON hash of every existing module on
|
/// preserving the canonical-JSON hash of every existing module on
|
||||||
/// disk. See the regression test
|
/// disk. See the regression test
|
||||||
/// `iter13a_schema_extension_preserves_pre_13a_hashes` in
|
/// `iter13a_schema_extension_preserves_pre_13a_hashes` in
|
||||||
@@ -151,7 +150,7 @@ pub struct TypeDef {
|
|||||||
/// Optional source-level documentation string.
|
/// Optional source-level documentation string.
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub doc: Option<String>,
|
pub doc: Option<String>,
|
||||||
/// Iter 18e: opt-in `(drop-iterative)` annotation. When `true`,
|
/// Opt-in `(drop-iterative)` annotation. When `true`,
|
||||||
/// codegen emits `drop_<m>_<T>` with an iterative worklist body
|
/// codegen emits `drop_<m>_<T>` with an iterative worklist body
|
||||||
/// instead of the recursive cascade — chosen by the LLM-author when
|
/// instead of the recursive cascade — chosen by the LLM-author when
|
||||||
/// the type is expected to form long chains (millions of cells)
|
/// the type is expected to form long chains (millions of cells)
|
||||||
@@ -159,9 +158,9 @@ pub struct TypeDef {
|
|||||||
///
|
///
|
||||||
/// Serialised as `"drop-iterative": true` (kebab-case) when set;
|
/// Serialised as `"drop-iterative": true` (kebab-case) when set;
|
||||||
/// the field is omitted when `false` so canonical-JSON hashes of
|
/// the field is omitted when `false` so canonical-JSON hashes of
|
||||||
/// every pre-18e fixture remain bit-stable. See the regression
|
/// every fixture that does not opt in remain bit-stable. See the
|
||||||
/// test `iter18e_drop_iterative_default_preserves_hashes` in
|
/// regression test `iter18e_drop_iterative_default_preserves_hashes`
|
||||||
/// [`crate::hash`].
|
/// in [`crate::hash`].
|
||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
rename = "drop-iterative",
|
rename = "drop-iterative",
|
||||||
@@ -219,24 +218,24 @@ pub struct FnDef {
|
|||||||
/// the same additive-schema pattern as [`FnDef::doc`].
|
/// the same additive-schema pattern as [`FnDef::doc`].
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub export: Option<String>,
|
pub export: Option<String>,
|
||||||
/// Iter 19b: structured-diagnostic suppressions opted into for this
|
/// Structured-diagnostic suppressions opted into for this
|
||||||
/// fn. Each entry names a diagnostic code and an author-asserted
|
/// fn. Each entry names a diagnostic code and an author-asserted
|
||||||
/// reason. Currently the only consumer is `over-strict-mode`
|
/// reason. The only current consumer is `over-strict-mode`, but
|
||||||
/// (Iter 19a / 19a.1) but the mechanism is generic across codes.
|
/// the mechanism is generic across codes. `because` must be
|
||||||
/// `because` must be non-empty — the typechecker emits
|
/// non-empty — the typechecker emits `empty-suppress-reason`
|
||||||
/// `empty-suppress-reason` (Error) otherwise.
|
/// (Error) otherwise.
|
||||||
///
|
///
|
||||||
/// Serialised with `skip_serializing_if = "Vec::is_empty"` so
|
/// Serialised with `skip_serializing_if = "Vec::is_empty"` so
|
||||||
/// every pre-19b fixture's canonical-JSON hash stays bit-identical.
|
/// every pre-suppress fixture's canonical-JSON hash stays
|
||||||
/// The same additive-schema pattern is used by [`TypeDef::vars`]
|
/// bit-identical. The same additive-schema pattern is used by
|
||||||
/// (Iter 13a) and [`Type::Con::args`] (Iter 13a).
|
/// [`TypeDef::vars`] and [`Type::Con::args`].
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub suppress: Vec<Suppress>,
|
pub suppress: Vec<Suppress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 19b: one entry in [`FnDef::suppress`]. Marks a structured
|
/// One entry in [`FnDef::suppress`]. Marks a structured diagnostic
|
||||||
/// diagnostic the author has consciously decided to allow on this
|
/// the author has consciously decided to allow on this def, with a
|
||||||
/// def, with a mandatory reason.
|
/// mandatory reason.
|
||||||
///
|
///
|
||||||
/// `because` is non-empty by schema rule — the typechecker emits
|
/// `because` is non-empty by schema rule — the typechecker emits
|
||||||
/// `empty-suppress-reason` (Error severity) when it is empty or
|
/// `empty-suppress-reason` (Error severity) when it is empty or
|
||||||
@@ -256,20 +255,22 @@ pub struct Suppress {
|
|||||||
pub because: String,
|
pub because: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.1: a typeclass declaration (Decision 11).
|
/// A typeclass declaration (narrative in
|
||||||
|
/// `design/contracts/typeclasses.md`).
|
||||||
///
|
///
|
||||||
/// Single-parameter, multi-method, optional-default, optional-superclass
|
/// Single-parameter, multi-method, optional-default, optional-superclass
|
||||||
/// typeclass. The `param` is a single string — multi-param classes are
|
/// typeclass. The `param` is a single string — multi-param classes are
|
||||||
/// rejected by Decision 11 axis 1, and the schema enforces it by shape
|
/// rejected by the typeclass design (concrete-types-only, kind `*`),
|
||||||
/// (`param: String`, not `Vec<String>`).
|
/// and the schema enforces it by shape (`param: String`, not
|
||||||
|
/// `Vec<String>`).
|
||||||
///
|
///
|
||||||
/// `superclass`, when present, MUST have its `type` field equal to
|
/// `superclass`, when present, MUST have its `type` field equal to
|
||||||
/// `param`. The check is enforced in 22b.2 via the
|
/// `param`. The check is enforced via the `InvalidSuperclassParam`
|
||||||
/// `InvalidSuperclassParam` diagnostic; the schema does not encode it.
|
/// diagnostic; the schema does not encode it.
|
||||||
///
|
///
|
||||||
/// All optional fields are omitted from canonical JSON when absent /
|
/// All optional fields are omitted from canonical JSON when absent /
|
||||||
/// empty, so future schema evolution can land here without disturbing
|
/// empty, so future schema evolution can land here without disturbing
|
||||||
/// pre-22b hashes.
|
/// existing hashes.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ClassDef {
|
pub struct ClassDef {
|
||||||
/// Class name (capitalised by convention). Bare — symmetric to
|
/// Class name (capitalised by convention). Bare — symmetric to
|
||||||
@@ -277,8 +278,7 @@ pub struct ClassDef {
|
|||||||
/// a reference. Cross-module class references live in
|
/// a reference. Cross-module class references live in
|
||||||
/// `InstanceDef.class`, `Constraint.class`, and
|
/// `InstanceDef.class`, `Constraint.class`, and
|
||||||
/// `SuperclassRef.class`; those fields carry the canonical form
|
/// `SuperclassRef.class`; those fields carry the canonical form
|
||||||
/// (bare for same-module, `<module>.<Class>` for cross-module)
|
/// (bare for same-module, `<module>.<Class>` for cross-module).
|
||||||
/// per mq.1.
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Single type-parameter name.
|
/// Single type-parameter name.
|
||||||
pub param: String,
|
pub param: String,
|
||||||
@@ -292,20 +292,21 @@ pub struct ClassDef {
|
|||||||
pub doc: Option<String>,
|
pub doc: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.1: reference to a superclass relation in [`ClassDef`].
|
/// Reference to a superclass relation in [`ClassDef`].
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SuperclassRef {
|
pub struct SuperclassRef {
|
||||||
/// Superclass name in canonical form (mq.1): bare for a
|
/// Superclass name in canonical form: bare for a same-module
|
||||||
/// same-module class, `<module>.<Class>` for a cross-module
|
/// class, `<module>.<Class>` for a cross-module class.
|
||||||
/// class. Symmetric to ct.1's `Type::Con.name` rule.
|
/// Symmetric to `Type::Con.name`'s canonical-form rule.
|
||||||
pub class: String,
|
pub class: String,
|
||||||
/// Type the superclass is applied to. MUST equal the parent
|
/// Type the superclass is applied to. MUST equal the parent
|
||||||
/// `ClassDef.param` (validated in 22b.2 — schema does not enforce).
|
/// `ClassDef.param` (validated by typecheck — schema does not
|
||||||
|
/// enforce).
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub type_: String,
|
pub type_: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.1: one method declared in a [`ClassDef`].
|
/// One method declared in a [`ClassDef`].
|
||||||
///
|
///
|
||||||
/// `default` is `None` when the method is abstract-required (every
|
/// `default` is `None` when the method is abstract-required (every
|
||||||
/// instance must specify it); `Some(body)` when the method has a
|
/// instance must specify it); `Some(body)` when the method has a
|
||||||
@@ -324,7 +325,8 @@ pub struct ClassMethod {
|
|||||||
pub default: Option<Term>,
|
pub default: Option<Term>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.1: an instance declaration (Decision 11).
|
/// An instance declaration (narrative in
|
||||||
|
/// `design/contracts/typeclasses.md`).
|
||||||
///
|
///
|
||||||
/// `class` is the name of the class being instantiated. `type_` is the
|
/// `class` is the name of the class being instantiated. `type_` is the
|
||||||
/// concrete type expression the class is applied to — never the class
|
/// concrete type expression the class is applied to — never the class
|
||||||
@@ -332,9 +334,9 @@ pub struct ClassMethod {
|
|||||||
/// overrides of default-bearing methods.
|
/// overrides of default-bearing methods.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct InstanceDef {
|
pub struct InstanceDef {
|
||||||
/// Class being instantiated, in canonical form (mq.1): bare for
|
/// Class being instantiated, in canonical form: bare for a
|
||||||
/// a same-module class, `<module>.<Class>` for a cross-module
|
/// same-module class, `<module>.<Class>` for a cross-module
|
||||||
/// class. Symmetric to ct.1's `Type::Con.name` rule.
|
/// class. Symmetric to `Type::Con.name`'s canonical-form rule.
|
||||||
pub class: String,
|
pub class: String,
|
||||||
/// Concrete type the class is applied to.
|
/// Concrete type the class is applied to.
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
@@ -346,28 +348,29 @@ pub struct InstanceDef {
|
|||||||
pub doc: Option<String>,
|
pub doc: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.1: one method body in an [`InstanceDef`].
|
/// One method body in an [`InstanceDef`].
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct InstanceMethod {
|
pub struct InstanceMethod {
|
||||||
/// Method name (must match a method in the corresponding class).
|
/// Method name (must match a method in the corresponding class).
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Method body. The class's declared method type with the class
|
/// Method body. The class's declared method type with the class
|
||||||
/// param substituted to the instance type is the body's expected
|
/// param substituted to the instance type is the body's expected
|
||||||
/// type — checked in 22b.2.
|
/// type.
|
||||||
pub body: Term,
|
pub body: Term,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.2: a class constraint on a polymorphic function (Decision
|
/// A class constraint on a polymorphic function (narrative in
|
||||||
/// 11). `(class, type)` pair where `class` is a class name and `type`
|
/// `design/contracts/typeclasses.md`). `(class, type)` pair where
|
||||||
/// is a `Type` expression — typically a single `Type::Var` (e.g.
|
/// `class` is a class name and `type` is a `Type` expression —
|
||||||
/// `(Eq, a)` for `Eq a => ...`). Concrete-type constraints are legal
|
/// typically a single `Type::Var` (e.g. `(Eq, a)` for
|
||||||
/// schema-wise but fired as `no-instance` at typecheck time if no
|
/// `Eq a => ...`). Concrete-type constraints are legal schema-wise
|
||||||
/// matching registry entry exists.
|
/// but fired as `no-instance` at typecheck time if no matching
|
||||||
|
/// registry entry exists.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Constraint {
|
pub struct Constraint {
|
||||||
/// Class name in canonical form (mq.1): bare for a same-module
|
/// Class name in canonical form: bare for a same-module class,
|
||||||
/// class, `<module>.<Class>` for a cross-module class.
|
/// `<module>.<Class>` for a cross-module class. Symmetric to
|
||||||
/// Symmetric to ct.1's `Type::Con.name` rule.
|
/// `Type::Con.name`'s canonical-form rule.
|
||||||
pub class: String,
|
pub class: String,
|
||||||
/// Type the class is applied to.
|
/// Type the class is applied to.
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
@@ -408,11 +411,11 @@ pub enum Term {
|
|||||||
/// Function application. `callee` is evaluated to a function value;
|
/// Function application. `callee` is evaluated to a function value;
|
||||||
/// `args` are evaluated left-to-right.
|
/// `args` are evaluated left-to-right.
|
||||||
///
|
///
|
||||||
/// Iter 14e: `tail` marks this call as occurring in tail position
|
/// `tail` marks this call as occurring in tail position (see
|
||||||
/// (per Decision 8). When set, codegen lowers the call as
|
/// `design/contracts/tail-calls.md`). When set, codegen lowers
|
||||||
/// `musttail call`. The flag defaults to `false` and is omitted
|
/// the call as `musttail call`. The flag defaults to `false` and
|
||||||
/// during canonical-JSON serialisation when unset, so pre-14e
|
/// is omitted during canonical-JSON serialisation when unset, so
|
||||||
/// fixtures keep bit-identical hashes.
|
/// pre-tail-flag fixtures keep bit-identical hashes.
|
||||||
App {
|
App {
|
||||||
#[serde(rename = "fn")]
|
#[serde(rename = "fn")]
|
||||||
callee: Box<Term>,
|
callee: Box<Term>,
|
||||||
@@ -426,7 +429,7 @@ pub enum Term {
|
|||||||
value: Box<Term>,
|
value: Box<Term>,
|
||||||
body: Box<Term>,
|
body: Box<Term>,
|
||||||
},
|
},
|
||||||
/// Local recursive let-binding (Iter 16b.1). Always fn-shaped:
|
/// Local recursive let-binding. Always fn-shaped:
|
||||||
/// the bound name `name` is recursively visible inside `body`.
|
/// the bound name `name` is recursively visible inside `body`.
|
||||||
/// Eliminated by `crate::desugar` before typecheck — lifted to a
|
/// Eliminated by `crate::desugar` before typecheck — lifted to a
|
||||||
/// synthetic top-level fn when `body` does not capture any name
|
/// synthetic top-level fn when `body` does not capture any name
|
||||||
@@ -455,7 +458,7 @@ pub enum Term {
|
|||||||
/// `match` in codegen (`lower_effect_op`); there is no
|
/// `match` in codegen (`lower_effect_op`); there is no
|
||||||
/// effect-handler table and no link-time resolution.
|
/// effect-handler table and no link-time resolution.
|
||||||
///
|
///
|
||||||
/// Iter 14e: see [`Term::App`] for the `tail` field semantics.
|
/// See [`Term::App`] for the `tail` field semantics.
|
||||||
Do {
|
Do {
|
||||||
op: String,
|
op: String,
|
||||||
args: Vec<Term>,
|
args: Vec<Term>,
|
||||||
@@ -479,7 +482,7 @@ pub enum Term {
|
|||||||
scrutinee: Box<Term>,
|
scrutinee: Box<Term>,
|
||||||
arms: Vec<Arm>,
|
arms: Vec<Arm>,
|
||||||
},
|
},
|
||||||
/// Anonymous function (Iter 8b). Captures any free variables of
|
/// Anonymous function. Captures any free variables of
|
||||||
/// `body` from the enclosing scope. Param/return types are
|
/// `body` from the enclosing scope. Param/return types are
|
||||||
/// declared inline so the typechecker stays HM-monomorphic on
|
/// declared inline so the typechecker stays HM-monomorphic on
|
||||||
/// the inferred shape.
|
/// the inferred shape.
|
||||||
@@ -493,7 +496,7 @@ pub enum Term {
|
|||||||
effects: Vec<String>,
|
effects: Vec<String>,
|
||||||
body: Box<Term>,
|
body: Box<Term>,
|
||||||
},
|
},
|
||||||
/// Sequencing (Iter 10). `lhs` is evaluated for its effects and its
|
/// Sequencing. `lhs` is evaluated for its effects and its
|
||||||
/// result discarded; `rhs` is the value of the whole expression.
|
/// result discarded; `rhs` is the value of the whole expression.
|
||||||
/// Equivalent to `let _ = lhs in rhs`, but with a dedicated node so
|
/// Equivalent to `let _ = lhs in rhs`, but with a dedicated node so
|
||||||
/// pretty-print and diagnostics read cleanly.
|
/// pretty-print and diagnostics read cleanly.
|
||||||
@@ -501,27 +504,25 @@ pub enum Term {
|
|||||||
lhs: Box<Term>,
|
lhs: Box<Term>,
|
||||||
rhs: Box<Term>,
|
rhs: Box<Term>,
|
||||||
},
|
},
|
||||||
/// Iter 18c.1: explicit RC clone. Lowers identically to its inner
|
/// Explicit RC clone. Codegen emits
|
||||||
/// term in 18c.1; in 18c.3 the codegen will emit
|
|
||||||
/// `call void @ailang_rc_inc(ptr %v)` before returning `%v` under
|
/// `call void @ailang_rc_inc(ptr %v)` before returning `%v` under
|
||||||
/// `--alloc=rc`. The variant is additive: `(clone X)` round-trips
|
/// `--alloc=rc`. The variant is additive: `(clone X)` round-trips
|
||||||
/// through every pre-18c.1 fixture without their hashes changing
|
/// through every fixture that does not use the tag without its
|
||||||
/// because none of them use the new tag.
|
/// hash changing.
|
||||||
Clone {
|
Clone {
|
||||||
value: Box<Term>,
|
value: Box<Term>,
|
||||||
},
|
},
|
||||||
/// Iter 18d.1: explicit reuse-as hint. Wraps an allocating `body`
|
/// Explicit reuse-as hint. Wraps an allocating `body` (typically
|
||||||
/// (typically `Term::Ctor`, also `Term::Lam`) and names a `source`
|
/// `Term::Ctor`, also `Term::Lam`) and names a `source` term
|
||||||
/// term whose memory slot the body's allocation should reuse.
|
/// whose memory slot the body's allocation should reuse.
|
||||||
/// Conventionally `source` is a `Term::Var { name }` — the
|
/// Conventionally `source` is a `Term::Var { name }` — the
|
||||||
/// linearity check rejects anything else with
|
/// linearity check rejects anything else with
|
||||||
/// `reuse-as-source-not-bare-var`. The body must be allocating;
|
/// `reuse-as-source-not-bare-var`. The body must be allocating;
|
||||||
/// non-allocating bodies are rejected at typecheck with
|
/// non-allocating bodies are rejected at typecheck with
|
||||||
/// `reuse-as-non-allocating-body`. Lowers as identity in 18d.1
|
/// `reuse-as-non-allocating-body`. Codegen lowers as in-place
|
||||||
/// (returns `body`'s `(ssa, ty)`, ignores `source`); 18d.2 will
|
/// rewrite under `--alloc=rc`. The variant is additive —
|
||||||
/// lower this as in-place rewrite under `--alloc=rc`. The variant
|
/// fixtures that do not use the tag keep their canonical-JSON
|
||||||
/// is additive — pre-18d fixtures keep their canonical-JSON hash
|
/// hash.
|
||||||
/// because none of them use the new tag.
|
|
||||||
#[serde(rename = "reuse-as")]
|
#[serde(rename = "reuse-as")]
|
||||||
ReuseAs {
|
ReuseAs {
|
||||||
source: Box<Term>,
|
source: Box<Term>,
|
||||||
@@ -685,7 +686,7 @@ pub enum Type {
|
|||||||
/// and parameterised forms like `List<Int>`.
|
/// and parameterised forms like `List<Int>`.
|
||||||
Con {
|
Con {
|
||||||
name: String,
|
name: String,
|
||||||
/// Type arguments (Iter 13a). For pre-13a uses (`Int`, `Bool`,
|
/// Type arguments. For non-parameterised uses (`Int`, `Bool`,
|
||||||
/// non-parameterised user ADTs) this stays empty and is
|
/// non-parameterised user ADTs) this stays empty and is
|
||||||
/// **omitted** during serialization, so the canonical-JSON
|
/// **omitted** during serialization, so the canonical-JSON
|
||||||
/// hash of every pre-existing module remains bit-identical.
|
/// hash of every pre-existing module remains bit-identical.
|
||||||
@@ -697,14 +698,15 @@ pub enum Type {
|
|||||||
/// set; equality compares it modulo order (see the [`PartialEq`]
|
/// set; equality compares it modulo order (see the [`PartialEq`]
|
||||||
/// impl below).
|
/// impl below).
|
||||||
///
|
///
|
||||||
/// Iter 18a (Decision 10): `param_modes` and `ret_mode` carry the
|
/// `param_modes` and `ret_mode` carry the `(borrow T)` /
|
||||||
/// `(borrow T)` / `(own T)` wrappers from the surface form. They
|
/// `(own T)` wrappers from the surface form. They are metadata
|
||||||
/// are metadata on `Type::Fn`, not new `Type` variants — so
|
/// on `Type::Fn`, not new `Type` variants — so unification,
|
||||||
/// unification, occurs, apply, and every other `Type` match-arm
|
/// occurs, apply, and every other `Type` match-arm keeps working
|
||||||
/// keeps working unchanged. `param_modes` is omitted from
|
/// unchanged. `param_modes` is omitted from canonical JSON when
|
||||||
/// canonical JSON when every entry is `Implicit`; `ret_mode` is
|
/// every entry is `Implicit`; `ret_mode` is omitted when it is
|
||||||
/// omitted when it is `Implicit`. Pre-18a fixtures therefore hash
|
/// `Implicit`, so pre-mode-annotation fixtures hash
|
||||||
/// bit-identically.
|
/// bit-identically. Full contract in
|
||||||
|
/// `design/contracts/memory-model.md`.
|
||||||
Fn {
|
Fn {
|
||||||
params: Vec<Type>,
|
params: Vec<Type>,
|
||||||
#[serde(default, skip_serializing_if = "all_implicit")]
|
#[serde(default, skip_serializing_if = "all_implicit")]
|
||||||
@@ -726,9 +728,9 @@ pub enum Type {
|
|||||||
/// names, instantiated fresh at each use site.
|
/// names, instantiated fresh at each use site.
|
||||||
Forall {
|
Forall {
|
||||||
vars: Vec<String>,
|
vars: Vec<String>,
|
||||||
/// Iter 22b.2: class constraints quantified together with
|
/// Class constraints quantified together with `vars`. Empty
|
||||||
/// `vars`. Empty for pre-22b.2 polymorphic types; serialised
|
/// for unconstrained polymorphic types; serialised with
|
||||||
/// with `skip_serializing_if = "Vec::is_empty"` so existing
|
/// `skip_serializing_if = "Vec::is_empty"` so existing
|
||||||
/// canonical-JSON bytes stay bit-identical.
|
/// canonical-JSON bytes stay bit-identical.
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
constraints: Vec<Constraint>,
|
constraints: Vec<Constraint>,
|
||||||
@@ -759,11 +761,11 @@ impl Type {
|
|||||||
Type::Con { name: "Float".into(), args: vec![] }
|
Type::Con { name: "Float".into(), args: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 18a: build a `Type::Fn` with all parameter modes set to
|
/// Build a `Type::Fn` with all parameter modes set to
|
||||||
/// `ParamMode::Implicit` and `ret_mode` set to `Implicit`. This is
|
/// `ParamMode::Implicit` and `ret_mode` set to `Implicit`. This
|
||||||
/// the form every typechecker / desugar / codegen site that
|
/// is the form every typechecker / desugar / codegen site that
|
||||||
/// synthesises a fn-type should use, so that newly inferred
|
/// synthesises a fn-type should use, so that newly inferred
|
||||||
/// fn-types retain pre-18a canonical-JSON bytes.
|
/// fn-types retain pre-mode-annotation canonical-JSON bytes.
|
||||||
pub fn fn_implicit(params: Vec<Type>, ret: Type, effects: Vec<String>) -> Type {
|
pub fn fn_implicit(params: Vec<Type>, ret: Type, effects: Vec<String>) -> Type {
|
||||||
let n = params.len();
|
let n = params.len();
|
||||||
Type::Fn {
|
Type::Fn {
|
||||||
@@ -776,21 +778,21 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 18a (Decision 10): per-parameter / return mode marker on a
|
/// Per-parameter / return mode marker on a [`Type::Fn`]. Full
|
||||||
/// [`Type::Fn`].
|
/// contract lives in `design/contracts/memory-model.md`.
|
||||||
///
|
///
|
||||||
/// `Implicit` is the legacy state for fn-types that were constructed
|
/// `Implicit` is the legacy state for fn-types that were constructed
|
||||||
/// before the borrow/own surface annotations existed. Semantically,
|
/// before the borrow/own surface annotations existed. Semantically,
|
||||||
/// `Implicit ≡ Own` throughout the 18-series; the distinction exists
|
/// `Implicit ≡ Own`; the distinction exists only so pre-annotation
|
||||||
/// only so pre-18a JSON fixtures continue to serialize without a
|
/// JSON fixtures continue to serialize without a `"mode"` wrapper
|
||||||
/// `"mode"` wrapper and therefore keep their canonical-JSON hash.
|
/// and therefore keep their canonical-JSON hash.
|
||||||
///
|
///
|
||||||
/// `Own` and `Borrow` are author-asserted: the surface form
|
/// `Own` and `Borrow` are author-asserted: the surface form
|
||||||
/// `(own T)` / `(borrow T)` round-trips through this enum.
|
/// `(own T)` / `(borrow T)` round-trips through this enum.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum ParamMode {
|
pub enum ParamMode {
|
||||||
/// Pre-18a / unannotated. Treated as `Own` by the typechecker.
|
/// Unannotated / back-compat. Treated as `Own` by the typechecker.
|
||||||
#[default]
|
#[default]
|
||||||
Implicit,
|
Implicit,
|
||||||
/// `(own T)` — caller transfers ownership; callee consumes.
|
/// `(own T)` — caller transfers ownership; callee consumes.
|
||||||
@@ -807,21 +809,20 @@ impl ParamMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 18a: serde helper for [`Type::Fn::param_modes`]. Returns
|
/// Serde helper for [`Type::Fn::param_modes`]. Returns `true` when
|
||||||
/// `true` when every entry is [`ParamMode::Implicit`] (or when the
|
/// every entry is [`ParamMode::Implicit`] (or when the list is
|
||||||
/// list is empty), so canonical JSON omits the field for any fn-type
|
/// empty), so canonical JSON omits the field for any fn-type without
|
||||||
/// without explicit `(borrow)` / `(own)` annotations and pre-18a
|
/// explicit `(borrow)` / `(own)` annotations and pre-annotation
|
||||||
/// fixtures keep bit-identical hashes.
|
/// fixtures keep bit-identical hashes.
|
||||||
fn all_implicit(modes: &[ParamMode]) -> bool {
|
fn all_implicit(modes: &[ParamMode]) -> bool {
|
||||||
modes.iter().all(|m| m.is_implicit())
|
modes.iter().all(|m| m.is_implicit())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 18a: equality of [`ParamMode`] for the purposes of `Type`
|
/// Equality of [`ParamMode`] for the purposes of `Type` equality.
|
||||||
/// equality. `Implicit` and `Own` are treated as the same mode
|
/// `Implicit` and `Own` are treated as the same mode; `Borrow` is
|
||||||
/// throughout the 18-series; `Borrow` is distinct. This keeps
|
/// distinct. This keeps pre-annotation fixtures (whose fn-types
|
||||||
/// pre-18a fixtures (whose fn-types serialize `Implicit`) compatible
|
/// serialize `Implicit`) compatible with newly-written fixtures
|
||||||
/// with newly-written 18a fixtures that mark the same fn-type
|
/// that mark the same fn-type explicitly with `(own T)`.
|
||||||
/// explicitly with `(own T)`.
|
|
||||||
fn mode_eq(a: &ParamMode, b: &ParamMode) -> bool {
|
fn mode_eq(a: &ParamMode, b: &ParamMode) -> bool {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(ParamMode::Borrow, ParamMode::Borrow) => true,
|
(ParamMode::Borrow, ParamMode::Borrow) => true,
|
||||||
@@ -831,7 +832,7 @@ fn mode_eq(a: &ParamMode, b: &ParamMode) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 18a: equality of two `param_modes` slices, robust to the
|
/// Equality of two `param_modes` slices, robust to the
|
||||||
/// "elided when all-implicit" representation used by typechecker /
|
/// "elided when all-implicit" representation used by typechecker /
|
||||||
/// desugar / codegen sites that construct fn-types with
|
/// desugar / codegen sites that construct fn-types with
|
||||||
/// `param_modes: vec![]`. Both slices are normalised to "implicit
|
/// `param_modes: vec![]`. Both slices are normalised to "implicit
|
||||||
@@ -897,9 +898,10 @@ impl Eq for Type {}
|
|||||||
|
|
||||||
/// Serde helper for `#[serde(skip_serializing_if = "is_false")]`.
|
/// Serde helper for `#[serde(skip_serializing_if = "is_false")]`.
|
||||||
///
|
///
|
||||||
/// Used by [`Term::App::tail`] and [`Term::Do::tail`] (Iter 14e) so the
|
/// Used by [`Term::App::tail`] and [`Term::Do::tail`] so the `tail`
|
||||||
/// `tail` flag is omitted from the canonical JSON whenever it is false,
|
/// flag is omitted from the canonical JSON whenever it is false,
|
||||||
/// preserving bit-identical hashes for every pre-14e definition.
|
/// preserving bit-identical hashes for every fixture that does not
|
||||||
|
/// carry the flag.
|
||||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
fn is_false(b: &bool) -> bool {
|
fn is_false(b: &bool) -> bool {
|
||||||
!*b
|
!*b
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub fn to_bytes<T: serde::Serialize>(value: &T) -> Vec<u8> {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22b.1: 16-hex-char hash of a [`crate::ast::Type`] in isolation.
|
/// 16-hex-char hash of a [`crate::ast::Type`] in isolation.
|
||||||
///
|
///
|
||||||
/// Used by [`crate::workspace::Registry`] to key `InstanceDef`s by
|
/// Used by [`crate::workspace::Registry`] to key `InstanceDef`s by
|
||||||
/// their target type. Parallel in shape to [`crate::def_hash`] and
|
/// their target type. Parallel in shape to [`crate::def_hash`] and
|
||||||
@@ -138,7 +138,7 @@ mod tests {
|
|||||||
assert!(!s.contains('\n'));
|
assert!(!s.contains('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iter 22-floats.1 RED: a `Literal::Float` carries the IEEE-754
|
/// A `Literal::Float` carries the IEEE-754
|
||||||
/// binary64 bit pattern as a `u64`; canonical JSON encodes it as
|
/// binary64 bit pattern as a `u64`; canonical JSON encodes it as
|
||||||
/// `{"bits":"<16-lowercase-hex>","kind":"float"}` — string path,
|
/// `{"bits":"<16-lowercase-hex>","kind":"float"}` — string path,
|
||||||
/// NOT through `serde_json::Number` (which is not bit-stable for
|
/// NOT through `serde_json::Number` (which is not bit-stable for
|
||||||
|
|||||||
@@ -29,14 +29,14 @@
|
|||||||
//! re-evaluate effectful scrutinees per arm).
|
//! re-evaluate effectful scrutinees per arm).
|
||||||
//! 3. Build a chain of single-level matches via
|
//! 3. Build a chain of single-level matches via
|
||||||
//! `build_chain(s_var, arms, default)` where `default` is the
|
//! `build_chain(s_var, arms, default)` where `default` is the
|
||||||
//! polymorphic bottom builtin `__unreachable__` (`forall a. a`,
|
//! polymorphic bottom builtin `__unreachable__` (`forall a. a`)
|
||||||
//! Iter 16d) — codegen lowers it to LLVM `unreachable`. Valid
|
//! — codegen lowers it to LLVM `unreachable`. Valid programs
|
||||||
//! programs never reach it because the checker requires
|
//! never reach it because the checker requires exhaustiveness
|
||||||
//! exhaustiveness (the catch-all arm dominates the chain).
|
//! (the catch-all arm dominates the chain). An earlier shape used
|
||||||
//! Pre-16d the default was a `Unit` literal, which forced any
|
//! a `Unit` literal as the default, which forced any match whose
|
||||||
//! match whose arms returned a non-Unit type to carry a
|
//! arms returned a non-Unit type to carry a synthetic `_` arm
|
||||||
//! synthetic `_` arm dominating the terminator. The polymorphic
|
//! dominating the terminator. The polymorphic `__unreachable__`
|
||||||
//! `__unreachable__` removes that workaround.
|
//! removes that workaround.
|
||||||
//! 4. Each arm is lowered via `desugar_one_arm`:
|
//! 4. Each arm is lowered via `desugar_one_arm`:
|
||||||
//! - `Pattern::Wild` → arm body (catch-all; later arms are dropped).
|
//! - `Pattern::Wild` → arm body (catch-all; later arms are dropped).
|
||||||
//! - `Pattern::Var { name }` → `Let { name = scrutinee_var; body }`.
|
//! - `Pattern::Var { name }` → `Let { name = scrutinee_var; body }`.
|
||||||
|
|||||||
@@ -107,11 +107,11 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||||||
/// loading fails with [`Error::SchemaMismatch`].
|
/// loading fails with [`Error::SchemaMismatch`].
|
||||||
pub const SCHEMA: &str = "ailang/v0";
|
pub const SCHEMA: &str = "ailang/v0";
|
||||||
|
|
||||||
/// Iter 20f: complete LLM-targeted specification of Form-A — the
|
/// Complete LLM-targeted specification of Form-A — the canonical
|
||||||
/// canonical authoring surface (Decision 6). Embedded verbatim into
|
/// authoring surface (see `design/contracts/authoring-surface.md`).
|
||||||
/// any prompt that asks an LLM to produce or edit AILang code; in
|
/// Embedded verbatim into any prompt that asks an LLM to produce or
|
||||||
/// particular, `ail merge-prose` includes it in the round-trip
|
/// edit AILang code; in particular, `ail merge-prose` includes it in
|
||||||
/// prompt template. The string is the raw bytes of
|
/// the round-trip prompt template. The string is the raw bytes of
|
||||||
/// `specs/form_a.md`, co-located with this crate so the spec sits
|
/// `specs/form_a.md`, co-located with this crate so the spec sits
|
||||||
/// next to the AST it describes; a drift test
|
/// next to the AST it describes; a drift test
|
||||||
/// (`tests/spec_drift.rs`) walks every AST enum variant and asserts
|
/// (`tests/spec_drift.rs`) walks every AST enum variant and asserts
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 20f: drift detection between the AST and `specs/form_a.md`.
|
//! Drift detection between the AST and `specs/form_a.md`.
|
||||||
//!
|
//!
|
||||||
//! The spec is hand-curated, but it cannot silently fall behind the
|
//! The spec is hand-curated, but it cannot silently fall behind the
|
||||||
//! language. Every AST enum (`Term`, `Pattern`, `Type`, `Def`, `Literal`,
|
//! language. Every AST enum (`Term`, `Pattern`, `Type`, `Def`, `Literal`,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Iter 20a snapshot tests.
|
//! Prose-projection snapshot tests.
|
||||||
//!
|
//!
|
||||||
//! For each `examples/<name>.ail`, the renderer's output is compared
|
//! For each `examples/<name>.ail`, the renderer's output is compared
|
||||||
//! against the committed `examples/<name>.prose.txt`. The snapshot
|
//! against the committed `examples/<name>.prose.txt`. The snapshot
|
||||||
|
|||||||
+3
-3
@@ -35,9 +35,9 @@ evolving in lockstep with the language:
|
|||||||
`crates/ailang-codegen`): AST, type system, codegen.
|
`crates/ailang-codegen`): AST, type system, codegen.
|
||||||
- **Surface forms** (`crates/ailang-surface`, `crates/ailang-prose`):
|
- **Surface forms** (`crates/ailang-surface`, `crates/ailang-prose`):
|
||||||
the LLM-facing renderings of a module. `ailang-surface`
|
the LLM-facing renderings of a module. `ailang-surface`
|
||||||
is the lossless Form-A printer/parser — the canonical authoring
|
is the lossless Form-A printer/parser — the canonical
|
||||||
surface fixed by Decision 6, with a round-trip property
|
[authoring surface](contracts/authoring-surface.md), with a
|
||||||
`parse ∘ print = id` gating every release. `ailang-prose`
|
round-trip property `parse ∘ print = id` gating every release. `ailang-prose`
|
||||||
is the lossy Form-B projection — human-readable prose for review and
|
is the lossy Form-B projection — human-readable prose for review and
|
||||||
edit, with no parser; re-integration goes through the
|
edit, with no parser; re-integration goes through the
|
||||||
LLM-mediator round-trip documented in `docs/PROSE_ROUNDTRIP.md`.
|
LLM-mediator round-trip documented in `docs/PROSE_ROUNDTRIP.md`.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Authoring surface
|
# Authoring surface
|
||||||
|
|
||||||
## Decision 6: authoring surface
|
## Authoring surface
|
||||||
|
|
||||||
Form (A) is implemented as
|
Form (A) is implemented as
|
||||||
the `ailang-surface` crate (parser + printer). Form-A is
|
the `ailang-surface` crate (parser + printer). Form-A is
|
||||||
@@ -22,7 +22,7 @@ one projection among potentially many. Concretely:
|
|||||||
addressed representation of a module. All hashing, content-
|
addressed representation of a module. All hashing, content-
|
||||||
addressing, cross-module references, and typecheck/codegen input
|
addressing, cross-module references, and typecheck/codegen input
|
||||||
flow through the JSON-AST. **No new hashable form is introduced.**
|
flow through the JSON-AST. **No new hashable form is introduced.**
|
||||||
- The textual surface (form A, this Decision) is the **AI authoring
|
- The textual surface (form A, this contract) is the **AI authoring
|
||||||
projection**: optimised for me producing programs token-efficiently
|
projection**: optimised for me producing programs token-efficiently
|
||||||
and for foreign LLMs producing programs from a spec. It is not
|
and for foreign LLMs producing programs from a spec. It is not
|
||||||
optimised for human authors and does not need to be human-pleasant.
|
optimised for human authors and does not need to be human-pleasant.
|
||||||
@@ -55,11 +55,11 @@ the AST and remain projection-agnostic.
|
|||||||
shape. The full bijection between `.ail.json` and `.ail` (both
|
shape. The full bijection between `.ail.json` and `.ail` (both
|
||||||
directions, BLAKE3-stable hashing, Float-bits-hex encoding,
|
directions, BLAKE3-stable hashing, Float-bits-hex encoding,
|
||||||
workspace-CI enforcement points) is anchored as the top-level
|
workspace-CI enforcement points) is anchored as the top-level
|
||||||
[Roundtrip Invariant](roundtrip-invariant.md) — this constraint records that Decision
|
[Roundtrip Invariant](roundtrip-invariant.md) — this constraint
|
||||||
6's surface-design choice must satisfy that invariant; the
|
records that the surface-design choice must satisfy that
|
||||||
invariant itself lives at top level because the property is
|
invariant; the invariant itself lives at top level because the
|
||||||
load-bearing on the language identity, not on this Decision's
|
property is load-bearing on the language identity, not on this
|
||||||
surface-design rationale.
|
file's surface-design rationale.
|
||||||
3. **No external symbols.** ASCII only. No Greek (`∀`), no arrows
|
3. **No external symbols.** ASCII only. No Greek (`∀`), no arrows
|
||||||
(`→`), no subscripts. Reasoning: I substitute mojibake for
|
(`→`), no subscripts. Reasoning: I substitute mojibake for
|
||||||
non-ASCII characters under context pressure; foreign LLMs vary
|
non-ASCII characters under context pressure; foreign LLMs vary
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ narrative — defaults, superclasses, diagnostics — lives in
|
|||||||
"params": ["<id>"...], // names bound in body, in type.params order
|
"params": ["<id>"...], // names bound in body, in type.params order
|
||||||
"body": Term,
|
"body": Term,
|
||||||
"doc": "<optional string>",
|
"doc": "<optional string>",
|
||||||
"export": "<optional C symbol>", // omitted when absent (hash-stable when omitted); see §"Embedding ABI"
|
"export": "<optional C symbol>", // omitted when absent (hash-stable when omitted); embedding-ABI surface — see prose below
|
||||||
"suppress": [Suppress...] // omitted when empty
|
"suppress": [Suppress...] // omitted when empty
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,11 +65,11 @@ narrative — defaults, superclasses, diagnostics — lives in
|
|||||||
"drop-iterative": true // opt-in; omitted when false (hash-stable when omitted)
|
"drop-iterative": true // opt-in; omitted when false (hash-stable when omitted)
|
||||||
}
|
}
|
||||||
|
|
||||||
// class (typeclass declaration; see Decision 11)
|
// class (typeclass declaration; narrative in contracts/typeclasses.md)
|
||||||
{ "kind": "class",
|
{ "kind": "class",
|
||||||
"name": "<id>", // class name (e.g. "Show")
|
"name": "<id>", // class name (e.g. "Show")
|
||||||
"param": "<id>", // single class parameter, kind *
|
"param": "<id>", // single class parameter, kind *
|
||||||
"superclass": null, // or { "class": "<id>", "type": "<param>" } — "class": canonical form (bare for same-module, "<module>.<Class>" for cross-module; see §"Class names" / mq.1)
|
"superclass": null, // or { "class": "<id>", "type": "<param>" } — "class": canonical form (bare for same-module, "<module>.<Class>" for cross-module)
|
||||||
"methods": [
|
"methods": [
|
||||||
{ "name": "<id>",
|
{ "name": "<id>",
|
||||||
"type": Type, // FnSig over the class param
|
"type": Type, // FnSig over the class param
|
||||||
@@ -80,9 +80,9 @@ narrative — defaults, superclasses, diagnostics — lives in
|
|||||||
"doc": "<optional string>"
|
"doc": "<optional string>"
|
||||||
}
|
}
|
||||||
|
|
||||||
// instance (typeclass instance; see Decision 11)
|
// instance (typeclass instance; narrative in contracts/typeclasses.md)
|
||||||
{ "kind": "instance",
|
{ "kind": "instance",
|
||||||
"class": "<id>", // class being instantiated; canonical form (bare for same-module, "<module>.<Class>" for cross-module; see §"Class names" / mq.1)
|
"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)
|
"type": Type, // concrete type expression (never the class param)
|
||||||
"methods": [
|
"methods": [
|
||||||
{ "name": "<id>", "body": Term }
|
{ "name": "<id>", "body": Term }
|
||||||
@@ -215,13 +215,14 @@ metadata is defined and gated there as well.
|
|||||||
```jsonc
|
```jsonc
|
||||||
// Type-constructor application. `args` omitted when empty
|
// Type-constructor application. `args` omitted when empty
|
||||||
// (hash-stable when omitted, for non-parameterised cases like Int, Bool, ...).
|
// (hash-stable when omitted, for non-parameterised cases like Int, Bool, ...).
|
||||||
{ "k": "con", "name": "<id>", "args": [Type...] } // "name": canonical form (bare for same-module / primitives, "<module>.<TypeName>" for cross-module; see §"Type::Con name scoping" / ct.1)
|
{ "k": "con", "name": "<id>", "args": [Type...] } // "name": canonical form (bare for same-module / primitives, "<module>.<TypeName>" for cross-module)
|
||||||
|
|
||||||
// Function type. Decision 10 added paramModes/retMode as
|
// Function type. paramModes/retMode are metadata on Type::Fn —
|
||||||
// metadata on Type::Fn — they are NOT separate Type variants, so every
|
// they are NOT separate Type variants, so every existing match-arm
|
||||||
// existing match-arm in the typechecker (unify, occurs, apply) keeps
|
// in the typechecker (unify, occurs, apply) keeps working.
|
||||||
// working. `paramModes` omitted when every entry is "implicit";
|
// `paramModes` omitted when every entry is "implicit"; `retMode`
|
||||||
// `retMode` omitted when "implicit" (hash-stable when omitted).
|
// omitted when "implicit" (hash-stable when omitted). Full mode
|
||||||
|
// contract lives in contracts/memory-model.md.
|
||||||
{ "k": "fn",
|
{ "k": "fn",
|
||||||
"params": [Type...],
|
"params": [Type...],
|
||||||
"paramModes": [ParamMode...],
|
"paramModes": [ParamMode...],
|
||||||
@@ -232,14 +233,16 @@ metadata is defined and gated there as well.
|
|||||||
{ "k": "var", "name": "<id>" }
|
{ "k": "var", "name": "<id>" }
|
||||||
|
|
||||||
// Top-level polymorphism only. `constraints` carries class
|
// Top-level polymorphism only. `constraints` carries class
|
||||||
// constraints (Decision 11); omitted when empty (hash-stable when omitted).
|
// constraints (narrative in contracts/typeclasses.md); omitted when
|
||||||
|
// empty (hash-stable when omitted).
|
||||||
{ "k": "forall",
|
{ "k": "forall",
|
||||||
"vars": ["<id>"...],
|
"vars": ["<id>"...],
|
||||||
"constraints": [{ "class": "<id>", "type": "<id>" }, ...], // "class": canonical form (bare for same-module, "<module>.<Class>" for cross-module; see §"Class names" / mq.1)
|
"constraints": [{ "class": "<id>", "type": "<id>" }, ...], // "class": canonical form (bare for same-module, "<module>.<Class>" for cross-module)
|
||||||
"body": Type }
|
"body": Type }
|
||||||
```
|
```
|
||||||
|
|
||||||
**`ParamMode`** (Decision 10):
|
**`ParamMode`** (full contract in
|
||||||
|
[memory model](memory-model.md)):
|
||||||
|
|
||||||
```
|
```
|
||||||
"implicit" — unannotated / back-compat. Treated as `own` by the typechecker.
|
"implicit" — unannotated / back-compat. Treated as `own` by the typechecker.
|
||||||
|
|||||||
@@ -36,16 +36,16 @@ A proposed feature ships only if all three hold:
|
|||||||
discouraged. A documentation note is not a reshape; the
|
discouraged. A documentation note is not a reshape; the
|
||||||
discriminator is whether the wrong code fails to typecheck, not
|
discriminator is whether the wrong code fails to typecheck, not
|
||||||
whether a guideline advises against it. Worked example: a bare
|
whether a guideline advises against it. Worked example: a bare
|
||||||
`while` over mutable state would pass clauses 1 and 2 yet fail
|
`while` over mutable state would pass criteria 1 and 2 yet fail
|
||||||
clause 3 (it reinstates iterated-mutable-state reasoning the pure
|
criterion 3 (it reinstates iterated-mutable-state reasoning the
|
||||||
core exists to remove); a hypothetical "all repetition is either
|
pure core exists to remove); a hypothetical "all repetition is
|
||||||
structurally-decreasing recursion over an acyclic ADT or an
|
either structurally-decreasing recursion over an acyclic ADT or
|
||||||
explicit named loop" iteration story would, *if it could be built
|
an explicit named loop" iteration story would, *if it could be
|
||||||
without a documented-unenforced precondition*, pass all three —
|
built without a documented-unenforced precondition*, pass all
|
||||||
and the fact that the 2026-05 attempt could not (it forced a
|
three — and the fact that the 2026-05 attempt could not (it
|
||||||
silent-divergence precondition; see
|
forced a silent-divergence precondition; see
|
||||||
`docs/specs/2026-05-16-iteration-discipline-revert.md`) is itself
|
`docs/specs/2026-05-16-iteration-discipline-revert.md`) is itself
|
||||||
the clause-3 mechanism working as intended.
|
the bug-class-reintroduction discriminator working as intended.
|
||||||
|
|
||||||
This is the positive complement to the CLAUDE.md rule that
|
This is the positive complement to the CLAUDE.md rule that
|
||||||
implementation effort is not a rationale: cost is not a reason *for* a
|
implementation effort is not a rationale: cost is not a reason *for* a
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ no `f32` variant. The runtime / codegen contract:
|
|||||||
`0x7ff8000000000000` on one target and a different qNaN on
|
`0x7ff8000000000000` on one target and a different qNaN on
|
||||||
another.
|
another.
|
||||||
- The textual rendering of NaN through `float_to_str` (the runtime
|
- The textual rendering of NaN through `float_to_str` (the runtime
|
||||||
C helper that backs `instance Show Float` and, post-iter-rpe.1,
|
C helper that backs `instance Show Float` and every Float-typed
|
||||||
every Float-typed `print` call). The libc `printf("%g", nan)`
|
`print` call). The libc `printf("%g", nan)`
|
||||||
glue used by `float_to_str` is permitted to emit `nan` / `-nan`
|
glue used by `float_to_str` is permitted to emit `nan` / `-nan`
|
||||||
/ `NaN` etc. depending on libc version and the NaN's sign bit;
|
/ `NaN` etc. depending on libc version and the NaN's sign bit;
|
||||||
AILang does not normalise this, since the prose / surface-print
|
AILang does not normalise this, since the prose / surface-print
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ Two things never belong in a contract or model file:
|
|||||||
|
|
||||||
A cross-reference that does belong stays: it is a formal,
|
A cross-reference that does belong stays: it is a formal,
|
||||||
file-relative Markdown link into the durable tier (`design/` or
|
file-relative Markdown link into the durable tier (`design/` or
|
||||||
source), enforced by
|
source), enforced by the body-link durability gate in
|
||||||
[`design_index_pin.rs`](../../crates/ailang-core/tests/design_index_pin.rs)
|
[`design_index_pin.rs`](../../crates/ailang-core/tests/design_index_pin.rs).
|
||||||
clause-5. A reference that cannot be expressed as such a link is,
|
A reference that cannot be expressed as such a link is, by that
|
||||||
by that fact, the history-or-rationale prose the rule above removes.
|
fact, the history-or-rationale prose the rule above removes.
|
||||||
|
|
||||||
The single legitimate exception is a present-tense reserved or
|
The single legitimate exception is a present-tense reserved or
|
||||||
deliberately-excluded claim that is explicitly and correctly
|
deliberately-excluded claim that is explicitly and correctly
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
The four constraints below are necessary preconditions for RC to
|
The four constraints below are necessary preconditions for RC to
|
||||||
be sound and complete *without* a cycle-collector backstop. They
|
be sound and complete *without* a cycle-collector backstop. They
|
||||||
are not new — AILang already satisfies all four — but Decision 10
|
are not new — AILang already satisfies all four — but the
|
||||||
makes them load-bearing rather than incidental:
|
[memory model](memory-model.md) makes them load-bearing rather
|
||||||
|
than incidental:
|
||||||
|
|
||||||
1. **Strict evaluation.** Every `Term::App` (see
|
1. **Strict evaluation.** Every `Term::App` (see
|
||||||
[Data model](data-model.md)) argument is fully
|
[Data model](data-model.md)) argument is fully
|
||||||
@@ -33,8 +34,8 @@ These constraints are the precondition for the
|
|||||||
[memory model](memory-model.md): a sound RC implementation without
|
[memory model](memory-model.md): a sound RC implementation without
|
||||||
a cycle-collector backstop requires the reference graph to be a
|
a cycle-collector backstop requires the reference graph to be a
|
||||||
DAG, which constraint 4 establishes from 1–3. They are also the
|
DAG, which constraint 4 establishes from 1–3. They are also the
|
||||||
load-bearing structural reason behind several
|
load-bearing structural reason behind several rejections under the
|
||||||
[feature-acceptance](feature-acceptance.md) clause-3 rejections
|
[feature-acceptance](feature-acceptance.md) bug-class-reintroduction
|
||||||
(notably iterated-mutable-state reasoning).
|
discriminator (notably iterated-mutable-state reasoning).
|
||||||
|
|
||||||
Ratified by: `crates/ailang-check/src/uniqueness.rs` (in-source mod tests).
|
Ratified by: `crates/ailang-check/src/uniqueness.rs` (in-source mod tests).
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ schema). The substantive reasons for per-position metadata over a
|
|||||||
`Type` would let the schema express forms like
|
`Type` would let the schema express forms like
|
||||||
`(con List (borrow Int))` — syntactically possible, semantically
|
`(con List (borrow Int))` — syntactically possible, semantically
|
||||||
meaningless (you cannot separately own/borrow a list element
|
meaningless (you cannot separately own/borrow a list element
|
||||||
from the list it lives in). Decision 1 is "schema = data,
|
from the list it lives in). The
|
||||||
|
[canonical-schema principle](data-model.md) is "schema = data,
|
||||||
schema permits exactly what is meaningful"; per-position
|
schema permits exactly what is meaningful"; per-position
|
||||||
metadata is the option that holds that line.
|
metadata is the option that holds that line.
|
||||||
- **Compositional clarity.** A `Type` value's identity should
|
- **Compositional clarity.** A `Type` value's identity should
|
||||||
@@ -62,7 +63,7 @@ identical: `param_modes` is skipped when every entry is
|
|||||||
`Implicit`, `ret_mode` is skipped when `Implicit`. Existing
|
`Implicit`, `ret_mode` is skipped when `Implicit`. Existing
|
||||||
modules emit the same bytes as before.
|
modules emit the same bytes as before.
|
||||||
|
|
||||||
**Type::Con name scoping (canonical form, since ct.1).** Within a
|
**Type::Con name scoping (canonical form).** Within a
|
||||||
`.ail.json`, a `Type::Con.name` is interpreted relative to the
|
`.ail.json`, a `Type::Con.name` is interpreted relative to the
|
||||||
file's top-level `"name"` field (the owning module). Bare names
|
file's top-level `"name"` field (the owning module). Bare names
|
||||||
(no `.`) refer to a TypeDef in the owning module's own `defs`.
|
(no `.`) refer to a TypeDef in the owning module's own `defs`.
|
||||||
@@ -75,15 +76,16 @@ qualified references whose owner is unknown are also a violation
|
|||||||
(`WorkspaceLoadError::BadCrossModuleTypeRef`). The same rule
|
(`WorkspaceLoadError::BadCrossModuleTypeRef`). The same rule
|
||||||
applies to `Term::Ctor.type_name`.
|
applies to `Term::Ctor.type_name`.
|
||||||
|
|
||||||
Class names follow the canonical-form rule (mq.1): bare for
|
Class names follow the same canonical-form rule: bare for
|
||||||
same-module references, `<module>.<Class>` for cross-module
|
same-module references, `<module>.<Class>` for cross-module
|
||||||
references — symmetric to `Type::Con.name`'s rule from ct.1.
|
references — symmetric to `Type::Con.name`'s rule above.
|
||||||
Three schema fields carry class references in this form:
|
Three schema fields carry class references in this form:
|
||||||
`InstanceDef.class`, `Constraint.class`, and `SuperclassRef.class`.
|
`InstanceDef.class`, `Constraint.class`, and `SuperclassRef.class`.
|
||||||
`ClassDef.name` itself stays bare (defining-site context, like
|
`ClassDef.name` itself stays bare (defining-site context, like
|
||||||
`TypeDef.name`).
|
`TypeDef.name`).
|
||||||
|
|
||||||
Method dispatch is type-driven post-mq.3 (see [Method dispatch](method-dispatch.md)):
|
Method dispatch is type-driven (see
|
||||||
|
[Method dispatch](method-dispatch.md)):
|
||||||
synth resolves a `Term::Var { name: "show" }` by consulting
|
synth resolves a `Term::Var { name: "show" }` by consulting
|
||||||
the workspace's method-to-candidate-class index, filtering by
|
the workspace's method-to-candidate-class index, filtering by
|
||||||
argument type (concrete) or by declared constraint (rigid-var), and
|
argument type (concrete) or by declared constraint (rigid-var), and
|
||||||
@@ -161,24 +163,24 @@ Skipped from serialisation when empty so existing fixtures keep
|
|||||||
bit-identical canonical-JSON hashes (regression-pinned by
|
bit-identical canonical-JSON hashes (regression-pinned by
|
||||||
`iter19b_empty_suppress_preserves_pre_19b_hashes` and
|
`iter19b_empty_suppress_preserves_pre_19b_hashes` and
|
||||||
`iter19b_schema_extension_preserves_pre_19b_hashes`).
|
`iter19b_schema_extension_preserves_pre_19b_hashes`).
|
||||||
The canonical-form tightening in ct.1 shifted the hashes of two
|
The canonical-form tightening for `Type::Con.name` shifted the
|
||||||
cross-module fixtures (`ordering_match.ail.json` and
|
hashes of two cross-module fixtures (`ordering_match.ail.json` and
|
||||||
`test_22b1_dup_a.ail.json`); all intra-module fixtures, including
|
`test_22b1_dup_a.ail.json`); all intra-module fixtures, including
|
||||||
the regression-pinned `sum.ail.json` and `list.ail.json`, remain
|
the regression-pinned `sum.ail.json` and `list.ail.json`, remain
|
||||||
bit-identical. The new pins are
|
bit-identical. The new pins are
|
||||||
`ct4_migrated_fixtures_have_canonical_form_hashes` (locks the
|
`ct4_migrated_fixtures_have_canonical_form_hashes` (locks the
|
||||||
post-migration hashes) and
|
post-migration hashes) and
|
||||||
`ct4_unmigrated_fixtures_remain_bit_identical` (re-asserts the
|
`ct4_unmigrated_fixtures_remain_bit_identical` (re-asserts the
|
||||||
existing 13a/19b/22b.1 hashes still hold).
|
pre-tightening hashes still hold).
|
||||||
|
|
||||||
## Advisory diagnostics
|
## Advisory diagnostics
|
||||||
|
|
||||||
The advisory-diagnostics arc introduces the language's first
|
The advisory-diagnostics arc introduces the language's first
|
||||||
**advisory** typechecker diagnostic and the suppression mechanism
|
**advisory** typechecker diagnostic and the suppression mechanism
|
||||||
that goes with it. Decision 10's mandatory-annotation rule is
|
that goes with it. The mandatory-annotation rule of this memory
|
||||||
unchanged: `param_modes` and `ret_mode` remain author-required;
|
model is unchanged: `param_modes` and `ret_mode` remain
|
||||||
the typechecker does not infer them. What's new is feedback when
|
author-required; the typechecker does not infer them. What's new
|
||||||
an authored annotation is *stricter than necessary*.
|
is feedback when an authored annotation is *stricter than necessary*.
|
||||||
|
|
||||||
**The lint: `over-strict-mode`.** Fires on a
|
**The lint: `over-strict-mode`.** Fires on a
|
||||||
fn-param `p` annotated `(own T)` when:
|
fn-param `p` annotated `(own T)` when:
|
||||||
|
|||||||
@@ -6,14 +6,16 @@ The class-method resolution rule that pairs with the
|
|||||||
[typeclasses](typeclasses.md) contract; the `Show`/`Eq`/`Ord` instances
|
[typeclasses](typeclasses.md) contract; the `Show`/`Eq`/`Ord` instances
|
||||||
the rule dispatches against live in [prelude classes](prelude-classes.md).
|
the rule dispatches against live in [prelude classes](prelude-classes.md).
|
||||||
|
|
||||||
Post-mq.3, dispatch is two-mode:
|
Dispatch is two-mode:
|
||||||
|
|
||||||
**Polymorphic call sites** (inside a fn body with `forall` +
|
**Polymorphic call sites** (inside a fn body with `forall` +
|
||||||
constraint set): the constraint names the class via the qualified
|
constraint set): the constraint names the class via the qualified
|
||||||
`Constraint.class` field (canonical-form per mq.1). Synth's
|
`Constraint.class` field (canonical form: bare for same-module,
|
||||||
residual carries the class name directly; constraint-discharge at
|
`<module>.<Class>` for cross-module; see
|
||||||
fn-body-end matches against the workspace registry by
|
[memory model](memory-model.md) for the canonical-form rule).
|
||||||
`(class, type_hash)` key.
|
Synth's residual carries the class name directly;
|
||||||
|
constraint-discharge at fn-body-end matches against the workspace
|
||||||
|
registry by `(class, type_hash)` key.
|
||||||
|
|
||||||
**Monomorphic call sites**: synth consults the workspace-flat
|
**Monomorphic call sites**: synth consults the workspace-flat
|
||||||
`Env.method_to_candidate_classes: BTreeMap<MethodName,
|
`Env.method_to_candidate_classes: BTreeMap<MethodName,
|
||||||
@@ -42,7 +44,7 @@ dispatch rule:
|
|||||||
The `method_to_candidate_classes` index is the load-bearing data
|
The `method_to_candidate_classes` index is the load-bearing data
|
||||||
structure for this routing — its construction in `build_check_env`
|
structure for this routing — its construction in `build_check_env`
|
||||||
inverts the per-module `class_methods` maps (themselves tuple-keyed
|
inverts the per-module `class_methods` maps (themselves tuple-keyed
|
||||||
by `(qualified-class, method)` post-mq.3) to a workspace-flat
|
by `(qualified-class, method)`) to a workspace-flat
|
||||||
method-name-to-class-set map.
|
method-name-to-class-set map.
|
||||||
|
|
||||||
Class-fn collisions resolve at the call site, not at workspace load
|
Class-fn collisions resolve at the call site, not at workspace load
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ corresponding runtime primitive (`int_to_str`, `bool_to_str`,
|
|||||||
heap-Str primitives); no codegen intercept is required. The
|
heap-Str primitives); no codegen intercept is required. The
|
||||||
polymorphic helper `print : forall a. Show a => a -> () !IO` has body
|
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
|
`\x -> let s = show x in do io/print_str s` (explicit let-binder for
|
||||||
heap-Str RC discipline per eob.1 Str carve-out). The let-binder is
|
heap-Str RC discipline per the Str carve-out in
|
||||||
|
[Str ABI](str-abi.md)). The let-binder is
|
||||||
structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`.
|
structurally pinned by `crates/ail/tests/print_mono_body_shape.rs`.
|
||||||
Routing through `print` is the path for non-`Str` primitives;
|
Routing through `print` is the path for non-`Str` primitives;
|
||||||
`io/print_str` is the only built-in direct-output effect-op.
|
`io/print_str` is the only built-in direct-output effect-op.
|
||||||
|
|||||||
@@ -85,15 +85,15 @@ never by relaxing the test.
|
|||||||
### Why this is anchored at top level
|
### Why this is anchored at top level
|
||||||
|
|
||||||
The invariant is a property of the language identity, not of any
|
The invariant is a property of the language identity, not of any
|
||||||
one surface-design Decision. Decision 6 introduces the `.ail`
|
one surface-design choice. The [authoring surface](authoring-surface.md)
|
||||||
surface and lists round-trip-as-property as one of its
|
introduces the `.ail` surface and lists round-trip-as-property as
|
||||||
constraints, but the property is load-bearing for every
|
one of its constraints, but the property is load-bearing for every
|
||||||
downstream concern that treats the two forms as exchangeable:
|
downstream concern that treats the two forms as exchangeable:
|
||||||
content-addressed hashing, the LLM-author's choice of authoring
|
content-addressed hashing, the LLM-author's choice of authoring
|
||||||
form, the integrity of fixture cross-references, and the
|
form, the integrity of fixture cross-references, and the
|
||||||
prerequisite for empirical cross-model authoring-form studies.
|
prerequisite for empirical cross-model authoring-form studies.
|
||||||
Lifting it out of Decision 6 makes the property quotable on its
|
Lifting it out of the authoring-surface contract makes the property
|
||||||
own and reviewable by the architect agent at every milestone
|
quotable on its own and reviewable by the architect agent at every
|
||||||
close.
|
milestone close.
|
||||||
|
|
||||||
Ratified by: `crates/ailang-surface/tests/round_trip.rs`.
|
Ratified by: `crates/ailang-surface/tests/round_trip.rs`.
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ What **is** supported (and used as the smoke test for the pipeline):
|
|||||||
pair allocates either via `@GC_malloc` (escaping; Boehm-managed) or
|
pair allocates either via `@GC_malloc` (escaping; Boehm-managed) or
|
||||||
via LLVM `alloca` (non-escaping; freed at fn return). The decision
|
via LLVM `alloca` (non-escaping; freed at fn return). The decision
|
||||||
is made by an escape-analysis pre-pass over the fn body — see
|
is made by an escape-analysis pre-pass over the fn body — see
|
||||||
Decision 9's "Per-fn arena via stack `alloca`" subsection.
|
the "Per-fn arena via stack `alloca`" subsection of
|
||||||
|
[RC + uniqueness](../models/rc-uniqueness.md).
|
||||||
Boehm-only soak tests are unchanged: `examples/gc_stress.ail.json`
|
Boehm-only soak tests are unchanged: `examples/gc_stress.ail.json`
|
||||||
and `examples/std_list_stress.ail.json` still allocate via
|
and `examples/std_list_stress.ail.json` still allocate via
|
||||||
`@GC_malloc` because their boxes flow into other fns and escape.
|
`@GC_malloc` because their boxes flow into other fns and escape.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Tail calls
|
# Tail calls
|
||||||
|
|
||||||
## Decision 8: explicit, verified tail calls
|
## Explicit, verified tail calls
|
||||||
|
|
||||||
For an LLM author, recursion is the natural iteration form
|
For an LLM author, recursion is the natural iteration form
|
||||||
(`\n. if n == 0 then () else loop(n-1)` is what I reach for, not
|
(`\n. if n == 0 then () else loop(n-1)` is what I reach for, not
|
||||||
|
|||||||
@@ -74,15 +74,18 @@ exactly as for any free function.
|
|||||||
|
|
||||||
## Cross-module references in synthesised bodies
|
## Cross-module references in synthesised bodies
|
||||||
|
|
||||||
The unified mono pass (per Decision 11's milestone-23.4 reorganisation)
|
The unified mono pass (see
|
||||||
synthesises mono symbols for polymorphic free fns and class-method
|
[models/typeclasses](../models/typeclasses.md) for the
|
||||||
instances in the symbol's owner module — e.g. `print__Int` lives in
|
resolution/monomorphisation algorithm) synthesises mono symbols for
|
||||||
`prelude` (because `print` is defined in the prelude), but a
|
polymorphic free fns and class-method instances in the symbol's
|
||||||
user-ADT call site `print (MkIntBox 7)` causes synthesis of
|
owner module — e.g. `print__Int` lives in `prelude` (because `print`
|
||||||
`prelude.print__IntBox` whose body references
|
is defined in the prelude), but a user-ADT call site
|
||||||
`show_user_adt.show__IntBox` (the user instance lives in the
|
`print (MkIntBox 7)` causes synthesis of `prelude.print__IntBox`
|
||||||
user-defining module per Decision 11 coherence). The synthesised body
|
whose body references `show_user_adt.show__IntBox` (the user
|
||||||
crosses a module boundary the source template did not.
|
instance lives in the user-defining module per the
|
||||||
|
orphan-free-coherence rule documented in
|
||||||
|
[models/typeclasses](../models/typeclasses.md)). The synthesised
|
||||||
|
body crosses a module boundary the source template did not.
|
||||||
|
|
||||||
Three invariants make this work:
|
Three invariants make this work:
|
||||||
|
|
||||||
@@ -127,7 +130,7 @@ Three invariants make this work:
|
|||||||
workspace registry; if no instance satisfies the residual at the
|
workspace registry; if no instance satisfies the residual at the
|
||||||
unified concrete type, the `NoInstance` diagnostic fires at
|
unified concrete type, the `NoInstance` diagnostic fires at
|
||||||
typecheck (correctly), not at codegen (confusingly). Without the
|
typecheck (correctly), not at codegen (confusingly). Without the
|
||||||
residual push, milestone-23-shape negative cases (e.g. `print f`
|
residual push, polymorphic-helper negative cases (e.g. `print f`
|
||||||
where `f : Int -> Int`) silently typecheck and surface as
|
where `f : Int -> Int`) silently typecheck and surface as
|
||||||
`unknown variable: show` from codegen instead of the right
|
`unknown variable: show` from codegen instead of the right
|
||||||
typecheck-phase NoInstance Show.
|
typecheck-phase NoInstance Show.
|
||||||
@@ -211,17 +214,17 @@ module.
|
|||||||
- `MissingConstraint` — body's residual constraint is not covered
|
- `MissingConstraint` — body's residual constraint is not covered
|
||||||
by declared (and superclass-expanded) constraints.
|
by declared (and superclass-expanded) constraints.
|
||||||
- `NoInstance` — fully concrete constraint has no registry entry.
|
- `NoInstance` — fully concrete constraint has no registry entry.
|
||||||
mq.2 adds an optional `candidate_classes` field surfacing the
|
Carries an optional `candidate_classes` field surfacing the
|
||||||
multi-candidate set when the bare-method dispatch path's filter
|
multi-candidate set when the bare-method dispatch path's filter
|
||||||
collapses to zero registry survivors.
|
collapses to zero registry survivors.
|
||||||
- `AmbiguousMethodResolution` (mq.2) — a monomorphic `Term::Var`
|
- `AmbiguousMethodResolution` — a monomorphic `Term::Var`
|
||||||
call site survives both type-driven and constraint-driven filters
|
call site survives both type-driven and constraint-driven filters
|
||||||
with more than one candidate class. LLM-author writes the explicit
|
with more than one candidate class. LLM-author writes the explicit
|
||||||
qualifier form `<module>.<Class>.<method>` to disambiguate.
|
qualifier form `<module>.<Class>.<method>` to disambiguate.
|
||||||
- `UnknownClass` (mq.2) — an explicit class qualifier in
|
- `UnknownClass` — an explicit class qualifier in
|
||||||
`Term::Var.name` names a qualified class that is not in the
|
`Term::Var.name` names a qualified class that is not in the
|
||||||
workspace's candidate-class index for the method.
|
workspace's candidate-class index for the method.
|
||||||
- `class-method-shadowed-by-fn` (mq.3, warning) — a `Term::Var`
|
- `class-method-shadowed-by-fn` (warning) — a `Term::Var`
|
||||||
resolved via fn lookup precedence (locals → caller-module-fn →
|
resolved via fn lookup precedence (locals → caller-module-fn →
|
||||||
imported-fn) while a class method of the same name also exists
|
imported-fn) while a class method of the same name also exists
|
||||||
in the workspace. Fn resolution proceeds; the warning surfaces
|
in the workspace. Fn resolution proceeds; the warning surfaces
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Effects — pure core + algebraic effects
|
# Effects — pure core + algebraic effects
|
||||||
|
|
||||||
## Decision 3: pure core language + algebraic effects
|
## Pure core language + algebraic effects
|
||||||
|
|
||||||
The default is total, pure functions. Effects are declared as a set in the
|
The default is total, pure functions. Effects are declared as a set in the
|
||||||
function type: `(Int) -> Int ![IO]`. The effect set is a
|
function type: `(Int) -> Int ![IO]`. The effect set is a
|
||||||
@@ -13,7 +13,9 @@ currently in scope vs. excluded lives in
|
|||||||
In the MVP only the effect `IO` is wired up (its sole op is `io/print_str`).
|
In the MVP only the effect `IO` is wired up (its sole op is `io/print_str`).
|
||||||
`Diverge` (for non-termination) is reserved as an effect name but is
|
`Diverge` (for non-termination) is reserved as an effect name but is
|
||||||
unimplemented — no op, no codegen, no checker injection — in the same
|
unimplemented — no op, no codegen, no checker injection — in the same
|
||||||
sense Decision 4 reserves refinements.
|
sense the refinements layer is reserved as a future extension without
|
||||||
|
present-day surface (see
|
||||||
|
[scope boundaries](../contracts/scope-boundaries.md)).
|
||||||
|
|
||||||
This is the most important LLM property: when I read a function, I can trust
|
This is the most important LLM property: when I read a function, I can trust
|
||||||
its signature without reading the body.
|
its signature without reading the body.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
├─ load + validate schema
|
├─ load + validate schema
|
||||||
├─ resolve names + assign hashes
|
├─ resolve names + assign hashes
|
||||||
├─ desugar (AST → AST)
|
├─ desugar (AST → AST)
|
||||||
├─ typecheck (HM, effect rows; mode-strict per Decision 10)
|
├─ typecheck (HM, effect rows; mode-strict per the memory model)
|
||||||
├─ lift_letrecs (post-typecheck AST → AST)
|
├─ lift_letrecs (post-typecheck AST → AST)
|
||||||
├─ lower to MIR (SSA-like, named SSA values)
|
├─ lower to MIR (SSA-like, named SSA values)
|
||||||
├─ emit LLVM IR (.ll)
|
├─ emit LLVM IR (.ll)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
AILang ships a
|
AILang ships a
|
||||||
second textual projection of the AST: `ailang-prose`, a one-way
|
second textual projection of the AST: `ailang-prose`, a one-way
|
||||||
projection from `Module → human-readable text`. It is **not** an
|
projection from `Module → human-readable text`. It is **not** an
|
||||||
authoring surface; it is the "display" projection that Decision 6's
|
authoring surface; it is the "display" projection that the
|
||||||
architectural pin (see [authoring surface](../contracts/authoring-surface.md))
|
[authoring surface](../contracts/authoring-surface.md)'s
|
||||||
explicitly anticipated:
|
architectural pin explicitly anticipated:
|
||||||
|
|
||||||
> *"Future projections are explicitly anticipated: a visual /
|
> *"Future projections are explicitly anticipated: a visual /
|
||||||
> graphical front-end is a plausible second projection for human
|
> graphical front-end is a plausible second projection for human
|
||||||
@@ -41,7 +41,8 @@ form (B) deliberately is not. Re-integrating prose edits requires an
|
|||||||
external LLM mediator, not a compiler pass — the prompt template
|
external LLM mediator, not a compiler pass — the prompt template
|
||||||
`ail merge-prose` composes the six-step cycle.
|
`ail merge-prose` composes the six-step cycle.
|
||||||
|
|
||||||
Form (B) does not weaken any Decision 6 invariant:
|
Form (B) does not weaken any
|
||||||
|
[authoring surface](../contracts/authoring-surface.md) invariant:
|
||||||
|
|
||||||
- The [JSON-AST](../contracts/data-model.md) remains the only
|
- The [JSON-AST](../contracts/data-model.md) remains the only
|
||||||
hashable artefact. Prose is not hashed, not content-addressed,
|
hashable artefact. Prose is not hashed, not content-addressed,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# RC + Uniqueness — memory model whitepaper
|
# RC + Uniqueness — memory model whitepaper
|
||||||
|
|
||||||
## Decision 9: dual allocator — RC canonical, Boehm parity oracle
|
## Dual allocator — RC canonical, Boehm parity oracle
|
||||||
|
|
||||||
AILang ships two allocator backends with an asymmetric role:
|
AILang ships two allocator backends with an asymmetric role:
|
||||||
|
|
||||||
@@ -25,12 +25,11 @@ when the parity oracle stops paying its keep — concretely, when a
|
|||||||
few iter families ship without the gc arm catching anything that
|
few iter families ship without the gc arm catching anything that
|
||||||
the rc arm did not already catch. Until then, the cost of
|
the rc arm did not already catch. Until then, the cost of
|
||||||
keeping libgc as a build dependency is accepted in exchange for
|
keeping libgc as a build dependency is accepted in exchange for
|
||||||
diagnostic leverage. Decision 10 (RC + uniqueness) holds as the
|
diagnostic leverage. The
|
||||||
specification of the canonical runtime (the contract version is
|
[RC + uniqueness memory model](../contracts/memory-model.md), built
|
||||||
[memory model](../contracts/memory-model.md), built on
|
on the [language constraints](../contracts/language-constraints.md),
|
||||||
[language constraints](../contracts/language-constraints.md));
|
holds as the specification of the canonical runtime; the rest of
|
||||||
the rest of this section documents the Boehm half, retained as
|
this section documents the Boehm half, retained as the oracle.
|
||||||
the oracle.
|
|
||||||
|
|
||||||
**Choice: Boehm-Demers-Weiser conservative GC.** The simplest
|
**Choice: Boehm-Demers-Weiser conservative GC.** The simplest
|
||||||
working option:
|
working option:
|
||||||
@@ -130,7 +129,7 @@ The closure-pair and its env share an escape verdict — they have
|
|||||||
parallel lifetimes. If the closure pair is non-escaping, the env
|
parallel lifetimes. If the closure pair is non-escaping, the env
|
||||||
is too.
|
is too.
|
||||||
|
|
||||||
## Decision 10: memory model — RC + Uniqueness with LLM-author annotations
|
## Memory model — RC + Uniqueness with LLM-author annotations
|
||||||
|
|
||||||
**The GC bench (`bench/run.sh`) showed
|
**The GC bench (`bench/run.sh`) showed
|
||||||
Boehm contributing a substantial fraction of runtime on allocation-heavy workloads
|
Boehm contributing a substantial fraction of runtime on allocation-heavy workloads
|
||||||
@@ -150,9 +149,10 @@ stdlib is the cheapest moment to commit.
|
|||||||
**Choice.** AILang's canonical [memory model](../contracts/memory-model.md)
|
**Choice.** AILang's canonical [memory model](../contracts/memory-model.md)
|
||||||
is reference counting with static uniqueness inference **and
|
is reference counting with static uniqueness inference **and
|
||||||
explicit LLM-author annotations on fn signatures**, in the lineage
|
explicit LLM-author annotations on fn signatures**, in the lineage
|
||||||
of Lean 4 / Roc / Koka. Boehm becomes a transitional allocator (Decision 9) and is
|
of Lean 4 / Roc / Koka. Boehm becomes a transitional allocator (see
|
||||||
retired when the RC pipeline matches the bump-allocator floor
|
the dual-allocator section above) and is retired when the RC pipeline
|
||||||
within an acceptable margin (target 1.3× on `bench/run.sh`).
|
matches the bump-allocator floor within an acceptable margin (target
|
||||||
|
1.3× on `bench/run.sh`).
|
||||||
|
|
||||||
**Workload scope of the 1.3× target.** The 1.3× target was
|
**Workload scope of the 1.3× target.** The 1.3× target was
|
||||||
calibrated on the original `bench/run.sh` corpus: linear list
|
calibrated on the original `bench/run.sh` corpus: linear list
|
||||||
@@ -168,16 +168,17 @@ one bump-pointer bump, doubling the allocation tax on closure
|
|||||||
construction (current ratio recorded in JOURNAL bench entries).
|
construction (current ratio recorded in JOURNAL bench entries).
|
||||||
This is a representational cost of the closure-pair layout,
|
This is a representational cost of the closure-pair layout,
|
||||||
not a defect in the RC implementation; a future slab/pool
|
not a defect in the RC implementation; a future slab/pool
|
||||||
allocator for fixed-shape pair cells (Decision 9 retirement
|
allocator for fixed-shape pair cells (a Boehm-retirement follow-up)
|
||||||
follow-up) would compress this ratio without changing semantics.
|
would compress this ratio without changing semantics.
|
||||||
|
|
||||||
The 1.3× retirement target therefore applies to the linear /
|
The 1.3× retirement target therefore applies to the linear /
|
||||||
tree / poly-ADT subset of the corpus. Closure-heavy workloads are
|
tree / poly-ADT subset of the corpus. Closure-heavy workloads are
|
||||||
tracked under a wider band (the closure-chain baseline records its rc/bump ratio as the `rc_over_bump` reference value with ±15% tolerance) and
|
tracked under a wider band (the closure-chain baseline records its rc/bump ratio as the `rc_over_bump` reference value with ±15% tolerance) and
|
||||||
are explicitly excluded from the Boehm-retirement gate until a
|
are explicitly excluded from the Boehm-retirement gate until a
|
||||||
slab/pool answer ships. Decision-10's RC commitment is unchanged;
|
slab/pool answer ships. The
|
||||||
what is scoped is the *quantitative* retirement criterion, not
|
[memory model](../contracts/memory-model.md)'s RC commitment is
|
||||||
the choice of memory model.
|
unchanged; what is scoped is the *quantitative* retirement criterion,
|
||||||
|
not the choice of memory model.
|
||||||
|
|
||||||
The architecture has two layers:
|
The architecture has two layers:
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ built-in classes shipped in the prelude in
|
|||||||
covers the design choices and the resolution / monomorphisation
|
covers the design choices and the resolution / monomorphisation
|
||||||
algorithm.
|
algorithm.
|
||||||
|
|
||||||
## Decision 11: typeclasses — Haskell-lite, monomorphised, coherent
|
## The design — Haskell-lite, monomorphised, coherent
|
||||||
|
|
||||||
**The design pass for typeclasses. Codified after the
|
**The design pass for typeclasses. Codified after the
|
||||||
[Feature-acceptance criterion](../contracts/feature-acceptance.md)
|
[Feature-acceptance criterion](../contracts/feature-acceptance.md)
|
||||||
@@ -158,10 +158,9 @@ component contains `__` by project convention.
|
|||||||
|
|
||||||
**No runtime dispatch, no dictionary passing.** The monomorphisation
|
**No runtime dispatch, no dictionary passing.** The monomorphisation
|
||||||
pass is the ONLY specialiser. Codegen sees only monomorphic
|
pass is the ONLY specialiser. Codegen sees only monomorphic
|
||||||
`Def::Fn`s and direct calls; the pre-iter-23.4 codegen-time
|
`Def::Fn`s and direct calls. A call that cannot be monomorphised —
|
||||||
specialiser (`lower_polymorphic_call` + `module_polymorphic_fns` +
|
for instance, because a constraint remains unresolved at the entry
|
||||||
`mono_queue`) was removed in iter 23.4. A call that cannot be
|
point — is a static error, not a runtime one. This is the
|
||||||
monomorphised — for instance, because a constraint remains
|
LLVM-friendly form and is consistent with the performance
|
||||||
unresolved at the entry point — is a static error, not a runtime
|
commitment of the
|
||||||
one. This is the LLVM-friendly form and is consistent with
|
[RC + uniqueness memory model](../contracts/memory-model.md).
|
||||||
Decision 10's performance commitment.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user