Skip to content

Commit 29644ec

Browse files
cristianoccknitt
authored andcommitted
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. # Conflicts: # CHANGELOG.md # jscomp/test/UntaggedVariants.js
1 parent 5936649 commit 29644ec

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
@@ -14,6 +14,7 @@
1414

1515
#### :bug: Bug Fix
1616

17+
- Fix issue of incorrect switch cases with identical bodies when mixing object and array. https://github.com/rescript-lang/rescript-compiler/pull/6792
1718
- Fix formatter eats comments on the first argument of a uncurried function. https://github.com/rescript-lang/rescript-compiler/pull/6763
1819
- Fix formatter removes parens in pipe operator with anonymous uncurried function. https://github.com/rescript-lang/rescript-compiler/pull/6766
1920

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,
@@ -511,6 +511,7 @@ and compile_general_cases :
511511
_ -> ('a * J.case_clause) list -> J.statement) ->
512512
switch_exp: J.expression ->
513513
default: default_case ->
514+
?merge_cases: ('a -> 'a -> bool) ->
514515
('a * Lam.t) list ->
515516
J.block =
516517
fun (type a)
@@ -524,6 +525,7 @@ and compile_general_cases :
524525
)
525526
~(switch_exp : J.expression)
526527
~(default : default_case)
528+
?(merge_cases = fun _ _ -> true)
527529
(cases : (a * Lam.t) list) ->
528530
match (cases, default) with
529531
| [], Default lam -> Js_output.output_as_block (compile_lambda cxt lam)
@@ -586,7 +588,7 @@ and compile_general_cases :
586588
Some (Js_output.output_as_block (compile_lambda cxt lam))
587589
in
588590
let body =
589-
group_apply cases (fun last (switch_case, lam) ->
591+
group_apply ~merge_cases cases (fun last (switch_case, lam) ->
590592
if last then
591593
(* merge and shared *)
592594
let switch_body, should_break =
@@ -768,25 +770,29 @@ and compile_untagged_cases ~cxt ~switch_exp ~default ~block_cases cases =
768770
in
769771
E.emit_check check
770772
in
771-
let is_not_typeof (l, _) = match l with
772-
| Ast_untagged_variants.Untagged (InstanceType _) -> true
773-
| _ -> false in
773+
let tag_is_not_typeof = function
774+
| Ast_untagged_variants.Untagged (InstanceType _) -> true
775+
| _ -> false in
776+
let clause_is_not_typeof (tag, _) = tag_is_not_typeof tag in
774777
let switch ?default ?declaration e clauses =
775-
let (not_typeof_clauses, typeof_clauses) = List.partition is_not_typeof clauses in
778+
let (not_typeof_clauses, typeof_clauses) = List.partition clause_is_not_typeof clauses in
776779
let rec build_if_chain remaining_clauses = (match remaining_clauses with
777780
| (Ast_untagged_variants.Untagged (InstanceType instanceType), {J.switch_body}) :: rest ->
778781
S.if_ (E.emit_check (IsInstanceOf (instanceType, Expr e)))
779782
(switch_body)
780783
~else_:([build_if_chain rest])
781784
| _ -> S.string_switch ?default ?declaration (E.typeof e) typeof_clauses) in
782785
build_if_chain not_typeof_clauses in
786+
let merge_cases tag1 tag2 = (* only merge typeof cases, as instanceof cases are pulled out into if-then-else *)
787+
not (tag_is_not_typeof tag1 || tag_is_not_typeof tag2) in
783788
cases |> compile_general_cases
784789
~make_exp: E.tag_type
785790
~eq_exp: mk_eq
786791
~cxt
787792
~switch
788793
~switch_exp
789794
~default
795+
~merge_cases
790796
791797
and compile_stringswitch l cases default (lambda_cxt : Lam_compile_context.t) =
792798
(* 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)