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": { "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": { "usage": {
"prompt_tokens": 1200, "prompt_tokens": 1200,
"completion_tokens": 200, "completion_tokens": 200,
@@ -10,7 +10,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Bool" }, "ret": { "k": "con", "name": "Bool" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -23,7 +25,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Str" }, "ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -36,7 +40,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Unit" }, "ret": { "k": "con", "name": "Unit" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -14,7 +14,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "var", "name": "a" } ], "params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" }, "ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": [] "effects": []
} }
} }
@@ -1,45 +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"}
"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 } } ]
}
}
}
]
}
@@ -6,14 +6,23 @@
{ {
"kind": "type", "kind": "type",
"name": "List", "name": "List",
"doc": "Monomorphic singly-linked Int list boxed, recursive.", "doc": "Monomorphic singly-linked Int list \u2014 boxed, recursive.",
"ctors": [ "ctors": [
{ "name": "Nil", "fields": [] }, {
"name": "Nil",
"fields": []
},
{ {
"name": "Cons", "name": "Cons",
"fields": [ "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.", "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": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "List" } ], "params": [
"param_modes": ["borrow"], {
"ret": { "k": "con", "name": "Int" }, "k": "con",
"name": "List"
}
],
"param_modes": [
"borrow"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["xs"], "params": [
"xs"
],
"body": { "body": {
"t": "match", "t": "match",
"scrutinee": { "t": "var", "name": "xs" }, "scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [ "arms": [
{ {
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] }, "pat": {
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } "p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
}, },
{ {
"pat": { "pat": {
"p": "ctor", "p": "ctor",
"ctor": "Cons", "ctor": "Cons",
"fields": [ "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.", "doc": "Walk xs counting nodes using a local recursive let. Exercises TermLetRec and TermVar; the recursive `go` is bound locally.",
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "List" } ], "params": [
"param_modes": ["borrow"], {
"ret": { "k": "con", "name": "Int" }, "k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["xs"], "params": [
"xs"
],
"body": { "body": {
"t": "letrec", "t": "letrec",
"name": "go", "name": "go",
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "List" } ], "params": [
"param_modes": ["borrow"], {
"ret": { "k": "con", "name": "Int" }, "k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["ys"], "params": [
"ys"
],
"body": { "body": {
"t": "match", "t": "match",
"scrutinee": { "t": "var", "name": "ys" }, "scrutinee": {
"t": "var",
"name": "ys"
},
"arms": [ "arms": [
{ {
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] }, "pat": {
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } "p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
}, },
{ {
"pat": { "pat": {
"p": "ctor", "p": "ctor",
"ctor": "Cons", "ctor": "Cons",
"fields": [ "fields": [
{ "p": "wild" }, {
{ "p": "var", "name": "t" } "p": "wild"
},
{
"p": "var",
"name": "t"
}
] ]
}, },
"body": { "body": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "+" }, "fn": {
"t": "var",
"name": "+"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }, {
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{ {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "go" }, "fn": {
"args": [ { "t": "var", "name": "t" } ] "t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "t"
}
]
} }
] ]
} }
@@ -109,8 +213,16 @@
}, },
"in": { "in": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "go" }, "fn": {
"args": [ { "t": "var", "name": "xs" } ] "t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "xs"
}
]
} }
} }
} }
@@ -13,7 +13,9 @@
{ "k": "con", "name": "Int" }, { "k": "con", "name": "Int" },
{ "k": "con", "name": "Int" } { "k": "con", "name": "Int" }
], ],
"param_modes": ["own", "own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["x", "y"], "params": ["x", "y"],
@@ -10,7 +10,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -10,21 +10,94 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"ret": { "k": "con", "name": "Unit" }, "param_modes": [],
"effects": ["IO"] "ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
}, },
"params": [], "params": [],
"body": { "body": {
"t": "seq",
"lhs": {
"t": "seq", "t": "seq",
"lhs": { "lhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ] "args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
}
]
}
]
}, },
"rhs": { "rhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 2 } } ] "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": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "ret": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["x"], "params": ["x"],
@@ -13,7 +13,9 @@
"body": { "body": {
"k": "fn", "k": "fn",
"params": [ { "k": "var", "name": "a" } ], "params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "var", "name": "a" }, "ret": { "k": "var", "name": "a" },
"ret_mode": "own",
"effects": [] "effects": []
} }
}, },
@@ -14,7 +14,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "var", "name": "a" } ], "params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" }, "ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": [] "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": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["n"], "params": ["n"],
@@ -36,7 +38,9 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["n"], "params": ["n"],
@@ -44,7 +48,7 @@
"t": "if", "t": "if",
"cond": { "cond": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "<" }, "fn": { "t": "var", "name": "lt" },
"args": [ "args": [
{ "t": "var", "name": "n" }, { "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } } { "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -55,7 +59,7 @@
"t": "if", "t": "if",
"cond": { "cond": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "==" }, "fn": { "t": "var", "name": "eq" },
"args": [ "args": [
{ "t": "var", "name": "n" }, { "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } } { "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 @@
{ {"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"}
"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" }
}
]
}
@@ -133,38 +133,30 @@ BODY-TYPE)`.
## 4. Mode annotations ## 4. Mode annotations
Every function parameter carries a mode. The mode says how the Every function parameter carries a mode, and so does the return
callee may treat the value: `implicit` is the legacy default position. There are exactly two modes. `own` means the caller
(treated as `own`), `own` means the caller transfers ownership and transfers ownership and the callee consumes the value; `borrow`
the callee consumes the value, `borrow` means the caller retains means the caller retains ownership and the callee may inspect but
ownership and the callee may inspect but not consume. 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 return position is always `own` — a function returns a fresh
the function transfers ownership to the caller (`own`) or returns a owned value; borrow-returns are not permitted.
borrow (`borrow`). For most functions the return mode is `own` or
implicit; `borrow` returns are rare and used only for accessor-style
functions.
Modes are mandatory on every parameter and on the return position. Modes are mandatory: every parameter and the return position carries
The schema rejects unannotated fn signatures only when annotations exactly one mode. There is no default or omitted mode.
are explicitly requested; for compatibility the legacy form (no
mode field at all) is treated as all-implicit.
{form-only: json} {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 - `param_modes`: an array of strings, each `"own"` or `"borrow"`,
`"borrow"`, one per parameter, in the same order as `params`. The one per parameter, in the same order as `params`.
array is omitted from the canonical bytes when every entry is - `ret_mode`: a single string, always `"own"`.
`"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"`.
Example: a fn that borrows two ints and returns an int: Both are present on every fn type. Example a fn that borrows a
`"param_modes": ["borrow", "borrow"]`, `ret_mode` omitted (implicit). boxed list and returns an int: `"param_modes": ["borrow"]`,
A fn that consumes a list and returns a new list: `"ret_mode": "own"`. A fn that consumes an int and returns an int:
`"param_modes": ["own"]`, `"ret_mode": "own"`. `"param_modes": ["own"]`, `"ret_mode": "own"`.
{/form-only} {/form-only}
@@ -173,21 +165,13 @@ Modes are surface wrappers around the type in parameter and return
position. Inside a `(fn-type ...)`: position. Inside a `(fn-type ...)`:
- `(params (own T1) (borrow T2))``T1` is owned, `T2` is borrowed. - `(params (own T1) (borrow T2))``T1` is owned, `T2` is borrowed.
- `(ret (own T))` — the function returns an owned value. - `(ret (own T))` — the return is always owned.
- A bare type with no mode wrapper is implicit (legacy default).
When all parameters and the return are implicit, the surface omits Every parameter and the return carries a mode wrapper; there is no
the mode wrappers entirely; this is the canonical form for fns that bare-type (mode-less) form. `borrow` is legal only over a boxed
do not yet need explicit annotations. Adding modes is a forward type: `(borrow (con Int))` is a `borrow-over-value` error — write
change: an existing fn keeps the same canonical bytes as long as no `(own (con Int))`. The mode is part of the fn's signature and
annotation is added. contributes to its content hash.
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.
{/form-only} {/form-only}
{example: param_modes_all} {example: param_modes_all}
@@ -9,25 +9,62 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ "params": [
{ "k": "con", "name": "Int" }, {
{ "k": "con", "name": "Int" }, "k": "con",
{ "k": "con", "name": "Int" } "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": [] "effects": []
}, },
"params": ["a", "b", "c"], "params": [
"a",
"b",
"c"
],
"body": { "body": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "+" }, "fn": {
"t": "var",
"name": "+"
},
"args": [ "args": [
{ "t": "var", "name": "a" }, {
"t": "var",
"name": "a"
},
{ {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "+" }, "fn": {
"t": "var",
"name": "+"
},
"args": [ "args": [
{ "t": "var", "name": "b" }, {
{ "t": "var", "name": "c" } "t": "var",
"name": "b"
},
{
"t": "var",
"name": "c"
}
] ]
} }
] ]
@@ -39,37 +76,140 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"ret": { "k": "con", "name": "Unit" }, "param_modes": [],
"effects": ["IO"] "ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
}, },
"params": [], "params": [],
"body": { "body": {
"t": "seq",
"lhs": {
"t": "seq", "t": "seq",
"lhs": { "lhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{
"t": "app",
"fn": { "t": "var", "name": "add_three" },
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }, {
{ "t": "lit", "lit": { "kind": "int", "value": 2 } }, "t": "app",
{ "t": "lit", "lit": { "kind": "int", "value": 3 } } "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": { "rhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{
"t": "app",
"fn": { "t": "var", "name": "add_three" },
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 10 } }, {
{ "t": "lit", "lit": { "kind": "int", "value": 20 } }, "t": "lit",
{ "t": "lit", "lit": { "kind": "int", "value": 30 } } "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", "kind": "type",
"name": "List", "name": "List",
"vars": ["a"], "vars": [
"a"
],
"ctors": [ "ctors": [
{ "name": "Nil", "fields": [] }, {
"name": "Nil",
"fields": []
},
{ {
"name": "Cons", "name": "Cons",
"fields": [ "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", "name": "length",
"type": { "type": {
"k": "forall", "k": "forall",
"vars": ["a"], "vars": [
"a"
],
"body": { "body": {
"k": "fn", "k": "fn",
"params": [ "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": [] "effects": []
} }
}, },
"params": ["xs"], "params": [
"xs"
],
"body": { "body": {
"t": "match", "t": "match",
"scrutinee": { "t": "var", "name": "xs" }, "scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [ "arms": [
{ {
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] }, "pat": {
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } "p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
}, },
{ {
"pat": { "pat": {
"p": "ctor", "p": "ctor",
"ctor": "Cons", "ctor": "Cons",
"fields": [ "fields": [
{ "p": "wild" }, {
{ "p": "var", "name": "rest" } "p": "wild"
},
{
"p": "var",
"name": "rest"
}
] ]
}, },
"body": { "body": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "+" }, "fn": {
"t": "var",
"name": "+"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }, {
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{ {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "length" }, "fn": {
"args": [{ "t": "var", "name": "rest" }] "t": "var",
"name": "length"
},
"args": [
{
"t": "var",
"name": "rest"
}
]
} }
] ]
} }
@@ -73,47 +145,152 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"ret": { "k": "con", "name": "Unit" }, "param_modes": [],
"effects": ["IO"] "ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
}, },
"params": [], "params": [],
"body": { "body": {
"t": "seq",
"lhs": {
"t": "seq", "t": "seq",
"lhs": { "lhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{ "args": [
{
"t": "app", "t": "app",
"fn": { "t": "var", "name": "length" }, "fn": {
"args": [{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }] "t": "var",
}] "name": "int_to_str"
},
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "length"
},
"args": [
{
"t": "ctor",
"type": "List",
"ctor": "Nil",
"args": []
}
]
}
]
}
]
}, },
"rhs": { "rhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{ "args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app", "t": "app",
"fn": { "t": "var", "name": "length" }, "fn": {
"args": [{ "t": "var",
"t": "ctor", "type": "List", "ctor": "Cons", "name": "int_to_str"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 7 } },
{ {
"t": "ctor", "type": "List", "ctor": "Cons", "t": "app",
"fn": {
"t": "var",
"name": "length"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 8 } },
{ {
"t": "ctor", "type": "List", "ctor": "Cons", "t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [ "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": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"ret": { "k": "con", "name": "Unit" }, "param_modes": [],
"effects": ["IO"] "ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
}, },
"params": [], "params": [],
"body": { "body": {
"t": "seq",
"lhs": {
"t": "seq", "t": "seq",
"lhs": { "lhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] "args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 42
}
}
]
}
]
}, },
"rhs": { "rhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{ "t": "lit", "lit": { "kind": "int", "value": 1337 } }] "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", "kind": "type",
"name": "List", "name": "List",
"vars": ["a"], "vars": [
"a"
],
"ctors": [ "ctors": [
{ "name": "Nil", "fields": [] }, {
"name": "Nil",
"fields": []
},
{ {
"name": "Cons", "name": "Cons",
"fields": [ "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": { "type": {
"k": "fn", "k": "fn",
"params": [ "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": [] "effects": []
}, },
"params": ["xs"], "params": [
"xs"
],
"body": { "body": {
"t": "match", "t": "match",
"scrutinee": { "t": "var", "name": "xs" }, "scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [ "arms": [
{ {
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] }, "pat": {
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } "p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
}, },
{ {
"pat": { "pat": {
"p": "ctor", "p": "ctor",
"ctor": "Cons", "ctor": "Cons",
"fields": [ "fields": [
{ "p": "var", "name": "h" }, {
{ "p": "var", "name": "rest" } "p": "var",
"name": "h"
},
{
"p": "var",
"name": "rest"
}
] ]
}, },
"body": { "body": {
"t": "if", "t": "if",
"cond": { "cond": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "eq" }, "fn": {
"t": "var",
"name": "eq"
},
"args": [ "args": [
{ "t": "var", "name": "h" }, {
{ "t": "lit", "lit": { "kind": "int", "value": 0 } } "t": "var",
"name": "h"
},
{
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
] ]
}, },
"then": { "then": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "+" }, "fn": {
"t": "var",
"name": "+"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }, {
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{ {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "count_zeros" }, "fn": {
"args": [{ "t": "var", "name": "rest" }] "t": "var",
"name": "count_zeros"
},
"args": [
{
"t": "var",
"name": "rest"
}
]
} }
] ]
}, },
"else": { "else": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "count_zeros" }, "fn": {
"args": [{ "t": "var", "name": "rest" }] "t": "var",
"name": "count_zeros"
},
"args": [
{
"t": "var",
"name": "rest"
}
]
} }
} }
} }
@@ -85,48 +176,151 @@
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"ret": { "k": "con", "name": "Unit" }, "param_modes": [],
"effects": ["IO"] "ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
}, },
"params": [], "params": [],
"body": { "body": {
"t": "seq",
"lhs": {
"t": "seq", "t": "seq",
"lhs": { "lhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{ "args": [
{
"t": "app", "t": "app",
"fn": { "t": "var", "name": "count_zeros" }, "fn": {
"args": [{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }] "t": "var",
}] "name": "int_to_str"
},
"args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "count_zeros"
},
"args": [
{
"t": "ctor",
"type": "List",
"ctor": "Nil",
"args": []
}
]
}
]
}
]
}, },
"rhs": { "rhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [{ "args": [
{
"t": "lit",
"lit": {
"kind": "str",
"value": "\n"
}
}
]
}
},
"rhs": {
"t": "seq",
"lhs": {
"t": "do",
"op": "io/print_str",
"args": [
{
"t": "app", "t": "app",
"fn": { "t": "var", "name": "count_zeros" }, "fn": {
"args": [{ "t": "var",
"t": "ctor", "type": "List", "ctor": "Cons", "name": "int_to_str"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 0 } },
{ {
"t": "ctor", "type": "List", "ctor": "Cons", "t": "app",
"fn": {
"t": "var",
"name": "count_zeros"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 5 } },
{ {
"t": "ctor", "type": "List", "ctor": "Cons", "t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 0 } },
{ {
"t": "ctor", "type": "List", "ctor": "Cons", "t": "lit",
"args": [ "lit": {
{ "t": "lit", "lit": { "kind": "int", "value": 3 } }, "kind": "int",
"value": 0
}
},
{ {
"t": "ctor", "type": "List", "ctor": "Cons", "t": "ctor",
"type": "List",
"ctor": "Cons",
"args": [ "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 std::path::PathBuf;
use ailang_core::ast::{ 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -41,6 +41,9 @@ enum VariantTag {
TermSeq, TermSeq,
TermClone, TermClone,
TermReuseAs, TermReuseAs,
TermLoop,
TermRecur,
TermNew,
// Pattern // Pattern
PatternWild, PatternWild,
PatternVar, PatternVar,
@@ -58,7 +61,6 @@ enum VariantTag {
TypeVar, TypeVar,
TypeForall, TypeForall,
// ParamMode // ParamMode
ParamModeImplicit,
ParamModeOwn, ParamModeOwn,
ParamModeBorrow, ParamModeBorrow,
} }
@@ -86,6 +88,9 @@ const EXPECTED_VARIANTS: &[VariantTag] = &[
VariantTag::TermSeq, VariantTag::TermSeq,
VariantTag::TermClone, VariantTag::TermClone,
VariantTag::TermReuseAs, VariantTag::TermReuseAs,
VariantTag::TermLoop,
VariantTag::TermRecur,
VariantTag::TermNew,
VariantTag::PatternWild, VariantTag::PatternWild,
VariantTag::PatternVar, VariantTag::PatternVar,
VariantTag::PatternLit, VariantTag::PatternLit,
@@ -99,7 +104,6 @@ const EXPECTED_VARIANTS: &[VariantTag] = &[
VariantTag::TypeFn, VariantTag::TypeFn,
VariantTag::TypeVar, VariantTag::TypeVar,
VariantTag::TypeForall, VariantTag::TypeForall,
VariantTag::ParamModeImplicit,
VariantTag::ParamModeOwn, VariantTag::ParamModeOwn,
VariantTag::ParamModeBorrow, VariantTag::ParamModeBorrow,
]; ];
@@ -222,6 +226,37 @@ fn visit_term(t: &Term, observed: &mut HashSet<VariantTag>) {
visit_term(source, observed); visit_term(source, observed);
visit_term(body, 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>) { fn visit_param_mode(m: &ParamMode, observed: &mut HashSet<VariantTag>) {
match m { match m {
ParamMode::Implicit => {
observed.insert(VariantTag::ParamModeImplicit);
}
ParamMode::Own => { ParamMode::Own => {
observed.insert(VariantTag::ParamModeOwn); observed.insert(VariantTag::ParamModeOwn);
} }
@@ -48,7 +48,7 @@ through this pair is gated by an integration test.
(module fn_returns_int (module fn_returns_int
(fn answer (fn answer
(doc "The simplest possible fn — no params, returns a fixed Int.") (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) (params)
(body 42))) (body 42)))
``` ```
@@ -99,68 +99,57 @@ BODY-TYPE)`.
(module forall_polymorphic (module forall_polymorphic
(fn id (fn id
(doc "The polymorphic identity function. Exercises Type::Forall and Type::Var.") (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) (params x)
(body x))) (body x)))
``` ```
## 4. Mode annotations ## 4. Mode annotations
Every function parameter carries a mode. The mode says how the Every function parameter carries a mode, and so does the return
callee may treat the value: `implicit` is the legacy default position. There are exactly two modes. `own` means the caller
(treated as `own`), `own` means the caller transfers ownership and transfers ownership and the callee consumes the value; `borrow`
the callee consumes the value, `borrow` means the caller retains means the caller retains ownership and the callee may inspect but
ownership and the callee may inspect but not consume. 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 return position is always `own` — a function returns a fresh
the function transfers ownership to the caller (`own`) or returns a owned value; borrow-returns are not permitted.
borrow (`borrow`). For most functions the return mode is `own` or
implicit; `borrow` returns are rare and used only for accessor-style
functions.
Modes are mandatory on every parameter and on the return position. Modes are mandatory: every parameter and the return position carries
The schema rejects unannotated fn signatures only when annotations exactly one mode. There is no default or omitted mode.
are explicitly requested; for compatibility the legacy form (no
mode field at all) is treated as all-implicit.
Modes are surface wrappers around the type in parameter and return Modes are surface wrappers around the type in parameter and return
position. Inside a `(fn-type ...)`: position. Inside a `(fn-type ...)`:
- `(params (own T1) (borrow T2))``T1` is owned, `T2` is borrowed. - `(params (own T1) (borrow T2))``T1` is owned, `T2` is borrowed.
- `(ret (own T))` — the function returns an owned value. - `(ret (own T))` — the return is always owned.
- A bare type with no mode wrapper is implicit (legacy default).
When all parameters and the return are implicit, the surface omits Every parameter and the return carries a mode wrapper; there is no
the mode wrappers entirely; this is the canonical form for fns that bare-type (mode-less) form. `borrow` is legal only over a boxed
do not yet need explicit annotations. Adding modes is a forward type: `(borrow (con Int))` is a `borrow-over-value` error — write
change: an existing fn keeps the same canonical bytes as long as no `(own (con Int))`. The mode is part of the fn's signature and
annotation is added. contributes to its content hash.
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.
```ail ```ail
(module param_modes_all (module param_modes_all
(fn f_implicit (data List
(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.") (ctor Nil)
(type (fn-type (params (con Int)) (ret (con Int)))) (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) (params x)
(body x)) (body x))
(fn f_own (fn f_borrow_boxed
(doc "(own T) — caller transfers ownership; callee consumes.") (doc "(borrow List) — boxed types may be borrowed; the callee reads without consuming.")
(type (fn-type (params (own (con Int))) (ret (con Int)))) (type (fn-type (params (borrow (con List))) (ret (own (con Int)))))
(params x) (params xs)
(body x)) (body (match xs
(fn f_borrow (case (pat-ctor Nil) 0)
(doc "(borrow T) — caller retains ownership; callee may not consume.") (case (pat-ctor Cons h t) (app + 1 (app f_borrow_boxed t)))))))
(type (fn-type (params (borrow (con Int))) (ret (con Int))))
(params x)
(body x)))
``` ```
## 5. Functions ## 5. Functions
@@ -211,7 +200,7 @@ number, a string is a double-quoted string, a bool is `true` or
(module fn_calls_prelude (module fn_calls_prelude
(fn add (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.") (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) (params x y)
(body (let s (app + x y) (clone s))))) (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 (module fn_with_lambda
(fn make_adder (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.") (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) (params x)
(body (lam (params (typed y (con Int))) (ret (con Int)) (body (app + x y)))))) (body (lam (params (typed y (con Int))) (ret (con Int)) (body (app + x y))))))
``` ```
@@ -264,13 +253,12 @@ element list.
```ail ```ail
(module data_simple (module data_simple
(data Box (vars a) (data Box (vars a)
(doc "A unary box around a polymorphic value.")
(ctor MkBox a)) (ctor MkBox a))
(fn wrap_one (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)))))) (type (fn-type (params (own (con Box (con Int)))) (ret (own (con Box (con Int))))))
(params src) (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 ## 7. Pattern matching
@@ -319,16 +307,16 @@ arms or into code outside the `match`.
(ctor Cons (con Int) (con List))) (ctor Cons (con Int) (con List)))
(fn head_or_zero (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.") (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) (params xs)
(body (match xs (body (match xs
(case (pat-ctor Nil) 0) (case (pat-ctor Nil) 0)
(case (pat-ctor Cons h _) h)))) (case (pat-ctor Cons h _) h))))
(fn count_via_letrec (fn count_via_letrec
(doc "Walk xs counting nodes using a local recursive let. Exercises TermLetRec and TermVar; the recursive `go` is bound locally.") (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) (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 Nil) 0)
(case (pat-ctor Cons _ t) (app + 1 (app go t))))) (in (app go xs)))))) (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 (module match_literal_pattern
(fn classify (fn classify
(doc "Classify an Int via literal patterns plus a wildcard fallback. Exercises PatternLit and PatternWild.") (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) (params n)
(body (match n (body (match n
(case (pat-lit 0) 100) (case (pat-lit 0) 100)
(case _ 200)))) (case _ 200))))
(fn sign_if (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.") (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) (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 ## 8. Effects
@@ -390,9 +378,9 @@ value.
(module fn_with_do_seq (module fn_with_do_seq
(fn main (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).") (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) (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 ## 9. Literals
@@ -447,17 +435,17 @@ authoring surface; for non-finite values use the prelude constants
(module bool_str (module bool_str
(fn is_true (fn is_true
(doc "Trivial Bool literal. Exercises Literal::Bool.") (doc "Trivial Bool literal. Exercises Literal::Bool.")
(type (fn-type (params) (ret (con Bool)))) (type (fn-type (params) (ret (own (con Bool)))))
(params) (params)
(body true)) (body true))
(fn greeting (fn greeting
(doc "Trivial Str literal. Exercises Literal::Str.") (doc "Trivial Str literal. Exercises Literal::Str.")
(type (fn-type (params) (ret (con Str)))) (type (fn-type (params) (ret (own (con Str)))))
(params) (params)
(body "hi")) (body "hi"))
(fn unit_value (fn unit_value
(doc "Trivial Unit literal — needed so Literal::Unit appears at least once in the corpus.") (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) (params)
(body (lit-unit)))) (body (lit-unit))))
``` ```
@@ -508,7 +496,7 @@ also legal (typically a typecheck error unless an instance exists).
(param a) (param a)
(doc "A toy single-method class. The instance in instance_def.ail.json provides a body for `show` at type Int.") (doc "A toy single-method class. The instance in instance_def.ail.json provides a body for `show` at type Int.")
(method show (method show
(type (fn-type (params a) (ret (con Str))))))) (type (fn-type (params (own a)) (ret (own (con Str))))))))
``` ```
```ail ```ail
@@ -517,7 +505,7 @@ also legal (typically a typecheck error unless an instance exists).
(param a) (param a)
(doc "Re-declared here so this fixture is self-contained; class_def.ail.json declares the same class verbatim.") (doc "Re-declared here so this fixture is self-contained; class_def.ail.json declares the same class verbatim.")
(method show (method show
(type (fn-type (params a) (ret (con Str)))))) (type (fn-type (params (own a)) (ret (own (con Str)))))))
(instance (instance
(class MyShow) (class MyShow)
(type (con Int)) (type (con Int))
@@ -58,7 +58,9 @@ carries a `kind` discriminator.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -123,7 +125,9 @@ optional `constraints` array for class constraints (see section 10).
"body": { "body": {
"k": "fn", "k": "fn",
"params": [ { "k": "var", "name": "a" } ], "params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "var", "name": "a" }, "ret": { "k": "var", "name": "a" },
"ret_mode": "own",
"effects": [] "effects": []
} }
}, },
@@ -136,89 +140,34 @@ optional `constraints` array for class constraints (see section 10).
## 4. Mode annotations ## 4. Mode annotations
Every function parameter carries a mode. The mode says how the Every function parameter carries a mode, and so does the return
callee may treat the value: `implicit` is the legacy default position. There are exactly two modes. `own` means the caller
(treated as `own`), `own` means the caller transfers ownership and transfers ownership and the callee consumes the value; `borrow`
the callee consumes the value, `borrow` means the caller retains means the caller retains ownership and the callee may inspect but
ownership and the callee may inspect but not consume. 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 return position is always `own` — a function returns a fresh
the function transfers ownership to the caller (`own`) or returns a owned value; borrow-returns are not permitted.
borrow (`borrow`). For most functions the return mode is `own` or
implicit; `borrow` returns are rare and used only for accessor-style
functions.
Modes are mandatory on every parameter and on the return position. Modes are mandatory: every parameter and the return position carries
The schema rejects unannotated fn signatures only when annotations exactly one mode. There is no default or omitted mode.
are explicitly requested; for compatibility the legacy form (no
mode field at all) is treated as all-implicit.
On `Type::Fn`, two optional fields carry modes: On `Type::Fn`, two required fields carry modes:
- `param_modes`: array of strings `"implicit"`, `"own"`, or - `param_modes`: an array of strings, each `"own"` or `"borrow"`,
`"borrow"`, one per parameter, in the same order as `params`. The one per parameter, in the same order as `params`.
array is omitted from the canonical bytes when every entry is - `ret_mode`: a single string, always `"own"`.
`"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"`.
Example: a fn that borrows two ints and returns an int: Both are present on every fn type. Example a fn that borrows a
`"param_modes": ["borrow", "borrow"]`, `ret_mode` omitted (implicit). boxed list and returns an int: `"param_modes": ["borrow"]`,
A fn that consumes a list and returns a new list: `"ret_mode": "own"`. A fn that consumes an int and returns an int:
`"param_modes": ["own"]`, `"ret_mode": "own"`. `"param_modes": ["own"]`, `"ret_mode": "own"`.
```json ```json
{ {"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"}
"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" }
}
]
}
``` ```
## 5. Functions ## 5. Functions
@@ -279,7 +228,9 @@ the difference.
{ "k": "con", "name": "Int" }, { "k": "con", "name": "Int" },
{ "k": "con", "name": "Int" } { "k": "con", "name": "Int" }
], ],
"param_modes": ["own", "own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["x", "y"], "params": ["x", "y"],
@@ -314,20 +265,24 @@ the difference.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "ret": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["x"], "params": ["x"],
"body": { "body": {
"t": "lam", "t": "lam",
"params": ["y"], "params": ["y"],
"paramTypes": [ { "k": "con", "name": "Int" } ], "param-types": [ { "k": "con", "name": "Int" } ],
"retType": { "k": "con", "name": "Int" }, "ret-type": { "k": "con", "name": "Int" },
"effects": [], "effects": [],
"body": { "body": {
"t": "app", "t": "app",
@@ -378,51 +333,7 @@ carry `"args": []`.
```json ```json
{ {"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"}
"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 } } ]
}
}
}
]
}
``` ```
## 7. Pattern matching ## 7. Pattern matching
@@ -470,14 +381,23 @@ single pattern. Repeating a name is a typecheck error.
{ {
"kind": "type", "kind": "type",
"name": "List", "name": "List",
"doc": "Monomorphic singly-linked Int list boxed, recursive.", "doc": "Monomorphic singly-linked Int list \u2014 boxed, recursive.",
"ctors": [ "ctors": [
{ "name": "Nil", "fields": [] }, {
"name": "Nil",
"fields": []
},
{ {
"name": "Cons", "name": "Cons",
"fields": [ "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.", "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": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "List" } ], "params": [
"param_modes": ["borrow"], {
"ret": { "k": "con", "name": "Int" }, "k": "con",
"name": "List"
}
],
"param_modes": [
"borrow"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["xs"], "params": [
"xs"
],
"body": { "body": {
"t": "match", "t": "match",
"scrutinee": { "t": "var", "name": "xs" }, "scrutinee": {
"t": "var",
"name": "xs"
},
"arms": [ "arms": [
{ {
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] }, "pat": {
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } "p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
}, },
{ {
"pat": { "pat": {
"p": "ctor", "p": "ctor",
"ctor": "Cons", "ctor": "Cons",
"fields": [ "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.", "doc": "Walk xs counting nodes using a local recursive let. Exercises TermLetRec and TermVar; the recursive `go` is bound locally.",
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "List" } ], "params": [
"param_modes": ["borrow"], {
"ret": { "k": "con", "name": "Int" }, "k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["xs"], "params": [
"xs"
],
"body": { "body": {
"t": "letrec", "t": "letrec",
"name": "go", "name": "go",
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "List" } ], "params": [
"param_modes": ["borrow"], {
"ret": { "k": "con", "name": "Int" }, "k": "con",
"name": "List"
}
],
"param_modes": [
"own"
],
"ret": {
"k": "con",
"name": "Int"
},
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["ys"], "params": [
"ys"
],
"body": { "body": {
"t": "match", "t": "match",
"scrutinee": { "t": "var", "name": "ys" }, "scrutinee": {
"t": "var",
"name": "ys"
},
"arms": [ "arms": [
{ {
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] }, "pat": {
"body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } "p": "ctor",
"ctor": "Nil",
"fields": []
},
"body": {
"t": "lit",
"lit": {
"kind": "int",
"value": 0
}
}
}, },
{ {
"pat": { "pat": {
"p": "ctor", "p": "ctor",
"ctor": "Cons", "ctor": "Cons",
"fields": [ "fields": [
{ "p": "wild" }, {
{ "p": "var", "name": "t" } "p": "wild"
},
{
"p": "var",
"name": "t"
}
] ]
}, },
"body": { "body": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "+" }, "fn": {
"t": "var",
"name": "+"
},
"args": [ "args": [
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }, {
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
},
{ {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "go" }, "fn": {
"args": [ { "t": "var", "name": "t" } ] "t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "t"
}
]
} }
] ]
} }
@@ -573,8 +588,16 @@ single pattern. Repeating a name is a typecheck error.
}, },
"in": { "in": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "go" }, "fn": {
"args": [ { "t": "var", "name": "xs" } ] "t": "var",
"name": "go"
},
"args": [
{
"t": "var",
"name": "xs"
}
]
} }
} }
} }
@@ -595,7 +618,9 @@ single pattern. Repeating a name is a typecheck error.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["n"], "params": ["n"],
@@ -621,7 +646,9 @@ single pattern. Repeating a name is a typecheck error.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "con", "name": "Int" } ], "params": [ { "k": "con", "name": "Int" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Int" }, "ret": { "k": "con", "name": "Int" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": ["n"], "params": ["n"],
@@ -629,7 +656,7 @@ single pattern. Repeating a name is a typecheck error.
"t": "if", "t": "if",
"cond": { "cond": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "<" }, "fn": { "t": "var", "name": "lt" },
"args": [ "args": [
{ "t": "var", "name": "n" }, { "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } } { "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -640,7 +667,7 @@ single pattern. Repeating a name is a typecheck error.
"t": "if", "t": "if",
"cond": { "cond": {
"t": "app", "t": "app",
"fn": { "t": "var", "name": "==" }, "fn": { "t": "var", "name": "eq" },
"args": [ "args": [
{ "t": "var", "name": "n" }, { "t": "var", "name": "n" },
{ "t": "lit", "lit": { "kind": "int", "value": 0 } } { "t": "lit", "lit": { "kind": "int", "value": 0 } }
@@ -704,21 +731,94 @@ discarded but its effects count toward the enclosing fn's row.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"ret": { "k": "con", "name": "Unit" }, "param_modes": [],
"effects": ["IO"] "ret": {
"k": "con",
"name": "Unit"
},
"ret_mode": "own",
"effects": [
"IO"
]
}, },
"params": [], "params": [],
"body": { "body": {
"t": "seq",
"lhs": {
"t": "seq", "t": "seq",
"lhs": { "lhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ] "args": [
{
"t": "app",
"fn": {
"t": "var",
"name": "int_to_str"
},
"args": [
{
"t": "lit",
"lit": {
"kind": "int",
"value": 1
}
}
]
}
]
}, },
"rhs": { "rhs": {
"t": "do", "t": "do",
"op": "io/print_int", "op": "io/print_str",
"args": [ { "t": "lit", "lit": { "kind": "int", "value": 2 } } ] "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": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Bool" }, "ret": { "k": "con", "name": "Bool" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -808,7 +910,9 @@ not by the author. When you write `3.14` in AIL, the parser emits
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Str" }, "ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -821,7 +925,9 @@ not by the author. When you write `3.14` in AIL, the parser emits
"type": { "type": {
"k": "fn", "k": "fn",
"params": [], "params": [],
"param_modes": [],
"ret": { "k": "con", "name": "Unit" }, "ret": { "k": "con", "name": "Unit" },
"ret_mode": "own",
"effects": [] "effects": []
}, },
"params": [], "params": [],
@@ -889,7 +995,9 @@ constraints; omitted from canonical bytes when empty.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "var", "name": "a" } ], "params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" }, "ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": [] "effects": []
} }
} }
@@ -916,7 +1024,9 @@ constraints; omitted from canonical bytes when empty.
"type": { "type": {
"k": "fn", "k": "fn",
"params": [ { "k": "var", "name": "a" } ], "params": [ { "k": "var", "name": "a" } ],
"param_modes": ["own"],
"ret": { "k": "con", "name": "Str" }, "ret": { "k": "con", "name": "Str" },
"ret_mode": "own",
"effects": [] "effects": []
} }
} }