Skip to content

Commit 326fd32

Browse files
committed
Fix issue of incorrect switch cases with identical bodies when mixing object and array.
Fixes #6789 The issue happens when 2 cases, here `Object` and `Array`, have identical body (here `Console.log(v)`). The switch-generation code, which was not designed with untagged unions in mind, merges the two cases into one (and makes one of the two empty). However, for `Object` and `Array`, what's generated is not a straight switch, but a mix of `if-then-else` and `switch`. This means that the `Object` and `Array` cases are apart in the generated code, and merging them (making one empty) is wrong.
1 parent c7dedbb commit 326fd32

File tree

4 files changed

+80
-9
lines changed

4 files changed

+80
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#### :bug: Bug Fix
3232

3333
- Allow to use exotic ParscalCased identifiers for types. https://github.com/rescript-lang/rescript-compiler/pull/6777
34+
- Fix issue of incorrect switch cases with identical bodies when mixing object and array. https://github.com/rescript-lang/rescript-compiler/pull/6792
3435

3536
#### :house: Internal
3637

jscomp/core/lam_compile.ml

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ let morph_declare_to_assign (cxt : Lam_compile_context.t) k =
121121
k { cxt with continuation = Assign did } (Some (kind, did))
122122
| _ -> k cxt None
123123

124-
let group_apply cases callback =
124+
let group_apply ~merge_cases cases callback =
125125
Ext_list.flat_map
126-
(Ext_list.stable_group cases (fun (_, lam) (_, lam1) ->
127-
Lam.eq_approx lam lam1))
126+
(Ext_list.stable_group cases (fun (tag1, lam) (tag2, lam1) ->
127+
merge_cases tag1 tag2 && Lam.eq_approx lam lam1))
128128
(fun group -> Ext_list.map_last group callback)
129129
(* TODO:
130130
for expression generation,
@@ -509,6 +509,7 @@ and compile_general_cases :
509509
_ -> ('a * J.case_clause) list -> J.statement) ->
510510
switch_exp: J.expression ->
511511
default: default_case ->
512+
?merge_cases: ('a -> 'a -> bool) ->
512513
('a * Lam.t) list ->
513514
J.block =
514515
fun (type a)
@@ -522,6 +523,7 @@ and compile_general_cases :
522523
)
523524
~(switch_exp : J.expression)
524525
~(default : default_case)
526+
?(merge_cases = fun _ _ -> true)
525527
(cases : (a * Lam.t) list) ->
526528
match (cases, default) with
527529
| [], Default lam -> Js_output.output_as_block (compile_lambda cxt lam)
@@ -584,7 +586,7 @@ and compile_general_cases :
584586
Some (Js_output.output_as_block (compile_lambda cxt lam))
585587
in
586588
let body =
587-
group_apply cases (fun last (switch_case, lam) ->
589+
group_apply ~merge_cases cases (fun last (switch_case, lam) ->
588590
if last then
589591
(* merge and shared *)
590592
let switch_body, should_break =
@@ -766,25 +768,29 @@ and compile_untagged_cases ~cxt ~switch_exp ~default ~block_cases cases =
766768
in
767769
E.emit_check check
768770
in
769-
let is_not_typeof (l, _) = match l with
770-
| Ast_untagged_variants.Untagged (InstanceType _) -> true
771-
| _ -> false in
771+
let tag_is_not_typeof = function
772+
| Ast_untagged_variants.Untagged (InstanceType _) -> true
773+
| _ -> false in
774+
let clause_is_not_typeof (tag, _) = tag_is_not_typeof tag in
772775
let switch ?default ?declaration e clauses =
773-
let (not_typeof_clauses, typeof_clauses) = List.partition is_not_typeof clauses in
776+
let (not_typeof_clauses, typeof_clauses) = List.partition clause_is_not_typeof clauses in
774777
let rec build_if_chain remaining_clauses = (match remaining_clauses with
775778
| (Ast_untagged_variants.Untagged (InstanceType instance_type), {J.switch_body}) :: rest ->
776779
S.if_ (E.emit_check (IsInstanceOf (instance_type, Expr e)))
777780
(switch_body)
778781
~else_:([build_if_chain rest])
779782
| _ -> S.string_switch ?default ?declaration (E.typeof e) typeof_clauses) in
780783
build_if_chain not_typeof_clauses in
784+
let merge_cases tag1 tag2 = (* only merge typeof cases, as instanceof cases are pulled out into if-then-else *)
785+
not (tag_is_not_typeof tag1 || tag_is_not_typeof tag2) in
781786
cases |> compile_general_cases
782787
~make_exp: E.tag_type
783788
~eq_exp: mk_eq
784789
~cxt
785790
~switch
786791
~switch_exp
787792
~default
793+
~merge_cases
788794
789795
and compile_stringswitch l cases default (lambda_cxt : Lam_compile_context.t) =
790796
(* TODO might better optimization according to the number of cases

jscomp/test/UntaggedVariants.js

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/UntaggedVariants.res

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,31 @@ module Aliased = {
431431
module OnlyOne = {
432432
@unboxed type onlyOne = OnlyOne
433433
let onlyOne = OnlyOne
434-
}
434+
}
435+
436+
module MergeCases = {
437+
type obj = {name: string}
438+
439+
@unboxed
440+
type t =
441+
| Boolean(bool)
442+
| Object(obj)
443+
| Array(array<int>)
444+
| Date(Js.Date.t)
445+
446+
let should_not_merge = x =>
447+
switch x {
448+
| Object(_) => "do not merge"
449+
| Array(_) => "do not merge"
450+
| Date(_) => "do not merge"
451+
| Boolean(_) => "boolean"
452+
}
453+
454+
let can_merge = x =>
455+
switch x {
456+
| Object(_) => "merge"
457+
| Array(_) => "do not merge"
458+
| Date(_) => "do not merge"
459+
| Boolean(_) => "merge"
460+
}
461+
}

0 commit comments

Comments
 (0)