Skip to content

Commit 880ca0c

Browse files
authored
Fix apparent nondeterminism in pattern matching compilation. (#7557)
Fixes #7465 Pattern matching compilation has some code to find if some exit is used at least 3 times. This is done by iterating on a hash table. However, the exit id is generated based on a global counter, which determines which exit value `n` is generated. This can affect the order in which the hash table is traversed, as the exit id is hashed. This PR makes the code generation deterministic by adding a tie-breaker if more than id leads to the same exit being used at least 3 times: take the smaller id.
1 parent e272f03 commit 880ca0c

File tree

5 files changed

+89
-13
lines changed

5 files changed

+89
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
- Prop punning when types don't match results in I/O error: _none_: No such file or directory. https://github.com/rescript-lang/rescript/pull/7533
4848
- Fix partial application with user-defined function types. https://github.com/rescript-lang/rescript/pull/7548
4949
- Fix doc comment before variant throwing syntax error. https://github.com/rescript-lang/rescript/pull/7535
50+
- Fix apparent non-determinism in generated code for pattern matching. https://github.com/rescript-lang/rescript/pull/7557
5051

5152
#### :nail_care: Polish
5253

compiler/ml/matching.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1808,7 +1808,10 @@ let reintroduce_fail sw =
18081808
let i_max = ref (-1) and max = ref (-1) in
18091809
Hashtbl.iter
18101810
(fun i c ->
1811-
if c > !max then (
1811+
if
1812+
c > !max || (c = !max && i > !i_max)
1813+
(* tie-break for determinism: choose the smallest index*)
1814+
then (
18121815
i_max := i;
18131816
max := c))
18141817
t;

tests/tests/src/NondetPatterMatch.mjs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Generated by ReScript, PLEASE EDIT WITH CARE
2+
3+
4+
function f1(action) {
5+
if (typeof action === "object") {
6+
switch (action.TAG) {
7+
case "WithPayload5" :
8+
case "WithPayload6" :
9+
case "WithPayload7" :
10+
case "WithPayload8" :
11+
console.log("hello");
12+
break;
13+
}
14+
}
15+
return 42;
16+
}
17+
18+
function f2(action) {
19+
if (typeof action === "object") {
20+
switch (action.TAG) {
21+
case "WithPayload5" :
22+
case "WithPayload6" :
23+
case "WithPayload7" :
24+
case "WithPayload8" :
25+
console.log("hello");
26+
break;
27+
}
28+
}
29+
return 42;
30+
}
31+
32+
export {
33+
f1,
34+
f2,
35+
}
36+
/* No side effect */

tests/tests/src/NondetPatterMatch.res

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
type action =
2+
| WithoutPayload1
3+
| WithoutPayload2
4+
| WithPayload2({y: int})
5+
| WithPayload3({y: int})
6+
| WithPayload5({y: int})
7+
| WithPayload6({x: int})
8+
| WithPayload7({y: int})
9+
| WithPayload8({x: int})
10+
11+
let f1 = (action: action) => {
12+
switch action {
13+
| WithPayload5(_)
14+
| WithPayload6(_)
15+
| WithPayload7(_)
16+
| WithPayload8(_) =>
17+
Console.log("hello")
18+
| _ => ()
19+
}
20+
42
21+
}
22+
23+
let f2 = (action: action) => {
24+
switch action {
25+
| WithPayload5(_)
26+
| WithPayload6(_)
27+
| WithPayload7(_)
28+
| WithPayload8(_) =>
29+
Console.log("hello")
30+
| _ => ()
31+
}
32+
42
33+
}

tests/tests/src/adt_optimize_test.mjs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,22 @@ function f8(x) {
134134

135135
function f9(x) {
136136
if (typeof x !== "object") {
137-
if (x === "T63") {
138-
return 3;
139-
} else {
140-
return 1;
137+
switch (x) {
138+
case "T60" :
139+
case "T61" :
140+
case "T62" :
141+
return 1;
142+
default:
143+
return 3;
144+
}
145+
} else {
146+
switch (x.TAG) {
147+
case "T64" :
148+
case "T65" :
149+
return 2;
150+
default:
151+
return 3;
141152
}
142-
}
143-
switch (x.TAG) {
144-
case "T64" :
145-
case "T65" :
146-
return 2;
147-
case "T66" :
148-
case "T68" :
149-
return 3;
150153
}
151154
}
152155

0 commit comments

Comments
 (0)