Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ee6d0cf
make arg to call a ref
crusso Sep 26, 2025
db009ea
merge with master
crusso Sep 26, 2025
08a4dd3
implement module (only) search
crusso Sep 26, 2025
886d90d
plug holes
crusso Sep 26, 2025
15484cf
test
crusso Sep 26, 2025
cc58e46
defer then check holes
crusso Sep 26, 2025
dc29037
ca marche!
crusso Sep 26, 2025
b4dfe31
extend test
crusso Sep 26, 2025
5341ce9
more tests
crusso Sep 26, 2025
54f5380
update test output
crusso Sep 26, 2025
ff002ae
test dynamics
crusso Sep 27, 2025
9e3f802
match on names
crusso Sep 27, 2025
5567059
test local override
crusso Sep 27, 2025
ccc1368
extend test with redefinition of implicit
crusso Sep 27, 2025
a2c5424
improve error messages, add codes
crusso Sep 27, 2025
bc808f8
missing file
crusso Sep 27, 2025
35b3dd4
add repro
crusso Sep 27, 2025
bd9076b
fixes for unary/nullary applications
christoph-dfinity Sep 27, 2025
32803f4
accept test output
christoph-dfinity Sep 27, 2025
ecae052
fix: only check holes in non-pre phase
christoph-dfinity Sep 28, 2025
007c511
experiment: disambiguate's multiple matches by trying to find a singl…
christoph-dfinity Sep 28, 2025
a794b06
only check inferred type for hole is closed in non-pre phase
christoph-dfinity Sep 28, 2025
5a7d44e
used nested named type to disambiguate, wild card for positional (ano…
crusso Sep 28, 2025
2cf2039
uses nested named types to indicate eponymous implicits with differnt…
crusso Sep 29, 2025
d5433f9
Merge branch 'master' into claudio/implicits
crusso Sep 29, 2025
50c2158
adds comments to the disambiguation pass
christoph-dfinity Sep 29, 2025
6c33dee
fix: don't flatten parenthesized tuple arguments when inserting holes…
christoph-dfinity Sep 29, 2025
d2dad10
Merge branch 'master' into claudio/implicits
crusso Sep 29, 2025
a4f9574
add example
crusso Sep 29, 2025
1860477
Changelog.md
crusso Sep 30, 2025
c627ef0
spaces not tabs - Silicon Valley
crusso Sep 30, 2025
a7f069c
edit Changelog
crusso Sep 30, 2025
96107d2
describe anonymous implicits
crusso Sep 30, 2025
8084d47
describe anonymous implicits
crusso Sep 30, 2025
6ce6e90
shorten changelog description
crusso Oct 1, 2025
41c9d9f
merge
crusso Oct 1, 2025
de4c2d4
kamil/implicits review (#5535)
Kamirus Oct 1, 2025
d54d8d7
merge with master
crusso Oct 1, 2025
871e4d4
split error code
crusso Oct 1, 2025
b3d3958
register untestes code 232
crusso Oct 1, 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
5 changes: 4 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
## Unreleased

* motoko (`moc`)
* Experimental support for Mixins (#5459)

* Experimental support for `implicit` argument declarations (#5517).

* Experimental support for Mixins (#5459).

## 0.16.3 (2025-09-29)

Expand Down
15 changes: 13 additions & 2 deletions doc/md/examples/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
X
X SEP <list(X, SEP)>

<id_wild> ::=
'_'

<typ_obj_sort> ::=
'object'
'actor'
Expand Down Expand Up @@ -67,6 +70,7 @@

<typ_item> ::=
<id> ':' <typ>
<id_wild> ':' <typ>
<typ>

<typ_args> ::=
Expand Down Expand Up @@ -177,19 +181,26 @@
<id>
'_'

<exp_arg> ::=
<ob>
<lit>
'(' <list(<exp>, ',')> ')'
<id>
'_'

<exp_post> ::=
<exp_nullary>
'[' 'var'? <list(<exp_nonvar>, ',')> ']'
<exp_post> '[' <exp> ']'
<exp_post> '.'<nat>
<exp_post> '.' <id>
<exp_post> <inst> <exp_nullary>
<exp_post> <inst> <exp_arg>
<exp_post> '!'
'(' 'system' <exp_post> '.' <id> ')'

<exp_un> ::=
<exp_post>
<parenthetical> <exp_post> <inst> <exp_nullary>
<parenthetical> <exp_post> <inst> <exp_arg>
'#' <id>
'#' <id> <exp_nullary>
'?' <exp_un>
Expand Down
3 changes: 3 additions & 0 deletions src/lang_utils/error_codes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ let error_codes : (string * string option) list =
"M0227", None; (* Non-actor include *)
"M0228", None; (* Only top-level mixins *)
"M0229", None; (* Non-var pattern for mixin import *)
"M0230", None; (* Cannot determine implicit argument *)
"M0231", None; (* Ambiguous implicit argument *)
"M0232", None; (* Cannot infer type of implicit argument *)
]

(** Message codes that can be both used as warnings and errors *)
Expand Down
124 changes: 67 additions & 57 deletions src/lowering/desugar.ml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ let typed_phrase' f x =
let n' = typ_note x.note in
{ x with it = f x.at n' x.it; note = n' }

let is_empty_tup e = e.it = S.TupE []

let rec exps es = List.map exp es

Expand All @@ -54,6 +55,7 @@ and exp e =
| _ -> typed_phrase' exp' e

and exp' at note = function
| S.HoleE (_, e) -> (exp !e).it
| S.VarE i -> I.VarE ((match i.note with Var -> I.Var | Const -> I.Const), i.it)
| S.ActorUrlE e ->
I.(PrimE (ActorOfIdBlob note.Note.typ, [url e at]))
Expand Down Expand Up @@ -125,110 +127,118 @@ and exp' at note = function
let tys = List.map (T.open_ vars) res_tys in
I.FuncE (name, s, control, tbs', args, tys, wrap (exp e))
(* Primitive functions in the prelude have particular shapes *)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_}, _);note;_}, _, e)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_}, _);note;_}, _, (_, e))
when Lib.String.chop_prefix "num_conv" p <> None ->
begin match String.split_on_char '_' p with
| ["num"; "conv"; s1; s2] ->
let p1 = Type.prim s1 in
let p2 = Type.prim s2 in
I.PrimE (I.NumConvTrapPrim (p1, p2), [exp e])
I.PrimE (I.NumConvTrapPrim (p1, p2), [exp !e])
| _ -> assert false
end
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_}, _);note;_}, _, e)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_}, _);note;_}, _, (_, e))
when Lib.String.chop_prefix "num_wrap" p <> None ->
begin match String.split_on_char '_' p with
| ["num"; "wrap"; s1; s2] ->
let p1 = Type.prim s1 in
let p2 = Type.prim s2 in
I.PrimE (I.NumConvWrapPrim (p1, p2), [exp e])
I.PrimE (I.NumConvWrapPrim (p1, p2), [exp !e])
| _ -> assert false
end
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "decodeUtf8";_},_);_}, _, e) ->
I.PrimE (I.DecodeUtf8, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "encodeUtf8";_},_);_}, _, e) ->
I.PrimE (I.EncodeUtf8, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cast";_}, _);note;_}, _, e) ->
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "decodeUtf8";_},_);_}, _, (_, e)) ->
I.PrimE (I.DecodeUtf8, [exp !e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "encodeUtf8";_},_);_}, _, (_, e)) ->
I.PrimE (I.EncodeUtf8, [exp !e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cast";_}, _);note;_}, _, (_, e)) ->
begin match note.S.note_typ with
| T.Func (T.Local, T.Returns, [], ts1, ts2) ->
I.PrimE (I.CastPrim (T.seq ts1, T.seq ts2), [exp e])
I.PrimE (I.CastPrim (T.seq ts1, T.seq ts2), [exp !e])
| _ -> assert false
end
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "serialize";_}, _);note;_}, _, e) ->
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "serialize";_}, _);note;_}, _, (_, e)) ->
begin match note.S.note_typ with
| T.Func (T.Local, T.Returns, [], ts1, ts2) ->
I.PrimE (I.SerializePrim ts1, [exp e])
I.PrimE (I.SerializePrim ts1, [exp !e])
| _ -> assert false
end
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "deserialize";_}, _);note;_}, _, e) ->
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "deserialize";_}, _);note;_}, _, (_, e)) ->
begin match note.S.note_typ with
| T.Func (T.Local, T.Returns, [], ts1, ts2) ->
I.PrimE (I.DeserializePrim ts2, [exp e])
I.PrimE (I.DeserializePrim ts2, [exp (!e)])
| _ -> assert false
end
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "caller";_},_);_}, _, {it=S.TupE es;_}) ->
assert (es = []);
I.PrimE (I.ICCallerPrim, [])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "deadline";_},_);_}, _, {it=S.TupE es;_}) ->
assert (es = []);
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "caller";_},_);_}, _, (_, e)) ->
(match !e with
| {it=S.TupE [];_} ->
I.PrimE (I.ICCallerPrim, [])
| _ -> assert false)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "deadline";_},_);_}, _, (_, e)) ->
assert ((!e).it = S.TupE []);
I.PrimE (I.ICReplyDeadlinePrim, [])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "time";_},_);_}, _, {it=S.TupE es;_}) ->
assert (es = []);
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "time";_},_);_}, _, (_, e)) ->
assert ((!e).it = S.TupE []);
I.PrimE (I.SystemTimePrim, [])
(* Cycles *)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesBalance";_},_);_}, _, {it=S.TupE es;_}) ->
assert (es = []);
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesBalance";_},_);_}, _, (_, e)) ->
assert (is_empty_tup !e);
I.PrimE (I.SystemCyclesBalancePrim, [])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesAvailable";_},_);_}, _, {it=S.TupE es;_}) ->
assert (es = []);
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesAvailable";_},_);_}, _, (_, e)) ->
assert (is_empty_tup !e);
I.PrimE (I.SystemCyclesAvailablePrim, [])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesRefunded";_},_);_}, _, {it=S.TupE es;_}) ->
assert (es = []);
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesRefunded";_},_);_}, _, (_, e)) ->
assert (is_empty_tup !e);
I.PrimE (I.SystemCyclesRefundedPrim, [])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesAccept";_},_);_}, _, e) ->
I.PrimE (I.SystemCyclesAcceptPrim, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesAdd";_},_);_}, _, e) ->
I.PrimE (I.SystemCyclesAddPrim, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesBurn";_},_);_}, _, e) ->
I.PrimE (I.SystemCyclesBurnPrim, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "timeoutSet";_},_);_}, _, e) ->
I.PrimE (I.SystemTimeoutSetPrim, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesAccept";_},_);_}, _, (_, e)) ->
I.PrimE (I.SystemCyclesAcceptPrim, [exp !e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesAdd";_},_);_}, _, (_, e)) ->
I.PrimE (I.SystemCyclesAddPrim, [exp !e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "cyclesBurn";_},_);_}, _, (_, e)) ->
I.PrimE (I.SystemCyclesBurnPrim, [exp !e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "timeoutSet";_},_);_}, _, (_, e)) ->
I.PrimE (I.SystemTimeoutSetPrim, [exp !e])
(* Certified data *)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "setCertifiedData";_},_);_}, _, e) ->
I.PrimE (I.SetCertifiedData, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "getCertificate";_},_);_}, _, {it=S.TupE es;_}) ->
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "setCertifiedData";_},_);_}, _, (_, e)) ->
I.PrimE (I.SetCertifiedData, [exp !e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE "getCertificate";_},_);_}, _, (_, e)) ->
assert (is_empty_tup !e);
I.PrimE (I.GetCertificate, [])
(* Other *)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_},_);_}, _, {it=S.TupE es;_}) ->
I.PrimE (I.OtherPrim p, exps es)
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_},_);_}, _, e) ->
I.PrimE (I.OtherPrim p, [exp e])
| S.CallE (None, {it=S.AnnotE ({it=S.PrimE p;_},_);_}, _, (_, e)) ->
(match (!e).it with
| S.TupE es ->
I.PrimE (I.OtherPrim p, exps es)
| _ ->
I.PrimE (I.OtherPrim p, [exp !e]))
(* Optimizing array.size() *)
| S.CallE (None, {it=S.DotE (e1, proj, _); _}, _, {it=S.TupE [];_})
when T.is_array e1.note.S.note_typ && proj.it = "size" ->
| S.CallE (None, {it=S.DotE (e1, proj, _); _}, _, (_, e))
when is_empty_tup !e &&
T.is_array e1.note.S.note_typ && proj.it = "size" ->
I.PrimE (I.OtherPrim "array_len", [exp e1])
| S.CallE (None, {it=S.DotE (e1, proj, _); _}, _, {it=S.TupE [];_})
when T.(is_prim Text) e1.note.S.note_typ && proj.it = "size" ->
| S.CallE (None, {it=S.DotE (e1, proj, _); _}, _, (_, e))
when is_empty_tup !e &&
T.(is_prim Text) e1.note.S.note_typ && proj.it = "size" ->
I.PrimE (I.OtherPrim "text_len", [exp e1])
| S.CallE (None, {it=S.DotE (e1, proj, _); _}, _, {it=S.TupE [];_})
when T.(is_prim Blob) e1.note.S.note_typ && proj.it = "size" ->
| S.CallE (None, {it=S.DotE (e1, proj,_); _}, _, (_, e))
when is_empty_tup !e &&
T.(is_prim Blob) e1.note.S.note_typ && proj.it = "size" ->
I.PrimE (I.OtherPrim "blob_size", [exp e1])
(* Contextual dot call *)
| S.CallE (None, {it=S.DotE(e1, id, n);_}, inst, e2) when Option.is_some !n ->
| S.CallE (None, {it=S.DotE(e1, id, n);_}, inst, (_, e2)) when Option.is_some !n ->
let func_exp = Option.get !n in
let args = S.contextual_dot_args e1 e2 func_exp in
let args = S.contextual_dot_args e1 !e2 func_exp in
I.(PrimE (CallPrim inst.note, [exp func_exp; exp args]))
(* Normal call *)
| S.CallE (None, e1, inst, e2) ->
I.(PrimE (CallPrim inst.note, [exp e1; exp e2]))
| S.CallE (None, e1, inst, (_, e2)) ->
I.(PrimE (CallPrim inst.note, [exp e1; exp !e2]))
(* Call with parenthetical *)
| S.CallE (Some _ as par_opt, e1, inst, e2) ->
| S.CallE (Some _ as par_opt, e1, inst, (_, e2)) ->
let send e1_typ = T.(is_func e1_typ &&
(let s, _, _, _, _ = as_func e1_typ in
is_shared_sort s || is_fut note.Note.typ)) in
let ds, rs = parenthetical (send e1.note.S.note_typ) par_opt in
let v1, v2 = fresh_var "e1" e1.note.S.note_typ, fresh_var "e2" e2.note.S.note_typ in
let v1, v2 = fresh_var "e1" e1.note.S.note_typ, fresh_var "e2" (!e2).note.S.note_typ in
(blockE
(ds @ letD v1 (exp e1) :: letD v2 (exp e2) :: rs)
(ds @ letD v1 (exp e1) :: letD v2 (exp !e2) :: rs)
I.{ at; note; it = PrimE (CallPrim inst.note, [varE v1; varE v2]) }).it
| S.BlockE [] -> (unitE ()).it
| S.BlockE [{it = S.ExpD e; _}] -> (exp e).it
Expand All @@ -251,9 +261,9 @@ and exp' at note = function
| S.WhileE (e1, e2) -> (whileE (exp e1) (exp e2)).it
| S.LoopE (e1, None) -> I.LoopE (exp e1)
| S.LoopE (e1, Some e2) -> (loopWhileE (exp e1) (exp e2)).it
| S.ForE (p, {it=S.CallE (None, {it=S.DotE (arr, proj, _); _}, _, e1); _}, e2)
| S.ForE (p, {it=S.CallE (None, {it=S.DotE (arr, proj, _); _}, _, (_, e1)); _}, e2)
when T.is_array arr.note.S.note_typ && (proj.it = "vals" || proj.it = "values" || proj.it = "keys")
-> (transform_for_to_while p arr proj e1 e2).it
-> (transform_for_to_while p arr proj (!e1) e2).it
| S.ForE (p, e1, e2) -> (forE (pat p) (exp e1) (exp e2)).it
| S.DebugE e -> if !Mo_config.Flags.release_mode then (unitE ()).it else (exp e).it
| S.LabelE (l, t, e) -> I.LabelE (l.it, t.Source.note, exp e)
Expand Down
3 changes: 2 additions & 1 deletion src/mo_def/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ module Make (Cfg : Config) = struct
currently. As a compromise, we annotate only the nodes that currently
matter for the language server, i.e. the left expression of a [DotE] node. *)
let rec exp ?(arrange_typ = false) e = source e.at (annot ~arrange_typ e.note (match e.it with
| HoleE (_, e) -> "HoleE" $$ [exp !e]
| VarE x -> "VarE" $$ [id x]
| LitE l -> "LitE" $$ [lit !l]
| ActorUrlE e -> "ActorUrlE" $$ [exp e]
Expand Down Expand Up @@ -135,7 +136,7 @@ module Make (Cfg : Config) = struct
Atom (if sugar then "" else "=");
exp e'
]
| CallE (par_opt, e1, ts, e2) -> "CallE" $$ parenthetical par_opt ([exp e1] @ inst ts @ [exp e2])
| CallE (par_opt, e1, ts, (_, e2)) -> "CallE" $$ parenthetical par_opt ([exp e1] @ inst ts @ [exp !e2])
| BlockE ds -> "BlockE" $$ List.map dec ds
| NotE e -> "NotE" $$ [exp e]
| AndE (e1, e2) -> "AndE" $$ [exp e1; exp e2]
Expand Down
7 changes: 5 additions & 2 deletions src/mo_def/syntax.ml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ type sugar = bool (* Is the source of a function body a block `<block>`,
public functions as oneway, shared functions *)

type exp = (exp', typ_note) Source.annotated_phrase
and hole_sort = Named of string | Anon of int
and exp' =
| HoleE of hole_sort * exp ref
| PrimE of string (* primitive *)
| VarE of id_ref (* variable *)
| LitE of lit ref (* literal *)
Expand All @@ -188,7 +190,7 @@ and exp' =
| ArrayE of mut * exp list (* array *)
| IdxE of exp * exp (* array indexing *)
| FuncE of string * sort_pat * typ_bind list * pat * typ option * sugar * exp (* function *)
| CallE of exp option * exp * inst * exp (* function call *)
| CallE of exp option * exp * inst * arg_exp (* function call *)
| BlockE of dec list (* block (with type after avoidance) *)
| NotE of exp (* negation *)
| AndE of exp * exp (* conjunction *)
Expand All @@ -214,7 +216,8 @@ and exp' =
| IgnoreE of exp (* ignore *)
(*
| AtomE of string (* atom *)
*)
*)
and arg_exp = (bool * (exp ref))

and assert_kind =
| Runtime | Static | Invariant | Precondition | Postcondition | Concurrency of string | Loop_entry | Loop_continue | Loop_exit | Loop_invariant
Expand Down
3 changes: 2 additions & 1 deletion src/mo_frontend/definedness.ml
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ let delayed_vars : f -> S.t =

let rec exp msgs e : f = match e.it with
(* Eager uses are either first-class uses of a variable: *)
| HoleE (s, e) -> exp msgs (!e)
| VarE i -> M.singleton i.it Eager
(* Or anything that is occurring in a call (as this may call a closure): *)
| CallE (par_opt, e1, _ts, e2) -> eagerify (Option.to_list par_opt @ [e1; e2] |> exps msgs)
| CallE (par_opt, e1, _ts, (_, e2)) -> eagerify (Option.to_list par_opt @ [e1; !e2] |> exps msgs)
(* And break, return, throw can be thought of as calling a continuation: *)
| BreakE (_, e)
| RetE e
Expand Down
8 changes: 5 additions & 3 deletions src/mo_frontend/effect.ml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ let effect_exp (exp:Syntax.exp) : T.eff = eff exp
(* infer the effect of an expression, assuming all sub-expressions are correctly effect-annotated es *)
let rec infer_effect_exp (exp:Syntax.exp) : T.eff =
match exp.it with
| HoleE _ -> T.Triv (* TBR *)
| CallE (_, exp1, inst, exp2) when is_async_call exp1 inst exp2 ->
T.Await
| CallE (Some par, exp1, _, exp2) ->
map_max_effs effect_exp [par; exp1; exp2]
| CallE (Some par, exp1, _, (_, exp2)) ->
map_max_effs effect_exp [par; exp1; !exp2]
| PrimE _
| VarE _
| LitE _
Expand Down Expand Up @@ -83,14 +84,15 @@ let rec infer_effect_exp (exp:Syntax.exp) : T.eff =
| IdxE (exp1, exp2)
| RelE (_, exp1, _, exp2)
| AssignE (exp1, exp2)
| CallE (None, exp1, _, exp2)
| AndE (exp1, exp2)
| OrE (exp1, exp2)
| ImpliesE (exp1, exp2)
| WhileE (exp1, exp2)
| LoopE (exp1, Some exp2)
| ForE (_, exp1, exp2) ->
map_max_effs effect_exp [exp1; exp2]
| CallE (None, exp1, _, (_, exp2)) ->
map_max_effs effect_exp [exp1; !exp2]
| DebugE exp1 ->
effect_exp exp1
| ToCandidE exps
Expand Down
Loading