feat(cma): revive cross-model harness corpus to current language (refs #68)

The cma authoring-form harness corpus had gone dead against the language
as it evolved since May. The plan modelled it as merely schema-dead
(missing param_modes/ret_mode); it was also drift-dead in the example
BODIES. Fixed in place, with `ail check` + both test suites as the oracle:

- Schema: param_modes/ret_mode completed on every fn type; existing
  borrow annotations preserved (data_with_match's borrow over List).
- Symbol drift: `<`/`==` -> `lt`/`eq` (operator-routing); the removed
  `io/print_int` op -> print_str(int_to_str n) followed by a newline
  print, preserving the trailing newline the references' expected_stdout
  needs.
- Ownership/ADT restructures: data_simple's reuse-as now wraps the
  source in a match arm (ctor must be statically visible);
  data_with_match's count_via_letrec + local go switched borrow->own
  (consume-while-borrowed under the tightened ownership analysis;
  head_or_zero still exercises borrow over the boxed List).
- param_modes_all rewritten to own (Int) + borrow over a boxed ADT --
  (borrow Int) is now a borrow-over-value error.
- Two new author-facing examples (loop_sum: Loop/Recur; new_rawbuf: New)
  cover the Term variants that landed since May.
- spec_completeness.rs: drop the deleted ParamMode::Implicit; cover
  Loop/Recur/New; allowlist the non-authorable Term::Intrinsic out.
- spec.md section 4 rewritten to own/borrow (mandatory, no implicit)
  with the borrow-over-value rule; rendered/ regenerated.
- mock_full_run fixture's t3 turn-2 program migrated so the harness
  score assertions hold; usage fields untouched.

Both render/ and harness/ cargo test suites green in mock mode; no live
IONOS call. Run the harness budget/reference tests with AIL_BIN pointing
at target/debug/ail (ail is not on PATH in the test env).
This commit is contained in:
2026-06-02 17:08:30 +02:00
parent 72d2d9c806
commit 29625e7262
23 changed files with 1398 additions and 562 deletions
@@ -30,7 +30,7 @@
}
},
"2": {
"content": "{\n \"schema\": \"ailang/v0\",\n \"name\": \"t3_main_prints\",\n \"imports\": [],\n \"defs\": [\n {\n \"kind\": \"fn\",\n \"name\": \"main\",\n \"type\": {\n \"k\": \"fn\",\n \"params\": [],\n \"ret\": { \"k\": \"con\", \"name\": \"Unit\" },\n \"effects\": [\"IO\"]\n },\n \"params\": [],\n \"body\": {\n \"t\": \"seq\",\n \"lhs\": {\n \"t\": \"do\",\n \"op\": \"io/print_int\",\n \"args\": [{ \"t\": \"lit\", \"lit\": { \"kind\": \"int\", \"value\": 42 } }]\n },\n \"rhs\": {\n \"t\": \"do\",\n \"op\": \"io/print_int\",\n \"args\": [{ \"t\": \"lit\", \"lit\": { \"kind\": \"int\", \"value\": 1337 } }]\n }\n }\n }\n ]\n}\n",
"content": "{\n \"schema\": \"ailang/v0\",\n \"name\": \"t3_main_prints\",\n \"imports\": [],\n \"defs\": [\n {\n \"kind\": \"fn\",\n \"name\": \"main\",\n \"type\": {\n \"k\": \"fn\",\n \"params\": [],\n \"param_modes\": [],\n \"ret\": {\n \"k\": \"con\",\n \"name\": \"Unit\"\n },\n \"ret_mode\": \"own\",\n \"effects\": [\n \"IO\"\n ]\n },\n \"params\": [],\n \"body\": {\n \"t\": \"seq\",\n \"lhs\": {\n \"t\": \"seq\",\n \"lhs\": {\n \"t\": \"do\",\n \"op\": \"io/print_str\",\n \"args\": [\n {\n \"t\": \"app\",\n \"fn\": {\n \"t\": \"var\",\n \"name\": \"int_to_str\"\n },\n \"args\": [\n {\n \"t\": \"lit\",\n \"lit\": {\n \"kind\": \"int\",\n \"value\": 42\n }\n }\n ]\n }\n ]\n },\n \"rhs\": {\n \"t\": \"do\",\n \"op\": \"io/print_str\",\n \"args\": [\n {\n \"t\": \"lit\",\n \"lit\": {\n \"kind\": \"str\",\n \"value\": \"\\n\"\n }\n }\n ]\n }\n },\n \"rhs\": {\n \"t\": \"seq\",\n \"lhs\": {\n \"t\": \"do\",\n \"op\": \"io/print_str\",\n \"args\": [\n {\n \"t\": \"app\",\n \"fn\": {\n \"t\": \"var\",\n \"name\": \"int_to_str\"\n },\n \"args\": [\n {\n \"t\": \"lit\",\n \"lit\": {\n \"kind\": \"int\",\n \"value\": 1337\n }\n }\n ]\n }\n ]\n },\n \"rhs\": {\n \"t\": \"do\",\n \"op\": \"io/print_str\",\n \"args\": [\n {\n \"t\": \"lit\",\n \"lit\": {\n \"kind\": \"str\",\n \"value\": \"\\n\"\n }\n }\n ]\n }\n }\n }\n }\n ]\n}\n",
"usage": {
"prompt_tokens": 1200,
"completion_tokens": 200,
@@ -10,7 +10,9 @@
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Bool" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -23,7 +25,9 @@
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -36,7 +40,9 @@
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Unit" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -14,7 +14,9 @@
"type": {
"k": "fn",
"params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": []
}
}
@@ -1,45 +1 @@
{
"schema": "ailang/v0",
"name": "data_simple",
"imports": [],
"defs": [
{
"kind": "type",
"name": "Box",
"vars": ["a"],
"doc": "A unary box around a polymorphic value.",
"ctors": [
{
"name": "MkBox",
"fields": [ { "k": "var", "name": "a" } ]
}
]
},
{
"kind": "fn",
"name": "wrap_one",
"doc": "Construct a Box Int via MkBox. The body uses (reuse-as src body) — a hint that the freshly-allocated Box should reuse the source's memory slot under --alloc=rc. Exercises TermCtor, ReuseAs, and Type::Var inside a ctor field position.",
"type": {
"k": "fn",
"params": [
{ "k": "con", "name": "Box", "args": [ { "k": "con", "name": "Int" } ] }
],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Box", "args": [ { "k": "con", "name": "Int" } ] },
"ret_mode": "own",
"effects": []
},
"params": ["src"],
"body": {
"t": "reuse-as",
"source": { "t": "var", "name": "src" },
"body": {
"t": "ctor",
"type": "Box",
"ctor": "MkBox",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ]
}
}
}
]
}
{"defs":[{"ctors":[{"fields":[{"k":"var","name":"a"}],"name":"MkBox"}],"kind":"type","name":"Box","vars":["a"]},{"body":{"arms":[{"body":{"body":{"args":[{"lit":{"kind":"int","value":1},"t":"lit"}],"ctor":"MkBox","t":"ctor","type":"Box"},"source":{"name":"src","t":"var"},"t":"reuse-as"},"pat":{"ctor":"MkBox","fields":[{"name":"v","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"src","t":"var"},"t":"match"},"kind":"fn","name":"wrap_one","params":["src"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"args":[{"k":"con","name":"Int"}],"k":"con","name":"Box"}],"ret":{"args":[{"k":"con","name":"Int"}],"k":"con","name":"Box"},"ret_mode":"own"}}],"imports":[],"name":"data_simple","schema":"ailang/v0"}
@@ -6,14 +6,23 @@
{
"kind": "type",
"name": "List",
"doc": "Monomorphic singly-linked Int list boxed, recursive.",
"doc": "Monomorphic singly-linked Int list \u2014 boxed, recursive.",
"ctors": [
{ "name": "Nil", "fields": [] },
{
"name": "Nil",
"fields": []
},
{
"name": "Cons",
"fields": [
{ "k": "con", "name": "Int" },
{ "k": "con", "name": "List" }
{
"k": "con",
"name": "Int"
},
{
"k": "con",
"name": "List"
}
]
}
]
@@ -24,30 +33,64 @@
"doc": "Return the head of xs, or 0 if empty. Exercises TermMatch with two arms, PatternCtor (both nullary Nil and binary Cons), and PatternWild on the tail field.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "List" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"params": [
{
"k": "con",
"name": "List"
}
],
"param_modes": [
"borrow"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["xs"],
"params": [
"xs"
],
"body": {
"t": "match",
"scrutinee": { "t": "var", "name": "xs" },
"scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [
{
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } }
"pat": {
"p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
},
{
"pat": {
"p": "ctor",
"ctor": "Cons",
"fields": [
{ "p": "var", "name": "h" },
{ "p": "wild" }
{
"p": "var",
"name": "h"
},
{
"p": "wild"
}
]
},
"body": { "t": "var", "name": "h" }
"body": {
"t": "var",
"name": "h"
}
}
]
}
@@ -58,49 +101,110 @@
"doc": "Walk xs counting nodes using a local recursive let. Exercises TermLetRec and TermVar; the recursive `go` is bound locally.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "List" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"params": [
{
"k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["xs"],
"params": [
"xs"
],
"body": {
"t": "letrec",
"name": "go",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "List" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"params": [
{
"k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["ys"],
"params": [
"ys"
],
"body": {
"t": "match",
"scrutinee": { "t": "var", "name": "ys" },
"scrutinee": {
"t": "var",
"name": "ys"
},
"arms": [
{
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } }
"pat": {
"p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
},
{
"pat": {
"p": "ctor",
"ctor": "Cons",
"fields": [
{ "p": "wild" },
{ "p": "var", "name": "t" }
{
"p": "wild"
},
{
"p": "var",
"name": "t"
}
]
},
"body": {
"t": "app",
"fn": { "t": "var", "name": "+" },
"fn": {
"t": "var",
"name": "+"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{
"t": "app",
"fn": { "t": "var", "name": "go" },
"args": [ { "t": "var", "name": "t" } ]
"fn": {
"t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "t"
}
]
}
]
}
@@ -109,8 +213,16 @@
},
"in": {
"t": "app",
"fn": { "t": "var", "name": "go" },
"args": [ { "t": "var", "name": "xs" } ]
"fn": {
"t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "xs"
}
]
}
}
}
@@ -13,7 +13,9 @@
{ "k": "con", "name": "Int" },
{ "k": "con", "name": "Int" }
],
"param_modes": ["own", "own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": ["x", "y"],
@@ -10,7 +10,9 @@
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -10,21 +10,94 @@
"type": {
"k": "fn",
"params": [],
"ret": { "k": "con", "name": "Unit" },
"effects": ["IO"]
"param_modes": [],
"ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
},
"params": [],
"body": {
"t": "seq",
"lhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_int",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ]
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_int",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 2 } } ]
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 2
}
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
}
}
}
@@ -10,12 +10,16 @@
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"ret_mode": "own",
"effects": []
},
"params": ["x"],
@@ -13,7 +13,9 @@
"body": {
"k": "fn",
"params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "var", "name": "a" },
"ret_mode": "own",
"effects": []
}
},
@@ -14,7 +14,9 @@
"type": {
"k": "fn",
"params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": []
}
}
@@ -0,0 +1 @@
{"defs":[{"body":{"binders":[{"init":{"lit":{"kind":"int","value":0},"t":"lit"},"name":"acc","type":{"k":"con","name":"Int"}},{"init":{"lit":{"kind":"int","value":1},"t":"lit"},"name":"i","type":{"k":"con","name":"Int"}}],"body":{"cond":{"args":[{"name":"i","t":"var"},{"name":"n","t":"var"}],"fn":{"name":"gt","t":"var"},"t":"app"},"else":{"args":[{"args":[{"name":"acc","t":"var"},{"name":"i","t":"var"}],"fn":{"name":"+","t":"var"},"t":"app"},{"args":[{"name":"i","t":"var"},{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"+","t":"var"},"t":"app"}],"t":"recur"},"t":"if","then":{"name":"acc","t":"var"}},"t":"loop"},"kind":"fn","name":"sum_to","params":["n"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Int"},"ret_mode":"own"}}],"imports":[],"name":"loop_sum","schema":"ailang/v0"}
@@ -10,7 +10,9 @@
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": ["n"],
@@ -36,7 +38,9 @@
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": ["n"],
@@ -44,7 +48,7 @@
"t": "if",
"cond": {
"t": "app",
"fn": { "t": "var", "name": "<" },
"fn": { "t": "var", "name": "lt" },
"args": [
{ "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -55,7 +59,7 @@
"t": "if",
"cond": {
"t": "app",
"fn": { "t": "var", "name": "==" },
"fn": { "t": "var", "name": "eq" },
"args": [
{ "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -0,0 +1 @@
{"defs":[{"body":{"body":{"args":[{"args":[{"name":"b","t":"var"}],"fn":{"name":"RawBuf.size","t":"var"},"t":"app"}],"fn":{"name":"print","t":"var"},"t":"app"},"name":"b","t":"let","value":{"args":[{"kind":"type","value":{"k":"con","name":"Int"}},{"kind":"value","value":{"lit":{"kind":"int","value":3},"t":"lit"}}],"t":"new","type":"RawBuf"}},"kind":"fn","name":"main","params":[],"type":{"effects":["IO"],"k":"fn","param_modes":[],"params":[],"ret":{"k":"con","name":"Unit"},"ret_mode":"own"}}],"imports":[],"name":"new_rawbuf","schema":"ailang/v0"}
@@ -1,48 +1 @@
{
"schema": "ailang/v0",
"name": "param_modes_all",
"imports": [],
"defs": [
{
"kind": "fn",
"name": "f_implicit",
"doc": "Implicit mode is the legacy default — `param_modes` is omitted from canonical JSON when every entry is Implicit, which is the case here. The visitor still observes ParamMode::Implicit via the (defaulted) ret_mode field on Type::Fn.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"ret": { "k": "con", "name": "Int" },
"effects": []
},
"params": ["x"],
"body": { "t": "var", "name": "x" }
},
{
"kind": "fn",
"name": "f_own",
"doc": "(own T) — caller transfers ownership; callee consumes.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"effects": []
},
"params": ["x"],
"body": { "t": "var", "name": "x" }
},
{
"kind": "fn",
"name": "f_borrow",
"doc": "(borrow T) — caller retains ownership; callee may not consume.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"effects": []
},
"params": ["x"],
"body": { "t": "var", "name": "x" }
}
]
}
{"defs":[{"ctors":[{"fields":[],"name":"Nil"},{"fields":[{"k":"con","name":"Int"},{"k":"con","name":"List"}],"name":"Cons"}],"kind":"type","name":"List"},{"body":{"name":"x","t":"var"},"doc":"(own Int) — unboxed value types (Int/Bool/Float/Unit) are always own; borrow over them is a borrow-over-value error.","kind":"fn","name":"f_own_value","params":["x"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Int"},"ret_mode":"own"}},{"body":{"arms":[{"body":{"lit":{"kind":"int","value":0},"t":"lit"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"lit":{"kind":"int","value":1},"t":"lit"},{"args":[{"name":"t","t":"var"}],"fn":{"name":"f_borrow_boxed","t":"var"},"t":"app"}],"fn":{"name":"+","t":"var"},"t":"app"},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"(borrow List) — boxed types may be borrowed; the callee reads without consuming.","kind":"fn","name":"f_borrow_boxed","params":["xs"],"type":{"effects":[],"k":"fn","param_modes":["borrow"],"params":[{"k":"con","name":"List"}],"ret":{"k":"con","name":"Int"},"ret_mode":"own"}}],"imports":[],"name":"param_modes_all","schema":"ailang/v0"}
@@ -133,38 +133,30 @@ BODY-TYPE)`.
## 4. Mode annotations
Every function parameter carries a mode. The mode says how the
callee may treat the value: `implicit` is the legacy default
(treated as `own`), `own` means the caller transfers ownership and
the callee consumes the value, `borrow` means the caller retains
ownership and the callee may inspect but not consume.
Every function parameter carries a mode, and so does the return
position. There are exactly two modes. `own` means the caller
transfers ownership and the callee consumes the value; `borrow`
means the caller retains ownership and the callee may inspect but
not consume. `borrow` is legal only over a boxed type (an algebraic
data type): unboxed value types (`Int`, `Bool`, `Float`, `Unit`)
are always `own`, and borrowing one is a `borrow-over-value` error.
The mode also applies to the return value: `ret_mode` says whether
the function transfers ownership to the caller (`own`) or returns a
borrow (`borrow`). For most functions the return mode is `own` or
implicit; `borrow` returns are rare and used only for accessor-style
functions.
The return position is always `own` — a function returns a fresh
owned value; borrow-returns are not permitted.
Modes are mandatory on every parameter and on the return position.
The schema rejects unannotated fn signatures only when annotations
are explicitly requested; for compatibility the legacy form (no
mode field at all) is treated as all-implicit.
Modes are mandatory: every parameter and the return position carries
exactly one mode. There is no default or omitted mode.
{form-only: json}
On `Type::Fn`, two optional fields carry modes:
On `Type::Fn`, two required fields carry modes:
- `param_modes`: array of strings `"implicit"`, `"own"`, or
`"borrow"`, one per parameter, in the same order as `params`. The
array is omitted from the canonical bytes when every entry is
`"implicit"`; this is purely a serialisation optimisation. Author
the field explicitly when at least one parameter is `own` or
`borrow`.
- `ret_mode`: a single string of the same vocabulary. Omitted when
it would be `"implicit"`.
- `param_modes`: an array of strings, each `"own"` or `"borrow"`,
one per parameter, in the same order as `params`.
- `ret_mode`: a single string, always `"own"`.
Example: a fn that borrows two ints and returns an int:
`"param_modes": ["borrow", "borrow"]`, `ret_mode` omitted (implicit).
A fn that consumes a list and returns a new list:
Both are present on every fn type. Example a fn that borrows a
boxed list and returns an int: `"param_modes": ["borrow"]`,
`"ret_mode": "own"`. A fn that consumes an int and returns an int:
`"param_modes": ["own"]`, `"ret_mode": "own"`.
{/form-only}
@@ -173,21 +165,13 @@ Modes are surface wrappers around the type in parameter and return
position. Inside a `(fn-type ...)`:
- `(params (own T1) (borrow T2))``T1` is owned, `T2` is borrowed.
- `(ret (own T))` — the function returns an owned value.
- A bare type with no mode wrapper is implicit (legacy default).
- `(ret (own T))` — the return is always owned.
When all parameters and the return are implicit, the surface omits
the mode wrappers entirely; this is the canonical form for fns that
do not yet need explicit annotations. Adding modes is a forward
change: an existing fn keeps the same canonical bytes as long as no
annotation is added.
Writing `(borrow T)` on a parameter the typechecker wants `own`
for surfaces as the `over-strict-mode` diagnostic — advisory, and
suppressable via the fn def's `suppress` clause. The mode is part
of the fn's signature and contributes to its content hash, so
making the choice explicit at authoring time avoids surprise
hash changes when the typechecker's defaults shift.
Every parameter and the return carries a mode wrapper; there is no
bare-type (mode-less) form. `borrow` is legal only over a boxed
type: `(borrow (con Int))` is a `borrow-over-value` error — write
`(own (con Int))`. The mode is part of the fn's signature and
contributes to its content hash.
{/form-only}
{example: param_modes_all}
@@ -9,25 +9,62 @@
"type": {
"k": "fn",
"params": [
{ "k": "con", "name": "Int" },
{ "k": "con", "name": "Int" },
{ "k": "con", "name": "Int" }
{
"k": "con",
"name": "Int"
},
{
"k": "con",
"name": "Int"
},
{
"k": "con",
"name": "Int"
}
],
"ret": { "k": "con", "name": "Int" },
"param_modes": [
"own",
"own",
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["a", "b", "c"],
"params": [
"a",
"b",
"c"
],
"body": {
"t": "app",
"fn": { "t": "var", "name": "+" },
"fn": {
"t": "var",
"name": "+"
},
"args": [
{ "t": "var", "name": "a" },
{
"t": "var",
"name": "a"
},
{
"t": "app",
"fn": { "t": "var", "name": "+" },
"fn": {
"t": "var",
"name": "+"
},
"args": [
{ "t": "var", "name": "b" },
{ "t": "var", "name": "c" }
{
"t": "var",
"name": "b"
},
{
"t": "var",
"name": "c"
}
]
}
]
@@ -39,37 +76,140 @@
"type": {
"k": "fn",
"params": [],
"ret": { "k": "con", "name": "Unit" },
"effects": ["IO"]
"param_modes": [],
"ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
},
"params": [],
"body": {
"t": "seq",
"lhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_int",
"args": [{
"t": "app",
"fn": { "t": "var", "name": "add_three" },
"op": "io/print_str",
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
{ "t": "lit", "lit": { "kind": "int", "value": 2 } },
{ "t": "lit", "lit": { "kind": "int", "value": 3 } }
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "add_three"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{
"t": "lit",
"lit": {
"kind": "int",
"value": 2
}
},
{
"t": "lit",
"lit": {
"kind": "int",
"value": 3
}
}
]
}
]
}
]
}]
},
"rhs": {
"t": "do",
"op": "io/print_int",
"args": [{
"t": "app",
"fn": { "t": "var", "name": "add_three" },
"op": "io/print_str",
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 10 } },
{ "t": "lit", "lit": { "kind": "int", "value": 20 } },
{ "t": "lit", "lit": { "kind": "int", "value": 30 } }
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "add_three"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 10
}
},
{
"t": "lit",
"lit": {
"kind": "int",
"value": 20
}
},
{
"t": "lit",
"lit": {
"kind": "int",
"value": 30
}
}
]
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
}
}
}
@@ -6,14 +6,31 @@
{
"kind": "type",
"name": "List",
"vars": ["a"],
"vars": [
"a"
],
"ctors": [
{ "name": "Nil", "fields": [] },
{
"name": "Nil",
"fields": []
},
{
"name": "Cons",
"fields": [
{ "k": "var", "name": "a" },
{ "k": "con", "name": "List", "args": [{ "k": "var", "name": "a" }] }
{
"k": "var",
"name": "a"
},
{
"k": "con",
"name": "List",
"args": [
{
"k": "var",
"name": "a"
}
]
}
]
}
]
@@ -23,43 +40,98 @@
"name": "length",
"type": {
"k": "forall",
"vars": ["a"],
"vars": [
"a"
],
"body": {
"k": "fn",
"params": [
{ "k": "con", "name": "List", "args": [{ "k": "var", "name": "a" }] }
{
"k": "con",
"name": "List",
"args": [
{
"k": "var",
"name": "a"
}
]
}
],
"ret": { "k": "con", "name": "Int" },
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
}
},
"params": ["xs"],
"params": [
"xs"
],
"body": {
"t": "match",
"scrutinee": { "t": "var", "name": "xs" },
"scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [
{
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } }
"pat": {
"p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
},
{
"pat": {
"p": "ctor",
"ctor": "Cons",
"fields": [
{ "p": "wild" },
{ "p": "var", "name": "rest" }
{
"p": "wild"
},
{
"p": "var",
"name": "rest"
}
]
},
"body": {
"t": "app",
"fn": { "t": "var", "name": "+" },
"fn": {
"t": "var",
"name": "+"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{
"t": "app",
"fn": { "t": "var", "name": "length" },
"args": [{ "t": "var", "name": "rest" }]
"fn": {
"t": "var",
"name": "length"
},
"args": [
{
"t": "var",
"name": "rest"
}
]
}
]
}
@@ -73,47 +145,152 @@
"type": {
"k": "fn",
"params": [],
"ret": { "k": "con", "name": "Unit" },
"effects": ["IO"]
"param_modes": [],
"ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
},
"params": [],
"body": {
"t": "seq",
"lhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_int",
"args": [{
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": { "t": "var", "name": "length" },
"args": [{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }]
}]
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "length"
},
"args": [
{
"t": "ctor",
"type": "List",
"ctor": "Nil",
"args": []
}
]
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_int",
"args": [{
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": { "t": "var", "name": "length" },
"args": [{
"t": "ctor", "type": "List", "ctor": "Cons",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 7 } },
{
"t": "ctor", "type": "List", "ctor": "Cons",
"t": "app",
"fn": {
"t": "var",
"name": "length"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 8 } },
{
"t": "ctor", "type": "List", "ctor": "Cons",
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 9 } },
{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }
{
"t": "lit",
"lit": {
"kind": "int",
"value": 7
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 8
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 9
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Nil",
"args": []
}
]
}
]
}
]
}
]
}
]
}]
}]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
}
}
}
@@ -9,21 +9,94 @@
"type": {
"k": "fn",
"params": [],
"ret": { "k": "con", "name": "Unit" },
"effects": ["IO"]
"param_modes": [],
"ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
},
"params": [],
"body": {
"t": "seq",
"lhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_int",
"args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }]
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 42
}
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_int",
"args": [{ "t": "lit", "lit": { "kind": "int", "value": 1337 } }]
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1337
}
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
}
}
}
@@ -6,14 +6,31 @@
{
"kind": "type",
"name": "List",
"vars": ["a"],
"vars": [
"a"
],
"ctors": [
{ "name": "Nil", "fields": [] },
{
"name": "Nil",
"fields": []
},
{
"name": "Cons",
"fields": [
{ "k": "var", "name": "a" },
{ "k": "con", "name": "List", "args": [{ "k": "var", "name": "a" }] }
{
"k": "var",
"name": "a"
},
{
"k": "con",
"name": "List",
"args": [
{
"k": "var",
"name": "a"
}
]
}
]
}
]
@@ -24,55 +41,129 @@
"type": {
"k": "fn",
"params": [
{ "k": "con", "name": "List", "args": [{ "k": "con", "name": "Int" }] }
{
"k": "con",
"name": "List",
"args": [
{
"k": "con",
"name": "Int"
}
]
}
],
"ret": { "k": "con", "name": "Int" },
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["xs"],
"params": [
"xs"
],
"body": {
"t": "match",
"scrutinee": { "t": "var", "name": "xs" },
"scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [
{
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } }
"pat": {
"p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
},
{
"pat": {
"p": "ctor",
"ctor": "Cons",
"fields": [
{ "p": "var", "name": "h" },
{ "p": "var", "name": "rest" }
{
"p": "var",
"name": "h"
},
{
"p": "var",
"name": "rest"
}
]
},
"body": {
"t": "if",
"cond": {
"t": "app",
"fn": { "t": "var", "name": "eq" },
"fn": {
"t": "var",
"name": "eq"
},
"args": [
{ "t": "var", "name": "h" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } }
{
"t": "var",
"name": "h"
},
{
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
]
},
"then": {
"t": "app",
"fn": { "t": "var", "name": "+" },
"fn": {
"t": "var",
"name": "+"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{
"t": "app",
"fn": { "t": "var", "name": "count_zeros" },
"args": [{ "t": "var", "name": "rest" }]
"fn": {
"t": "var",
"name": "count_zeros"
},
"args": [
{
"t": "var",
"name": "rest"
}
]
}
]
},
"else": {
"t": "app",
"fn": { "t": "var", "name": "count_zeros" },
"args": [{ "t": "var", "name": "rest" }]
"fn": {
"t": "var",
"name": "count_zeros"
},
"args": [
{
"t": "var",
"name": "rest"
}
]
}
}
}
@@ -85,48 +176,151 @@
"type": {
"k": "fn",
"params": [],
"ret": { "k": "con", "name": "Unit" },
"effects": ["IO"]
"param_modes": [],
"ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
},
"params": [],
"body": {
"t": "seq",
"lhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_int",
"args": [{
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": { "t": "var", "name": "count_zeros" },
"args": [{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }]
}]
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "count_zeros"
},
"args": [
{
"t": "ctor",
"type": "List",
"ctor": "Nil",
"args": []
}
]
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_int",
"args": [{
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": { "t": "var", "name": "count_zeros" },
"args": [{
"t": "ctor", "type": "List", "ctor": "Cons",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 0 } },
{
"t": "ctor", "type": "List", "ctor": "Cons",
"t": "app",
"fn": {
"t": "var",
"name": "count_zeros"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 5 } },
{
"t": "ctor", "type": "List", "ctor": "Cons",
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 0 } },
{
"t": "ctor", "type": "List", "ctor": "Cons",
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 3 } },
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
},
{
"t": "ctor", "type": "List", "ctor": "Cons",
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 0 } },
{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }
{
"t": "lit",
"lit": {
"kind": "int",
"value": 5
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 3
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
},
{
"t": "ctor",
"type": "List",
"ctor": "Nil",
"args": []
}
]
}
]
@@ -136,8 +330,26 @@
]
}
]
}]
}]
}
]
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
}
}
}
@@ -16,7 +16,7 @@ use std::collections::HashSet;
use std::path::PathBuf;
use ailang_core::ast::{
Def, InstanceMethod, Literal, Module, ParamMode, Pattern, Term, Type,
Def, InstanceMethod, Literal, Module, NewArg, ParamMode, Pattern, Term, Type,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -41,6 +41,9 @@ enum VariantTag {
TermSeq,
TermClone,
TermReuseAs,
TermLoop,
TermRecur,
TermNew,
// Pattern
PatternWild,
PatternVar,
@@ -58,7 +61,6 @@ enum VariantTag {
TypeVar,
TypeForall,
// ParamMode
ParamModeImplicit,
ParamModeOwn,
ParamModeBorrow,
}
@@ -86,6 +88,9 @@ const EXPECTED_VARIANTS: &[VariantTag] = &[
VariantTag::TermSeq,
VariantTag::TermClone,
VariantTag::TermReuseAs,
VariantTag::TermLoop,
VariantTag::TermRecur,
VariantTag::TermNew,
VariantTag::PatternWild,
VariantTag::PatternVar,
VariantTag::PatternLit,
@@ -99,7 +104,6 @@ const EXPECTED_VARIANTS: &[VariantTag] = &[
VariantTag::TypeFn,
VariantTag::TypeVar,
VariantTag::TypeForall,
VariantTag::ParamModeImplicit,
VariantTag::ParamModeOwn,
VariantTag::ParamModeBorrow,
];
@@ -222,6 +226,37 @@ fn visit_term(t: &Term, observed: &mut HashSet<VariantTag>) {
visit_term(source, observed);
visit_term(body, observed);
}
Term::Loop { binders, body } => {
observed.insert(VariantTag::TermLoop);
for b in binders {
visit_type(&b.ty, observed);
visit_term(&b.init, observed);
}
visit_term(body, observed);
}
Term::Recur { args } => {
observed.insert(VariantTag::TermRecur);
for a in args {
visit_term(a, observed);
}
}
Term::New { type_name: _, args } => {
observed.insert(VariantTag::TermNew);
for a in args {
match a {
NewArg::Type(t) => visit_type(t, observed),
NewArg::Value(v) => visit_term(v, observed),
}
}
}
// `Intrinsic` is the compiler-supplied body marker — non-authorable
// (absent from the parser's term dispatch; typecheck enforces
// kernel-tier-only via IntrinsicOutsideKernelTier). A foreign-author
// mini-spec must not show it and no master example can legally
// contain it, so it is allowlisted OUT of completeness: no VariantTag,
// no EXPECTED entry, a no-op arm here so the match compiles without
// claiming coverage.
Term::Intrinsic => {}
}
}
@@ -297,9 +332,6 @@ fn visit_type(t: &Type, observed: &mut HashSet<VariantTag>) {
fn visit_param_mode(m: &ParamMode, observed: &mut HashSet<VariantTag>) {
match m {
ParamMode::Implicit => {
observed.insert(VariantTag::ParamModeImplicit);
}
ParamMode::Own => {
observed.insert(VariantTag::ParamModeOwn);
}
@@ -48,7 +48,7 @@ through this pair is gated by an integration test.
(module fn_returns_int
(fn answer
(doc "The simplest possible fn — no params, returns a fixed Int.")
(type (fn-type (params) (ret (con Int))))
(type (fn-type (params) (ret (own (con Int)))))
(params)
(body 42)))
```
@@ -99,68 +99,57 @@ BODY-TYPE)`.
(module forall_polymorphic
(fn id
(doc "The polymorphic identity function. Exercises Type::Forall and Type::Var.")
(type (forall (vars a) (fn-type (params a) (ret a))))
(type (forall (vars a) (fn-type (params (own a)) (ret (own a)))))
(params x)
(body x)))
```
## 4. Mode annotations
Every function parameter carries a mode. The mode says how the
callee may treat the value: `implicit` is the legacy default
(treated as `own`), `own` means the caller transfers ownership and
the callee consumes the value, `borrow` means the caller retains
ownership and the callee may inspect but not consume.
Every function parameter carries a mode, and so does the return
position. There are exactly two modes. `own` means the caller
transfers ownership and the callee consumes the value; `borrow`
means the caller retains ownership and the callee may inspect but
not consume. `borrow` is legal only over a boxed type (an algebraic
data type): unboxed value types (`Int`, `Bool`, `Float`, `Unit`)
are always `own`, and borrowing one is a `borrow-over-value` error.
The mode also applies to the return value: `ret_mode` says whether
the function transfers ownership to the caller (`own`) or returns a
borrow (`borrow`). For most functions the return mode is `own` or
implicit; `borrow` returns are rare and used only for accessor-style
functions.
The return position is always `own` — a function returns a fresh
owned value; borrow-returns are not permitted.
Modes are mandatory on every parameter and on the return position.
The schema rejects unannotated fn signatures only when annotations
are explicitly requested; for compatibility the legacy form (no
mode field at all) is treated as all-implicit.
Modes are mandatory: every parameter and the return position carries
exactly one mode. There is no default or omitted mode.
Modes are surface wrappers around the type in parameter and return
position. Inside a `(fn-type ...)`:
- `(params (own T1) (borrow T2))``T1` is owned, `T2` is borrowed.
- `(ret (own T))` — the function returns an owned value.
- A bare type with no mode wrapper is implicit (legacy default).
- `(ret (own T))` — the return is always owned.
When all parameters and the return are implicit, the surface omits
the mode wrappers entirely; this is the canonical form for fns that
do not yet need explicit annotations. Adding modes is a forward
change: an existing fn keeps the same canonical bytes as long as no
annotation is added.
Writing `(borrow T)` on a parameter the typechecker wants `own`
for surfaces as the `over-strict-mode` diagnostic — advisory, and
suppressable via the fn def's `suppress` clause. The mode is part
of the fn's signature and contributes to its content hash, so
making the choice explicit at authoring time avoids surprise
hash changes when the typechecker's defaults shift.
Every parameter and the return carries a mode wrapper; there is no
bare-type (mode-less) form. `borrow` is legal only over a boxed
type: `(borrow (con Int))` is a `borrow-over-value` error — write
`(own (con Int))`. The mode is part of the fn's signature and
contributes to its content hash.
```ail
(module param_modes_all
(fn f_implicit
(doc "Implicit mode is the legacy default — `param_modes` is omitted from canonical JSON when every entry is Implicit, which is the case here. The visitor still observes ParamMode::Implicit via the (defaulted) ret_mode field on Type::Fn.")
(type (fn-type (params (con Int)) (ret (con Int))))
(data List
(ctor Nil)
(ctor Cons (con Int) (con List)))
(fn f_own_value
(doc "(own Int) — unboxed value types (Int/Bool/Float/Unit) are always own; borrow over them is a borrow-over-value error.")
(type (fn-type (params (own (con Int))) (ret (own (con Int)))))
(params x)
(body x))
(fn f_own
(doc "(own T) — caller transfers ownership; callee consumes.")
(type (fn-type (params (own (con Int))) (ret (con Int))))
(params x)
(body x))
(fn f_borrow
(doc "(borrow T) — caller retains ownership; callee may not consume.")
(type (fn-type (params (borrow (con Int))) (ret (con Int))))
(params x)
(body x)))
(fn f_borrow_boxed
(doc "(borrow List) — boxed types may be borrowed; the callee reads without consuming.")
(type (fn-type (params (borrow (con List))) (ret (own (con Int)))))
(params xs)
(body (match xs
(case (pat-ctor Nil) 0)
(case (pat-ctor Cons h t) (app + 1 (app f_borrow_boxed t)))))))
```
## 5. Functions
@@ -211,7 +200,7 @@ number, a string is a double-quoted string, a bool is `true` or
(module fn_calls_prelude
(fn add
(doc "Add two Ints via the polymorphic prelude `+`. Also exercises TermLet by binding the sum to a local name before returning it, and TermClone by re-using the let-bound value.")
(type (fn-type (params (con Int) (con Int)) (ret (con Int))))
(type (fn-type (params (own (con Int)) (own (con Int))) (ret (own (con Int)))))
(params x y)
(body (let s (app + x y) (clone s)))))
```
@@ -220,7 +209,7 @@ number, a string is a double-quoted string, a bool is `true` or
(module fn_with_lambda
(fn make_adder
(doc "Curried adder: takes an Int and returns an Int -> Int closure. Exercises TermLam and Type::Fn appearing as a return type.")
(type (fn-type (params (con Int)) (ret (fn-type (params (con Int)) (ret (con Int))))))
(type (fn-type (params (own (con Int))) (ret (own (fn-type (params (own (con Int))) (ret (own (con Int))))))))
(params x)
(body (lam (params (typed y (con Int))) (ret (con Int)) (body (app + x y))))))
```
@@ -264,13 +253,12 @@ element list.
```ail
(module data_simple
(data Box (vars a)
(doc "A unary box around a polymorphic value.")
(ctor MkBox a))
(fn wrap_one
(doc "Construct a Box Int via MkBox. The body uses (reuse-as src body) — a hint that the freshly-allocated Box should reuse the source's memory slot under --alloc=rc. Exercises TermCtor, ReuseAs, and Type::Var inside a ctor field position.")
(type (fn-type (params (own (con Box (con Int)))) (ret (own (con Box (con Int))))))
(params src)
(body (reuse-as src (term-ctor Box MkBox 1)))))
(body (match src
(case (pat-ctor MkBox v) (reuse-as src (term-ctor Box MkBox 1)))))))
```
## 7. Pattern matching
@@ -319,16 +307,16 @@ arms or into code outside the `match`.
(ctor Cons (con Int) (con List)))
(fn head_or_zero
(doc "Return the head of xs, or 0 if empty. Exercises TermMatch with two arms, PatternCtor (both nullary Nil and binary Cons), and PatternWild on the tail field.")
(type (fn-type (params (borrow (con List))) (ret (con Int))))
(type (fn-type (params (borrow (con List))) (ret (own (con Int)))))
(params xs)
(body (match xs
(case (pat-ctor Nil) 0)
(case (pat-ctor Cons h _) h))))
(fn count_via_letrec
(doc "Walk xs counting nodes using a local recursive let. Exercises TermLetRec and TermVar; the recursive `go` is bound locally.")
(type (fn-type (params (borrow (con List))) (ret (con Int))))
(type (fn-type (params (own (con List))) (ret (own (con Int)))))
(params xs)
(body (let-rec go (params ys) (type (fn-type (params (borrow (con List))) (ret (con Int)))) (body (match ys
(body (let-rec go (params ys) (type (fn-type (params (own (con List))) (ret (own (con Int))))) (body (match ys
(case (pat-ctor Nil) 0)
(case (pat-ctor Cons _ t) (app + 1 (app go t))))) (in (app go xs))))))
```
@@ -337,16 +325,16 @@ arms or into code outside the `match`.
(module match_literal_pattern
(fn classify
(doc "Classify an Int via literal patterns plus a wildcard fallback. Exercises PatternLit and PatternWild.")
(type (fn-type (params (con Int)) (ret (con Int))))
(type (fn-type (params (own (con Int))) (ret (own (con Int)))))
(params n)
(body (match n
(case (pat-lit 0) 100)
(case _ 200))))
(fn sign_if
(doc "Return -1 / 0 / 1 for the sign of n using nested TermIf. Exercises TermIf in both then/else positions plus TermApp on the polymorphic comparison.")
(type (fn-type (params (con Int)) (ret (con Int))))
(type (fn-type (params (own (con Int))) (ret (own (con Int)))))
(params n)
(body (if (app < n 0) -1 (if (app == n 0) 0 1)))))
(body (if (app lt n 0) -1 (if (app eq n 0) 0 1)))))
```
## 8. Effects
@@ -390,9 +378,9 @@ value.
(module fn_with_do_seq
(fn main
(doc "Print two Ints in sequence. Exercises TermDo, TermSeq, and the IO effect on a fn type. The trailing unit return is implicit in the last Do (op returns Unit).")
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(type (fn-type (params) (ret (own (con Unit))) (effects IO)))
(params)
(body (seq (do io/print_int 1) (do io/print_int 2)))))
(body (seq (seq (do io/print_str (app int_to_str 1)) (do io/print_str "\n")) (seq (do io/print_str (app int_to_str 2)) (do io/print_str "\n"))))))
```
## 9. Literals
@@ -447,17 +435,17 @@ authoring surface; for non-finite values use the prelude constants
(module bool_str
(fn is_true
(doc "Trivial Bool literal. Exercises Literal::Bool.")
(type (fn-type (params) (ret (con Bool))))
(type (fn-type (params) (ret (own (con Bool)))))
(params)
(body true))
(fn greeting
(doc "Trivial Str literal. Exercises Literal::Str.")
(type (fn-type (params) (ret (con Str))))
(type (fn-type (params) (ret (own (con Str)))))
(params)
(body "hi"))
(fn unit_value
(doc "Trivial Unit literal — needed so Literal::Unit appears at least once in the corpus.")
(type (fn-type (params) (ret (con Unit))))
(type (fn-type (params) (ret (own (con Unit)))))
(params)
(body (lit-unit))))
```
@@ -508,7 +496,7 @@ also legal (typically a typecheck error unless an instance exists).
(param a)
(doc "A toy single-method class. The instance in instance_def.ail.json provides a body for `show` at type Int.")
(method show
(type (fn-type (params a) (ret (con Str)))))))
(type (fn-type (params (own a)) (ret (own (con Str))))))))
```
```ail
@@ -517,7 +505,7 @@ also legal (typically a typecheck error unless an instance exists).
(param a)
(doc "Re-declared here so this fixture is self-contained; class_def.ail.json declares the same class verbatim.")
(method show
(type (fn-type (params a) (ret (con Str))))))
(type (fn-type (params (own a)) (ret (own (con Str)))))))
(instance
(class MyShow)
(type (con Int))
@@ -58,7 +58,9 @@ carries a `kind` discriminator.
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -123,7 +125,9 @@ optional `constraints` array for class constraints (see section 10).
"body": {
"k": "fn",
"params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "var", "name": "a" },
"ret_mode": "own",
"effects": []
}
},
@@ -136,89 +140,34 @@ optional `constraints` array for class constraints (see section 10).
## 4. Mode annotations
Every function parameter carries a mode. The mode says how the
callee may treat the value: `implicit` is the legacy default
(treated as `own`), `own` means the caller transfers ownership and
the callee consumes the value, `borrow` means the caller retains
ownership and the callee may inspect but not consume.
Every function parameter carries a mode, and so does the return
position. There are exactly two modes. `own` means the caller
transfers ownership and the callee consumes the value; `borrow`
means the caller retains ownership and the callee may inspect but
not consume. `borrow` is legal only over a boxed type (an algebraic
data type): unboxed value types (`Int`, `Bool`, `Float`, `Unit`)
are always `own`, and borrowing one is a `borrow-over-value` error.
The mode also applies to the return value: `ret_mode` says whether
the function transfers ownership to the caller (`own`) or returns a
borrow (`borrow`). For most functions the return mode is `own` or
implicit; `borrow` returns are rare and used only for accessor-style
functions.
The return position is always `own` — a function returns a fresh
owned value; borrow-returns are not permitted.
Modes are mandatory on every parameter and on the return position.
The schema rejects unannotated fn signatures only when annotations
are explicitly requested; for compatibility the legacy form (no
mode field at all) is treated as all-implicit.
Modes are mandatory: every parameter and the return position carries
exactly one mode. There is no default or omitted mode.
On `Type::Fn`, two optional fields carry modes:
On `Type::Fn`, two required fields carry modes:
- `param_modes`: array of strings `"implicit"`, `"own"`, or
`"borrow"`, one per parameter, in the same order as `params`. The
array is omitted from the canonical bytes when every entry is
`"implicit"`; this is purely a serialisation optimisation. Author
the field explicitly when at least one parameter is `own` or
`borrow`.
- `ret_mode`: a single string of the same vocabulary. Omitted when
it would be `"implicit"`.
- `param_modes`: an array of strings, each `"own"` or `"borrow"`,
one per parameter, in the same order as `params`.
- `ret_mode`: a single string, always `"own"`.
Example: a fn that borrows two ints and returns an int:
`"param_modes": ["borrow", "borrow"]`, `ret_mode` omitted (implicit).
A fn that consumes a list and returns a new list:
Both are present on every fn type. Example a fn that borrows a
boxed list and returns an int: `"param_modes": ["borrow"]`,
`"ret_mode": "own"`. A fn that consumes an int and returns an int:
`"param_modes": ["own"]`, `"ret_mode": "own"`.
```json
{
"schema": "ailang/v0",
"name": "param_modes_all",
"imports": [],
"defs": [
{
"kind": "fn",
"name": "f_implicit",
"doc": "Implicit mode is the legacy default — `param_modes` is omitted from canonical JSON when every entry is Implicit, which is the case here. The visitor still observes ParamMode::Implicit via the (defaulted) ret_mode field on Type::Fn.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"ret": { "k": "con", "name": "Int" },
"effects": []
},
"params": ["x"],
"body": { "t": "var", "name": "x" }
},
{
"kind": "fn",
"name": "f_own",
"doc": "(own T) — caller transfers ownership; callee consumes.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"effects": []
},
"params": ["x"],
"body": { "t": "var", "name": "x" }
},
{
"kind": "fn",
"name": "f_borrow",
"doc": "(borrow T) — caller retains ownership; callee may not consume.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"effects": []
},
"params": ["x"],
"body": { "t": "var", "name": "x" }
}
]
}
{"defs":[{"ctors":[{"fields":[],"name":"Nil"},{"fields":[{"k":"con","name":"Int"},{"k":"con","name":"List"}],"name":"Cons"}],"kind":"type","name":"List"},{"body":{"name":"x","t":"var"},"doc":"(own Int) — unboxed value types (Int/Bool/Float/Unit) are always own; borrow over them is a borrow-over-value error.","kind":"fn","name":"f_own_value","params":["x"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"k":"con","name":"Int"}],"ret":{"k":"con","name":"Int"},"ret_mode":"own"}},{"body":{"arms":[{"body":{"lit":{"kind":"int","value":0},"t":"lit"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"lit":{"kind":"int","value":1},"t":"lit"},{"args":[{"name":"t","t":"var"}],"fn":{"name":"f_borrow_boxed","t":"var"},"t":"app"}],"fn":{"name":"+","t":"var"},"t":"app"},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"(borrow List) — boxed types may be borrowed; the callee reads without consuming.","kind":"fn","name":"f_borrow_boxed","params":["xs"],"type":{"effects":[],"k":"fn","param_modes":["borrow"],"params":[{"k":"con","name":"List"}],"ret":{"k":"con","name":"Int"},"ret_mode":"own"}}],"imports":[],"name":"param_modes_all","schema":"ailang/v0"}
```
## 5. Functions
@@ -279,7 +228,9 @@ the difference.
{ "k": "con", "name": "Int" },
{ "k": "con", "name": "Int" }
],
"param_modes": ["own", "own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": ["x", "y"],
@@ -314,20 +265,24 @@ the difference.
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"ret_mode": "own",
"effects": []
},
"params": ["x"],
"body": {
"t": "lam",
"params": ["y"],
"paramTypes": [ { "k": "con", "name": "Int" } ],
"retType": { "k": "con", "name": "Int" },
"param-types": [ { "k": "con", "name": "Int" } ],
"ret-type": { "k": "con", "name": "Int" },
"effects": [],
"body": {
"t": "app",
@@ -378,51 +333,7 @@ carry `"args": []`.
```json
{
"schema": "ailang/v0",
"name": "data_simple",
"imports": [],
"defs": [
{
"kind": "type",
"name": "Box",
"vars": ["a"],
"doc": "A unary box around a polymorphic value.",
"ctors": [
{
"name": "MkBox",
"fields": [ { "k": "var", "name": "a" } ]
}
]
},
{
"kind": "fn",
"name": "wrap_one",
"doc": "Construct a Box Int via MkBox. The body uses (reuse-as src body) — a hint that the freshly-allocated Box should reuse the source's memory slot under --alloc=rc. Exercises TermCtor, ReuseAs, and Type::Var inside a ctor field position.",
"type": {
"k": "fn",
"params": [
{ "k": "con", "name": "Box", "args": [ { "k": "con", "name": "Int" } ] }
],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Box", "args": [ { "k": "con", "name": "Int" } ] },
"ret_mode": "own",
"effects": []
},
"params": ["src"],
"body": {
"t": "reuse-as",
"source": { "t": "var", "name": "src" },
"body": {
"t": "ctor",
"type": "Box",
"ctor": "MkBox",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ]
}
}
}
]
}
{"defs":[{"ctors":[{"fields":[{"k":"var","name":"a"}],"name":"MkBox"}],"kind":"type","name":"Box","vars":["a"]},{"body":{"arms":[{"body":{"body":{"args":[{"lit":{"kind":"int","value":1},"t":"lit"}],"ctor":"MkBox","t":"ctor","type":"Box"},"source":{"name":"src","t":"var"},"t":"reuse-as"},"pat":{"ctor":"MkBox","fields":[{"name":"v","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"src","t":"var"},"t":"match"},"kind":"fn","name":"wrap_one","params":["src"],"type":{"effects":[],"k":"fn","param_modes":["own"],"params":[{"args":[{"k":"con","name":"Int"}],"k":"con","name":"Box"}],"ret":{"args":[{"k":"con","name":"Int"}],"k":"con","name":"Box"},"ret_mode":"own"}}],"imports":[],"name":"data_simple","schema":"ailang/v0"}
```
## 7. Pattern matching
@@ -470,14 +381,23 @@ single pattern. Repeating a name is a typecheck error.
{
"kind": "type",
"name": "List",
"doc": "Monomorphic singly-linked Int list boxed, recursive.",
"doc": "Monomorphic singly-linked Int list \u2014 boxed, recursive.",
"ctors": [
{ "name": "Nil", "fields": [] },
{
"name": "Nil",
"fields": []
},
{
"name": "Cons",
"fields": [
{ "k": "con", "name": "Int" },
{ "k": "con", "name": "List" }
{
"k": "con",
"name": "Int"
},
{
"k": "con",
"name": "List"
}
]
}
]
@@ -488,30 +408,64 @@ single pattern. Repeating a name is a typecheck error.
"doc": "Return the head of xs, or 0 if empty. Exercises TermMatch with two arms, PatternCtor (both nullary Nil and binary Cons), and PatternWild on the tail field.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "List" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"params": [
{
"k": "con",
"name": "List"
}
],
"param_modes": [
"borrow"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["xs"],
"params": [
"xs"
],
"body": {
"t": "match",
"scrutinee": { "t": "var", "name": "xs" },
"scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [
{
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } }
"pat": {
"p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
},
{
"pat": {
"p": "ctor",
"ctor": "Cons",
"fields": [
{ "p": "var", "name": "h" },
{ "p": "wild" }
{
"p": "var",
"name": "h"
},
{
"p": "wild"
}
]
},
"body": { "t": "var", "name": "h" }
"body": {
"t": "var",
"name": "h"
}
}
]
}
@@ -522,49 +476,110 @@ single pattern. Repeating a name is a typecheck error.
"doc": "Walk xs counting nodes using a local recursive let. Exercises TermLetRec and TermVar; the recursive `go` is bound locally.",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "List" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"params": [
{
"k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["xs"],
"params": [
"xs"
],
"body": {
"t": "letrec",
"name": "go",
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "List" } ],
"param_modes": ["borrow"],
"ret": { "k": "con", "name": "Int" },
"params": [
{
"k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": []
},
"params": ["ys"],
"params": [
"ys"
],
"body": {
"t": "match",
"scrutinee": { "t": "var", "name": "ys" },
"scrutinee": {
"t": "var",
"name": "ys"
},
"arms": [
{
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } }
"pat": {
"p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
},
{
"pat": {
"p": "ctor",
"ctor": "Cons",
"fields": [
{ "p": "wild" },
{ "p": "var", "name": "t" }
{
"p": "wild"
},
{
"p": "var",
"name": "t"
}
]
},
"body": {
"t": "app",
"fn": { "t": "var", "name": "+" },
"fn": {
"t": "var",
"name": "+"
},
"args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{
"t": "app",
"fn": { "t": "var", "name": "go" },
"args": [ { "t": "var", "name": "t" } ]
"fn": {
"t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "t"
}
]
}
]
}
@@ -573,8 +588,16 @@ single pattern. Repeating a name is a typecheck error.
},
"in": {
"t": "app",
"fn": { "t": "var", "name": "go" },
"args": [ { "t": "var", "name": "xs" } ]
"fn": {
"t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "xs"
}
]
}
}
}
@@ -595,7 +618,9 @@ single pattern. Repeating a name is a typecheck error.
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": ["n"],
@@ -621,7 +646,9 @@ single pattern. Repeating a name is a typecheck error.
"type": {
"k": "fn",
"params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": []
},
"params": ["n"],
@@ -629,7 +656,7 @@ single pattern. Repeating a name is a typecheck error.
"t": "if",
"cond": {
"t": "app",
"fn": { "t": "var", "name": "<" },
"fn": { "t": "var", "name": "lt" },
"args": [
{ "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -640,7 +667,7 @@ single pattern. Repeating a name is a typecheck error.
"t": "if",
"cond": {
"t": "app",
"fn": { "t": "var", "name": "==" },
"fn": { "t": "var", "name": "eq" },
"args": [
{ "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -704,21 +731,94 @@ discarded but its effects count toward the enclosing fn's row.
"type": {
"k": "fn",
"params": [],
"ret": { "k": "con", "name": "Unit" },
"effects": ["IO"]
"param_modes": [],
"ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
},
"params": [],
"body": {
"t": "seq",
"lhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_int",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ]
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_int",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 2 } } ]
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 2
}
}
]
}
]
},
"rhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
}
}
}
@@ -795,7 +895,9 @@ not by the author. When you write `3.14` in AIL, the parser emits
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Bool" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -808,7 +910,9 @@ not by the author. When you write `3.14` in AIL, the parser emits
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -821,7 +925,9 @@ not by the author. When you write `3.14` in AIL, the parser emits
"type": {
"k": "fn",
"params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Unit" },
"ret_mode": "own",
"effects": []
},
"params": [],
@@ -889,7 +995,9 @@ constraints; omitted from canonical bytes when empty.
"type": {
"k": "fn",
"params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": []
}
}
@@ -916,7 +1024,9 @@ constraints; omitted from canonical bytes when empty.
"type": {
"k": "fn",
"params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": []
}
}