From 29625e7262babb71bb1c0a4aaae2ce94d5132ab0 Mon Sep 17 00:00:00 2001 From: Brummel Date: Tue, 2 Jun 2026 17:08:30 +0200 Subject: [PATCH] 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). --- .../harness/tests/fixtures/mock_full_run.json | 4 +- .../master/examples/bool_str.ail.json | 6 + .../master/examples/class_def.ail.json | 2 + .../master/examples/data_simple.ail.json | 46 +- .../master/examples/data_with_match.ail.json | 178 +++++-- .../master/examples/fn_calls_prelude.ail.json | 2 + .../master/examples/fn_returns_int.ail.json | 2 + .../master/examples/fn_with_do_seq.ail.json | 89 +++- .../master/examples/fn_with_lambda.ail.json | 4 + .../examples/forall_polymorphic.ail.json | 2 + .../master/examples/instance_def.ail.json | 2 + .../master/examples/loop_sum.ail.json | 1 + .../examples/match_literal_pattern.ail.json | 8 +- .../master/examples/new_rawbuf.ail.json | 1 + .../master/examples/param_modes_all.ail.json | 49 +- .../master/spec.md | 64 +-- .../tasks/t1_add_three.reference.ail.json | 200 ++++++-- .../master/tasks/t2_length.reference.ail.json | 277 +++++++++-- .../tasks/t3_main_prints.reference.ail.json | 89 +++- .../tasks/t4_count_zeros.reference.ail.json | 342 +++++++++++--- .../render/tests/spec_completeness.rs | 44 +- .../rendered/ail.md | 110 ++--- .../rendered/json.md | 438 +++++++++++------- 23 files changed, 1398 insertions(+), 562 deletions(-) create mode 100644 experiments/2026-05-12-cross-model-authoring/master/examples/loop_sum.ail.json create mode 100644 experiments/2026-05-12-cross-model-authoring/master/examples/new_rawbuf.ail.json diff --git a/experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/mock_full_run.json b/experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/mock_full_run.json index 979a09b..19d2435 100644 --- a/experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/mock_full_run.json +++ b/experiments/2026-05-12-cross-model-authoring/harness/tests/fixtures/mock_full_run.json @@ -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, @@ -123,4 +123,4 @@ } } } -} \ No newline at end of file +} diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/bool_str.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/bool_str.ail.json index f0f42a7..47f0245 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/bool_str.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/bool_str.ail.json @@ -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": [], diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/class_def.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/class_def.ail.json index 7a0c4a5..80c8216 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/class_def.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/class_def.ail.json @@ -14,7 +14,9 @@ "type": { "k": "fn", "params": [ { "k": "var", "name": "a" } ], + "param_modes": ["own"], "ret": { "k": "con", "name": "Str" }, + "ret_mode": "own", "effects": [] } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/data_simple.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/data_simple.ail.json index d861c33..8085dd4 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/data_simple.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/data_simple.ail.json @@ -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"} diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/data_with_match.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/data_with_match.ail.json index 8c01fcd..7fcd19e 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/data_with_match.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/data_with_match.ail.json @@ -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" + } + ] } } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_calls_prelude.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_calls_prelude.ail.json index 5d21ab7..479cc8d 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_calls_prelude.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_calls_prelude.ail.json @@ -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"], diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_returns_int.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_returns_int.ail.json index 56bf081..0e855e1 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_returns_int.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_returns_int.ail.json @@ -10,7 +10,9 @@ "type": { "k": "fn", "params": [], + "param_modes": [], "ret": { "k": "con", "name": "Int" }, + "ret_mode": "own", "effects": [] }, "params": [], diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_do_seq.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_do_seq.ail.json index 1dd26e8..16c889b 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_do_seq.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_do_seq.ail.json @@ -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": "do", - "op": "io/print_int", - "args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ] + "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": 1 + } + } + ] + } + ] + }, + "rhs": { + "t": "do", + "op": "io/print_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" + } + } + ] + } }, "rhs": { - "t": "do", - "op": "io/print_int", - "args": [ { "t": "lit", "lit": { "kind": "int", "value": 2 } } ] + "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" + } + } + ] + } } } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_lambda.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_lambda.ail.json index f5d426f..72e32eb 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_lambda.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/fn_with_lambda.ail.json @@ -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"], diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/forall_polymorphic.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/forall_polymorphic.ail.json index 2124887..39f7f2e 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/forall_polymorphic.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/forall_polymorphic.ail.json @@ -13,7 +13,9 @@ "body": { "k": "fn", "params": [ { "k": "var", "name": "a" } ], + "param_modes": ["own"], "ret": { "k": "var", "name": "a" }, + "ret_mode": "own", "effects": [] } }, diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/instance_def.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/instance_def.ail.json index 55b4350..b4f3009 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/instance_def.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/instance_def.ail.json @@ -14,7 +14,9 @@ "type": { "k": "fn", "params": [ { "k": "var", "name": "a" } ], + "param_modes": ["own"], "ret": { "k": "con", "name": "Str" }, + "ret_mode": "own", "effects": [] } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/loop_sum.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/loop_sum.ail.json new file mode 100644 index 0000000..ac3a1aa --- /dev/null +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/loop_sum.ail.json @@ -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"} diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/match_literal_pattern.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/match_literal_pattern.ail.json index de1da64..55a240c 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/match_literal_pattern.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/match_literal_pattern.ail.json @@ -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 } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/new_rawbuf.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/new_rawbuf.ail.json new file mode 100644 index 0000000..9e29a6c --- /dev/null +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/new_rawbuf.ail.json @@ -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"} diff --git a/experiments/2026-05-12-cross-model-authoring/master/examples/param_modes_all.ail.json b/experiments/2026-05-12-cross-model-authoring/master/examples/param_modes_all.ail.json index d366f05..539c483 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/examples/param_modes_all.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/examples/param_modes_all.ail.json @@ -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"} diff --git a/experiments/2026-05-12-cross-model-authoring/master/spec.md b/experiments/2026-05-12-cross-model-authoring/master/spec.md index 7e78ac4..65ff7be 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/spec.md +++ b/experiments/2026-05-12-cross-model-authoring/master/spec.md @@ -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} diff --git a/experiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.reference.ail.json b/experiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.reference.ail.json index 2585774..23ed7c0 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.reference.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/tasks/t1_add_three.reference.ail.json @@ -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": "do", - "op": "io/print_int", - "args": [{ - "t": "app", - "fn": { "t": "var", "name": "add_three" }, + "t": "seq", + "lhs": { + "t": "do", + "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_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" + } + } + ] + } }, "rhs": { - "t": "do", - "op": "io/print_int", - "args": [{ - "t": "app", - "fn": { "t": "var", "name": "add_three" }, + "t": "seq", + "lhs": { + "t": "do", + "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": "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" + } + } + ] + } } } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.reference.ail.json b/experiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.reference.ail.json index c4668d2..6ca358a 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.reference.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/tasks/t2_length.reference.ail.json @@ -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": "do", - "op": "io/print_int", - "args": [{ - "t": "app", - "fn": { "t": "var", "name": "length" }, - "args": [{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }] - }] + "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": "length" + }, + "args": [ + { + "t": "ctor", + "type": "List", + "ctor": "Nil", + "args": [] + } + ] + } + ] + } + ] + }, + "rhs": { + "t": "do", + "op": "io/print_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" + } + } + ] + } }, "rhs": { - "t": "do", - "op": "io/print_int", - "args": [{ - "t": "app", - "fn": { "t": "var", "name": "length" }, - "args": [{ - "t": "ctor", "type": "List", "ctor": "Cons", - "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": [] } - ] - } - ] + "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": "length" + }, + "args": [ + { + "t": "ctor", + "type": "List", + "ctor": "Cons", + "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" } - ] - }] - }] + } + ] + } } } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.reference.ail.json b/experiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.reference.ail.json index b43d4d8..d43b89c 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.reference.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/tasks/t3_main_prints.reference.ail.json @@ -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": "do", - "op": "io/print_int", - "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] + "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": 42 + } + } + ] + } + ] + }, + "rhs": { + "t": "do", + "op": "io/print_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" + } + } + ] + } }, "rhs": { - "t": "do", - "op": "io/print_int", - "args": [{ "t": "lit", "lit": { "kind": "int", "value": 1337 } }] + "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" + } + } + ] + } } } } diff --git a/experiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.reference.ail.json b/experiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.reference.ail.json index 400d3e4..9024ac9 100644 --- a/experiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.reference.ail.json +++ b/experiments/2026-05-12-cross-model-authoring/master/tasks/t4_count_zeros.reference.ail.json @@ -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,59 +176,180 @@ "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": "do", - "op": "io/print_int", - "args": [{ - "t": "app", - "fn": { "t": "var", "name": "count_zeros" }, - "args": [{ "t": "ctor", "type": "List", "ctor": "Nil", "args": [] }] - }] + "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": "count_zeros" + }, + "args": [ + { + "t": "ctor", + "type": "List", + "ctor": "Nil", + "args": [] + } + ] + } + ] + } + ] + }, + "rhs": { + "t": "do", + "op": "io/print_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" + } + } + ] + } }, "rhs": { - "t": "do", - "op": "io/print_int", - "args": [{ - "t": "app", - "fn": { "t": "var", "name": "count_zeros" }, - "args": [{ - "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": 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": [] } - ] + "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": "count_zeros" + }, + "args": [ + { + "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": 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": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "rhs": { + "t": "do", + "op": "io/print_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" } - ] - }] - }] + } + ] + } } } } diff --git a/experiments/2026-05-12-cross-model-authoring/render/tests/spec_completeness.rs b/experiments/2026-05-12-cross-model-authoring/render/tests/spec_completeness.rs index 8c85472..67ccefb 100644 --- a/experiments/2026-05-12-cross-model-authoring/render/tests/spec_completeness.rs +++ b/experiments/2026-05-12-cross-model-authoring/render/tests/spec_completeness.rs @@ -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) { 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) { fn visit_param_mode(m: &ParamMode, observed: &mut HashSet) { match m { - ParamMode::Implicit => { - observed.insert(VariantTag::ParamModeImplicit); - } ParamMode::Own => { observed.insert(VariantTag::ParamModeOwn); } diff --git a/experiments/2026-05-12-cross-model-authoring/rendered/ail.md b/experiments/2026-05-12-cross-model-authoring/rendered/ail.md index dae036e..69d2aa1 100644 --- a/experiments/2026-05-12-cross-model-authoring/rendered/ail.md +++ b/experiments/2026-05-12-cross-model-authoring/rendered/ail.md @@ -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)) diff --git a/experiments/2026-05-12-cross-model-authoring/rendered/json.md b/experiments/2026-05-12-cross-model-authoring/rendered/json.md index 59d1296..fdf9b77 100644 --- a/experiments/2026-05-12-cross-model-authoring/rendered/json.md +++ b/experiments/2026-05-12-cross-model-authoring/rendered/json.md @@ -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": "do", - "op": "io/print_int", - "args": [ { "t": "lit", "lit": { "kind": "int", "value": 1 } } ] + "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": 1 + } + } + ] + } + ] + }, + "rhs": { + "t": "do", + "op": "io/print_str", + "args": [ + { + "t": "lit", + "lit": { + "kind": "str", + "value": "\n" + } + } + ] + } }, "rhs": { - "t": "do", - "op": "io/print_int", - "args": [ { "t": "lit", "lit": { "kind": "int", "value": 2 } } ] + "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": [] } }