Skip to content

Commit

Permalink
bug: fix and repro for issue-4236 (bad renaming of or-patterns) (#4244
Browse files Browse the repository at this point in the history
)

Fix `ir_def/rename.ml`, for renaming bound variables, to properly handle our newly extended `or`-patterns.

With `or`-patterns, the same variable may have several binding occurrences, so renaming has to treat all of these consistently.
Instead of renaming variables on the fly, for `or`-patterns, we now determine the set of bound identifiers bound by all the 
patterns beneath the `or`, rename them, and substitute consistently within the `or`-pattern to produce the renamed `or`-pattern.
  • Loading branch information
crusso authored Oct 16, 2023
1 parent e8c2569 commit 5d1aa54
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 22 deletions.
4 changes: 3 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

* motoko (`moc`)

* bugfix: Unsuccessful Candid decoding of an optional array now default to null instead of crashing (#4240).
* bugfix: fix assertion failure renaming `or`-patterns (#4236, #4224).

* bugfix: unsuccessful Candid decoding of an optional array now defaults to null instead of crashing (#4240).

* bugfix: Candid decoding of an optional, unknown variant with a payload now succeeds instead of crashing (#4238).

Expand Down
79 changes: 58 additions & 21 deletions src/ir_def/rename.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ let id_bind rho i =
let i' = fresh_id i in
(i', Renaming.add i i' rho)

let rec ids_bind rho = function
| [] -> rho
| i::is' ->
let (i', rho') = id_bind rho i in
ids_bind rho' is'

let arg_bind rho a =
let i' = fresh_id a.it in
({a with it = i'}, Renaming.add a.it i' rho)
Expand Down Expand Up @@ -81,37 +87,68 @@ and args rho as_ =
(a'::as_', rho'')

and pat rho p =
let p',rho = pat' rho p.it in
{p with it = p'}, rho
let p', rho = pat' rho p.it in
{p with it = p'}, rho

and pat' rho = function
| WildP as p -> (p, rho)
| VarP i ->
| VarP i ->
let i, rho' = id_bind rho i in
(VarP i, rho')
| TupP ps -> let (ps, rho') = pats rho ps in
(TupP ps, rho')
| ObjP pfs ->
(VarP i, rho')
| TupP ps ->
let (ps, rho') = pats rho ps in
(TupP ps, rho')
| ObjP pfs ->
let (pats, rho') = pats rho (pats_of_obj_pat pfs) in
(ObjP (replace_obj_pat pfs pats), rho')
| LitP _ as p -> (p, rho)
| OptP p -> let (p', rho') = pat rho p in
(OptP p', rho')
| TagP (i, p) -> let (p', rho') = pat rho p in
(TagP (i, p'), rho')
| AltP (p1, p2) -> assert(Freevars.(M.is_empty (pat p1)));
assert(Freevars.(M.is_empty (pat p2)));
let (p1', _) = pat rho p1 in
let (p2' ,_) = pat rho p2 in
(AltP (p1', p2'), rho)
| LitP _ as p ->
(p, rho)
| OptP p ->
let (p', rho') = pat rho p in
(OptP p', rho')
| TagP (i, p) ->
let (p', rho') = pat rho p in
(TagP (i, p'), rho')
| AltP (p1, p2) ->
let is1 = Freevars.M.keys (Freevars.pat p1) in
assert begin
let is2 = Freevars.M.keys (Freevars.pat p1) in
List.compare String.compare is1 is2 = 0
end;
let rho' = ids_bind rho is1 in
(AltP (pat_subst rho' p1, pat_subst rho' p2), rho')

and pats rho ps =
match ps with
| [] -> ([],rho)
| [] -> ([], rho)
| p::ps ->
let (p', rho') = pat rho p in
let (ps', rho'') = pats rho' ps in
(p'::ps', rho'')
let (p', rho') = pat rho p in
let (ps', rho'') = pats rho' ps in
(p'::ps', rho'')

and pat_subst rho p =
let p' = pat_subst' rho p.it in
{p with it = p'}

and pat_subst' rho = function
| WildP as p -> p
| VarP i ->
VarP (id rho i)
| TupP ps ->
TupP (pats_subst rho ps)
| ObjP pfs ->
let pats = pats_subst rho (pats_of_obj_pat pfs) in
ObjP (replace_obj_pat pfs pats)
| LitP _ as p -> p
| OptP p ->
OptP (pat_subst rho p)
| TagP (i, p) ->
TagP (i, pat_subst rho p)
| AltP (p1, p2) ->
AltP (pat_subst rho p1, pat_subst rho p2)

and pats_subst rho ps =
List.map (pat_subst rho) ps

and case rho (c : case) =
{c with it = case' rho c.it}
Expand Down
24 changes: 24 additions & 0 deletions test/run-drun/issue-4236.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// crashes in renaming of or-pattern
// (NB: renaming itself only forced by actor class (to avoid capture when class arg binds parameters)
actor class () {
type Account = {
holder : Principal;
balance : Nat;
};
type Amount = {
amount : Nat
};
type Movement = {
#deposit : Account and Amount;
#withdraw : Account and Amount;
};

func getAccount(m : Movement) : Account =
switch m {
case (#deposit accnt or #withdraw accnt) accnt
}
}

//SKIP run
//SKIP run-ir
//SKIP run-low
2 changes: 2 additions & 0 deletions test/run-drun/ok/issue-4236.drun-run.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101
ingress Completed: Reply: 0x4449444c0000

0 comments on commit 5d1aa54

Please sign in to comment.