Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
cb2d3a9
tweak
ggreif Jun 4, 2025
dabcb73
more tweaks
ggreif Jun 4, 2025
2c22f59
tweak
ggreif Jun 4, 2025
5e49632
simplify
ggreif Jun 4, 2025
2c9b66e
fix types and the coercion
ggreif Jun 4, 2025
29a5626
accept
ggreif Jun 4, 2025
5464929
test
ggreif Jun 4, 2025
cead471
indent
ggreif Jun 5, 2025
45dc3fa
simplify
ggreif Jun 5, 2025
c342af0
simplify, return to previous types
ggreif Jun 5, 2025
a053eb8
connect loose ends
ggreif Jun 5, 2025
57c340e
no need to assert
ggreif Jun 5, 2025
7701980
restore
ggreif Jun 5, 2025
b8c54a8
WIP
ggreif Jun 5, 2025
891ae89
accept
ggreif Jun 5, 2025
421bba0
accept
ggreif Jun 5, 2025
f4432e2
remove cruft
ggreif Jun 5, 2025
8491185
accept
ggreif Jun 7, 2025
19f48fc
fix
ggreif Jun 7, 2025
e4c35ca
Merge branch 'master' into gabor/fastpath
ggreif Jun 7, 2025
8123e9f
Update src/ir_def/check_ir.ml
ggreif Jun 10, 2025
b9a64cd
Update src/ir_def/check_ir.ml
ggreif Jun 10, 2025
923099c
Merge branch 'master' into gabor/fastpath
ggreif Jun 10, 2025
eda49f2
Update Changelog.md
ggreif Jun 10, 2025
1f4bb4d
Merge branch 'master' into gabor/fastpath
ggreif Jun 10, 2025
0274330
Update Changelog.md
ggreif Jun 10, 2025
633b1c0
test semantic difference of a commit point
ggreif Jun 11, 2025
cdbc376
accept
ggreif Jun 11, 2025
7dd2f45
send queue idea
ggreif Jun 11, 2025
09ce9d7
Update Changelog.md
ggreif Jun 11, 2025
32f6f2b
Apply suggestions from code review
ggreif Jun 11, 2025
fcca72a
Merge branch 'master' into gabor/fastpath
ggreif Jun 11, 2025
824eb80
introduce and use `await?` (#5250)
ggreif Jun 11, 2025
c43a13e
review comments
ggreif Jun 11, 2025
ac3ca6e
Update Changelog.md
ggreif Jun 11, 2025
7b7e7dc
pass flag to `await`
ggreif Jun 11, 2025
7adf9b4
interpreter: short-circuit the continuations when required
ggreif Jun 11, 2025
a7dd642
fix tracing
ggreif Jun 11, 2025
aa4e687
Update Changelog.md
ggreif Jun 11, 2025
b0334e6
maintain the trace depth invariant
ggreif Jun 12, 2025
21b28b4
fix the IR interpreter
ggreif Jun 13, 2025
307681d
refactor
ggreif Jun 13, 2025
85378ab
tweak
ggreif Jun 13, 2025
0bae09f
Merge branch 'master' into gabor/fastpath
ggreif Jun 13, 2025
982e6e9
add section to the language manual about `await?`
ggreif Jun 13, 2025
7f5b78c
add blurb to tutorial
ggreif Jun 13, 2025
93367d6
doc: `await?` doc tweaks (#5266)
crusso Jun 16, 2025
582f570
Merge branch 'master' into gabor/fastpath
ggreif Jun 16, 2025
05593e2
Update src/ir_interpreter/interpret_ir.ml
ggreif Jun 17, 2025
33e65e0
Merge branch 'master' into gabor/fastpath
ggreif Jun 17, 2025
a23a938
Update doc/md/fundamentals/5-actors-async.md
crusso Jun 17, 2025
ecab9f4
Merge branch 'master' into gabor/fastpath
ggreif Jun 17, 2025
087c2c7
add test that short-circuiting also works for the `throw` case
ggreif Jun 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/ir_def/arrange_ir.ml
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ and prim = function
| GetLastArrayOffset -> Atom "GetLastArrayOffset"
| BreakPrim i -> "BreakPrim" $$ [id i]
| RetPrim -> Atom "RetPrim"
| AwaitPrim Type.Fut -> Atom "AwaitPrim"
| AwaitPrim Type.Cmp -> Atom "AwaitPrim*"
| AwaitPrim (AwaitFut _) -> Atom "AwaitPrim"
| AwaitPrim AwaitCmp -> Atom "AwaitPrim*"
| AssertPrim -> Atom "AssertPrim"
| ThrowPrim -> Atom "ThrowPrim"
| ShowPrim t -> "ShowPrim" $$ [typ t]
Expand All @@ -108,8 +108,8 @@ and prim = function
| SetCertifiedData -> Atom "SetCertifiedData"
| GetCertificate -> Atom "GetCertificate"
| OtherPrim s -> Atom s
| CPSAwait (Type.Fut, t) -> "CPSAwait" $$ [typ t]
| CPSAwait (Type.Cmp, t) -> "CPSAwait*" $$ [typ t]
| CPSAwait (AwaitFut _, t) -> "CPSAwait" $$ [typ t]
| CPSAwait (AwaitCmp, t) -> "CPSAwait*" $$ [typ t]
| CPSAsync (Type.Fut, t) -> "CPSAsync" $$ [typ t]
| CPSAsync (Type.Cmp, t) -> "CPSAsync*" $$ [typ t]
| ICArgDataPrim -> Atom "ICArgDataPrim"
Expand Down
6 changes: 3 additions & 3 deletions src/ir_def/check_ir.ml
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ let rec check_exp env (exp:Ir.exp) : unit =
| Some c -> T.Con(c, [])
| None -> error env exp.at "misplaced await" in
let t1 = T.promote (typ exp1) in
let (t2, t3) = try T.as_async_sub s t0 t1
let (t2, t3) = try T.as_async_sub (to_async_sort s) t0 t1
with Invalid_argument _ ->
error env exp1.at "expected async type, but expression has type\n %s"
(T.string_of_typ_expand t1)
Expand Down Expand Up @@ -566,8 +566,8 @@ let rec check_exp env (exp:Ir.exp) : unit =
typ exp1 <: T.blob;
T.Opt (T.seq ots) <: t
| CPSAwait (s, cont_typ), [a; krb] ->
let (_, t1) =
try T.as_async_sub s T.Non (T.normalize (typ a))
let (_, t1) = (*assert (s = AwaitFut false);*)
try T.as_async_sub (to_async_sort s) T.Non (T.normalize (typ a))
with _ -> error env exp.at "CPSAwait expect async arg, found %s" (T.string_of_typ (typ a))
in
(match cont_typ with
Expand Down
3 changes: 2 additions & 1 deletion src/ir_def/construct.ml
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ let asyncE s typ_bind e typ1 =
eff = T.(if s = Fut then Await else Triv) }
}

let awaitE s e =
let awaitE e =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead argument

let (s, _ , typ) = T.as_async (T.normalize (typ e)) in
let s = match s with T.Cmp -> AwaitCmp | T.Fut -> AwaitFut false in
{ it = PrimE (AwaitPrim s, [e]);
at = no_region;
note = Note.{ def with typ; eff = T.Await }
Expand Down
4 changes: 2 additions & 2 deletions src/ir_def/construct.mli
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ val primE : Ir.prim -> exp list -> exp
val selfRefE : typ -> exp
val assertE : exp -> exp
val asyncE : async_sort -> typ_bind -> exp -> typ -> exp
val awaitE : async_sort -> exp -> exp
val awaitE : exp -> exp
val cps_asyncE : async_sort -> typ -> typ -> exp -> exp
val cps_awaitE : async_sort -> typ -> exp -> exp -> exp
val cps_awaitE : await_sort -> typ -> exp -> exp -> exp
val ic_replyE : typ list -> exp -> exp
val ic_rejectE : exp -> exp
val ic_callE : exp -> exp -> exp -> exp -> exp -> exp
Expand Down
10 changes: 8 additions & 2 deletions src/ir_def/ir.ml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ and prim =
| IdxBlobPrim (* blob indexing *)
| BreakPrim of id (* break *)
| RetPrim (* return *)
| AwaitPrim of Type.async_sort (* await/await* *)
| AwaitPrim of await_sort (* await/await* *)
| AssertPrim (* assertion *)
| ThrowPrim (* throw *)
| ShowPrim of Type.typ (* debug_show *)
Expand Down Expand Up @@ -170,7 +170,7 @@ and prim =

| OtherPrim of string (* Other primitive operation, no custom typing rule *)
(* backend stuff *)
| CPSAwait of Type.async_sort * Type.typ
| CPSAwait of await_sort * Type.typ
(* typ is the current continuation type of cps translation *)
| CPSAsync of Type.async_sort * Type.typ
| ICPerformGC
Expand All @@ -186,6 +186,8 @@ and prim =
| ICStableRead of Type.typ (* deserialize value of stable type from stable memory *)
| ICStableSize of Type.typ

and await_sort = AwaitFut of bool | AwaitCmp

(* Declarations *)

and dec = dec' Source.phrase
Expand Down Expand Up @@ -335,3 +337,7 @@ let map_prim t_typ t_id p =
| ICStableWrite t -> ICStableWrite (t_typ t)
| ICStableRead t -> ICStableRead (t_typ t)
| ICStableSize t -> ICStableSize (t_typ t)

let to_async_sort = function
| AwaitCmp -> Type.Cmp
| AwaitFut _ -> Type.Fut
4 changes: 2 additions & 2 deletions src/ir_interpreter/interpret_ir.ml
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,10 @@ and interpret_exp_mut env exp (k : V.value V.cont) =
| BreakPrim id, [v1] -> find id env.labs v1
| RetPrim, [v1] -> Option.get env.rets v1
| ThrowPrim, [v1] -> Option.get env.throws v1
| AwaitPrim Type.Fut, [v1] ->
| AwaitPrim (AwaitFut _), [v1] ->
assert env.flavor.has_await;
await env exp.at (V.as_async v1) k (Option.get env.throws)
| AwaitPrim Type.Cmp, [v1] ->
| AwaitPrim AwaitCmp, [v1] ->
assert env.flavor.has_await;
(V.as_comp v1) k (Option.get env.throws)
| AssertPrim, [v1] ->
Expand Down
27 changes: 15 additions & 12 deletions src/ir_passes/async.ml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ let transform prog =
| VarE (_, _) -> exp'
| AssignE (exp1, exp2) ->
AssignE (t_lexp exp1, t_exp exp2)
| PrimE (CPSAwait (Fut, cont_typ), [a; krb]) ->
| PrimE (CPSAwait (AwaitFut short, cont_typ), [a; krb]) ->
begin match cont_typ with
| Func(_, _, [], _, []) ->
(* unit answer type, from await in `async {}` *)
Expand All @@ -265,19 +265,22 @@ let transform prog =
switch_variantE (t_exp a -*- varE vkrb)
[ ("suspend", wildP,
unitE()); (* suspend *)
("schedule", varP schedule, (* resume later *)
(* try await async (); schedule() catch e -> r(e) *)
(let v = fresh_var "call" unit in
letE v
(selfcallE [] (ic_replyE [] (unitE())) (varE schedule) (projE (varE vkrb) 1)
([] -->* (projE (varE vkrb) 2 -*- unitE ())))
(check_call_perform_status (varE v) (fun e -> projE (varE vkrb) 1 -*- e))))
("schedule", varP schedule,
(if short then (* resume immediately *)
varE schedule -*- unitE ()
else (* resume later *)
(* try await async (); schedule() catch e -> r(e) *)
let v = fresh_var "call" unit in
letE v
(selfcallE [] (ic_replyE [] (unitE())) (varE schedule) (projE (varE vkrb) 1)
([] -->* (projE (varE vkrb) 2 -*- unitE ())))
(check_call_perform_status (varE v) (fun e -> projE (varE vkrb) 1 -*- e))))
]
unit
)).it
| _ -> assert false
end
| PrimE (CPSAwait (Cmp, cont_typ), [a; krb]) ->
| PrimE (CPSAwait (AwaitCmp, cont_typ), [a; krb]) ->
begin match cont_typ with
| Func(_, _, [], _, []) ->
(t_exp a -*- t_exp krb).it
Expand All @@ -296,7 +299,7 @@ let transform prog =
letP (tupP [varP nary_async; varP nary_reply; varP reject; varP clean]) def;
let ic_reply = (* flatten v, here and below? *)
let v = fresh_var "v" (T.seq ts1) in
v --> (ic_replyE ts1 (varE v)) in
v --> ic_replyE ts1 (varE v) in
let ic_reject =
let e = fresh_var "e" catch in
e --> ic_rejectE (errorMessageE (varE e)) in
Expand Down Expand Up @@ -382,7 +385,7 @@ let transform prog =
| FuncE (x, s, c, typbinds, args, ret_tys, exp) ->
begin
match s with
| Local ->
| Local ->
FuncE (x, s, c, t_typ_binds typbinds, t_args args, List.map t_typ ret_tys, t_exp exp)
| Shared s' ->
begin
Expand All @@ -403,7 +406,7 @@ let transform prog =
| t -> assert false in
let k =
let v = fresh_var "v" t1 in
v --> (ic_replyE ret_tys (varE v)) in
v --> ic_replyE ret_tys (varE v) in
let r =
let e = fresh_var "e" catch in
e --> ic_rejectE (errorMessageE (varE e)) in
Expand Down
10 changes: 3 additions & 7 deletions src/ir_passes/await.ml
Original file line number Diff line number Diff line change
Expand Up @@ -462,12 +462,8 @@ and c_exp' context exp k =
in
k' -@- cps_async
| PrimE (AwaitPrim s, [exp1]) ->
let r = match LabelEnv.find_opt Throw context with
| Some (Cont r) -> r
| _ -> assert false
in
let b = match LabelEnv.find_opt Cleanup context with
| Some (Cont r) -> r
let r, b = match LabelEnv.find_opt Throw context, LabelEnv.find_opt Cleanup context with
| Some (Cont r), Some (Cont b) -> r, b
| _ -> assert false
in
letcont k (fun k ->
Expand All @@ -477,7 +473,7 @@ and c_exp' context exp k =
cps_awaitE s (typ_of_var k) (t_exp context exp1) krb
| T.Await ->
c_exp context exp1
(meta (typ exp1) (fun v1 -> (cps_awaitE s (typ_of_var k) (varE v1) krb)))
(meta (typ exp1) (fun v1 -> cps_awaitE s (typ_of_var k) (varE v1) krb))
)
| DeclareE (id, typ, exp1) ->
unary context k (fun v1 -> e (DeclareE (id, typ, varE v1))) exp1
Expand Down
8 changes: 5 additions & 3 deletions src/lowering/desugar.ml
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,9 @@ and exp' at note = function
| T.Async (_, t, _) -> t
| _ -> assert false) in
(blockE ds { at; note; it }).it
| S.AwaitE (s, e) -> I.PrimE (I.AwaitPrim s, [exp e])
| S.AwaitE (T.Cmp, e) when T.is_fut e.note.S.note_typ -> I.PrimE I.(AwaitPrim (AwaitFut true), [exp e])
| S.AwaitE (T.Cmp, e) -> I.PrimE I.(AwaitPrim AwaitCmp, [exp e])
| S.AwaitE (T.Fut, e) -> I.PrimE I.(AwaitPrim (AwaitFut false), [exp e])
| S.AssertE (Runtime, e) -> I.PrimE (I.AssertPrim, [exp e])
| S.AssertE (_, e) -> (unitE ()).it
| S.AnnotE (e, _) -> assert false
Expand Down Expand Up @@ -454,7 +456,7 @@ and call_system_func_opt name es obj_typ =
(primE (Ir.OtherPrim "trap")
[textE "canister_inspect_message explicitly refused message"]))
| "lowmemory" ->
awaitE T.Cmp
awaitE
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused argument elided

(callE (varE (var id.it note)) [T.scope_bound] (unitE()))
| name ->
let inst = match name with
Expand Down Expand Up @@ -1193,7 +1195,7 @@ let import_compiled_class (lib : S.comp_unit) wasm : import_declaration =
(asyncE T.Fut
(typ_arg c' T.Scope T.scope_bound)
(letE principal
(awaitE T.Cmp
(awaitE
(callE (varE install_actor_helper) cs'
(tupE [
install_arg;
Expand Down
7 changes: 5 additions & 2 deletions src/mo_frontend/typing.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1738,7 +1738,10 @@ and infer_exp'' env exp : T.typ =
let t0 = check_AwaitCap env "await" exp.at in
let t1 = infer_exp_promote env exp1 in
(try
let (t2, t3) = T.as_async_sub s t0 t1 in
let s1 = match s, t1 with
| T.(Cmp, Async(Fut, _, _)) -> T.Fut (* we can await* an async *)
| _ -> s in
let (t2, t3) = T.as_async_sub s1 t0 t1 in
if not (eq env exp.at t0 t2) then begin
local_error env exp1.at "M0087"
"ill-scoped await: expected async type from current scope %s, found async type from other scope %s%s%s"
Expand All @@ -1759,7 +1762,7 @@ and infer_exp'' env exp : T.typ =
(if s = T.Fut then
"\nUse keyword 'await*' (not 'await') to consume this type."
else
"\nUse keyword 'await' (not 'await*') to consume this type.")
assert false)
else "")
)
| AssertE (_, exp1) ->
Expand Down
1 change: 1 addition & 0 deletions src/mo_types/type.ml
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ let is_unit = function Tup [] -> true | _ -> false
let is_pair = function Tup [_; _] -> true | _ -> false
let is_func = function Func _ -> true | _ -> false
let is_async = function Async _ -> true | _ -> false
let is_fut = function Async (Fut, _, _) -> true | _ -> false
let is_mut = function Mut _ -> true | _ -> false
let is_typ = function Typ _ -> true | _ -> false
let is_con = function Con _ -> true | _ -> false
Expand Down
1 change: 1 addition & 0 deletions src/mo_types/type.mli
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ val is_unit : typ -> bool
val is_pair : typ -> bool
val is_func : typ -> bool
val is_async : typ -> bool
val is_fut : typ -> bool
val is_mut : typ -> bool
val is_typ : typ -> bool
val is_con : typ -> bool
Expand Down
14 changes: 5 additions & 9 deletions src/prelude/internals.mo
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,6 @@ func @new_async<T <: Any>() : (@Async<T>, @Cont<T>, @Cont<Error>, @CleanCont) {

var cleanup : @BailCont = @cleanup;

func clean() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the lambda in line 399. This reduces the closure size a bit.

cleanup();
};

func enqueue(k : @Cont<T>, r : @Cont<Error>, b : @BailCont) : {
#suspend;
#schedule : () -> ();
Expand All @@ -391,16 +387,16 @@ func @new_async<T <: Any>() : (@Async<T>, @Cont<T>, @Cont<Error>, @CleanCont) {
};
#suspend
};
case (? (#ok (r, t))) {
#schedule (func () { @refund := r; k(t) });
case (?#ok (r, t)) {
#schedule (func() { @refund := r; k(t) })
};
case (? (#error e)) {
#schedule (func () { r(e) });
case (?#error e) {
#schedule (func _ = r(e))
};
};
};

(enqueue, fulfill, fail, clean)
(enqueue, fulfill, fail, func() = cleanup())
};

// Subset of IC management canister interface required for our use
Expand Down
4 changes: 2 additions & 2 deletions test/fail/bad-async-sort.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
actor {
func f() : async* Nat {
await* g(); // reject
await* g(); // accept
};

func g() : async Nat {
Expand All @@ -12,7 +12,7 @@ actor {
};

func anon2() : async () {
await* async {}; // reject
await* async {}; // accept
};

func anon3() : async () {
Expand Down
6 changes: 0 additions & 6 deletions test/fail/ok/bad-async-sort.tc.ok
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
bad-async-sort.mo:3.12-3.15: type error [M0088], expected async* type, but expression has type
async<$f> Nat
Use keyword 'await' (not 'await*') to consume this type.
bad-async-sort.mo:7.10-7.13: type error [M0088], expected async type, but expression has type
async*<$g> Nat
Use keyword 'await*' (not 'await') to consume this type.
bad-async-sort.mo:11.11-11.20: type error [M0088], expected async type, but expression has type
async*<$anon1> ()
Use keyword 'await*' (not 'await') to consume this type.
bad-async-sort.mo:15.12-15.20: type error [M0088], expected async* type, but expression has type
async<$anon2> ()
Use keyword 'await' (not 'await*') to consume this type.
bad-async-sort.mo:19.14-19.22: type error [M0183], async expression cannot produce expected async type
async*<$anon3> ().
Use keyword 'async*' (not 'async') to produce the expected type.
Expand Down
2 changes: 1 addition & 1 deletion test/run-drun/await.mo
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ actor a {

func p():async (Text,Text) { ("fst","snd"); };
let h = async {
let (a,b) = ("a","b"); /* await p(a,b);*/
let (a,b) = await p();
Prim.debugPrint a;
Prim.debugPrint b;
};
Expand Down
6 changes: 6 additions & 0 deletions test/run-drun/blockless.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
actor A {
public func foo() : async Int { 42 };
public func go() : async Int { await* A.foo() }
};

A.go() //OR-CALL ingress go 0x4449444C0000
4 changes: 2 additions & 2 deletions test/run-drun/ok/await.drun-run.ok
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ debug.print: holy
debug.print: e-while
debug.print: g-label
debug.print: .
debug.print: a
debug.print: b
debug.print: cnt: 0 i: 0
debug.print: cnt: 1 i: 1
debug.print: cnt: 2 i: 2
debug.print: cnt: 3 i: 4
debug.print: cnt: 4 i: 5
debug.print: cnt: 5 i: 10
debug.print: .
debug.print: fst
debug.print: snd
debug.print: cnt: 6 i: 3
debug.print: cnt: 7 i: 6
debug.print: cnt: 8 i: 11
Expand Down
4 changes: 2 additions & 2 deletions test/run-drun/ok/await.run-ir.ok
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ holy
e-while
g-label
.
a
b
cnt: 0 i: 0
cnt: 1 i: 1
cnt: 2 i: 2
cnt: 3 i: 4
cnt: 4 i: 5
cnt: 5 i: 10
.
fst
snd
cnt: 6 i: 3
cnt: 7 i: 6
cnt: 8 i: 11
Expand Down
Loading
Loading