Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make variant pattern matching not rely on tags being int #6085

Merged
merged 5 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ These are only breaking changes for unformatted code.
- New internal representation for uncurried functions using built-in type `function$<fun_type, arity>` this avoids having to declare all the possible arities ahead of time https://github.com/rescript-lang/rescript-compiler/pull/5870
- PPX V3: allow uncurried `make` function and treat it like a curried one https://github.com/rescript-lang/rescript-compiler/pull/6081
- Add support for `|>` in uncurried mode by desugaring it https://github.com/rescript-lang/rescript-compiler/pull/6083
- Change the compilation of pattern matching for variants so it does not depends on variats being integers https://github.com/rescript-lang/rescript-compiler/pull/6085

# 10.1.4

Expand Down
3 changes: 3 additions & 0 deletions jscomp/core/js_exp_make.ml
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,9 @@ let string_equal ?comment (e0 : t) (e1 : t) : t =
let is_type_number ?comment (e : t) : t =
string_equal ?comment (typeof e) (str "number")

let is_tag (e : t) : t =
string_equal ~comment:"tag" (typeof e) (str "number")

let is_type_string ?comment (e : t) : t =
string_equal ?comment (typeof e) (str "string")

Expand Down
1 change: 1 addition & 0 deletions jscomp/core/js_exp_make.mli
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ val eq_null_undefined_boolean : ?comment:string -> t -> t -> t
val neq_null_undefined_boolean : ?comment:string -> t -> t -> t

val is_type_number : ?comment:string -> t -> t
val is_tag : t -> t

val is_type_string : ?comment:string -> t -> t

Expand Down
2 changes: 1 addition & 1 deletion jscomp/core/lam_compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ and compile_switch (switch_arg : Lam.t) (sw : Lam.lambda_switch)
else
(* [e] will be used twice *)
let dispatch e =
S.if_ (E.is_type_number e)
S.if_ (E.is_tag e)
(compile_cases cxt e sw_consts sw_num_default get_const_name)
(* default still needed, could simplified*)
~else_:
Expand Down
16 changes: 8 additions & 8 deletions jscomp/core/lam_convert.ml
Original file line number Diff line number Diff line change
Expand Up @@ -119,25 +119,24 @@ let lam_is_var (x : Lam.t) (y : Ident.t) =
(** Make sure no int range overflow happens
also we only check [int]
*)
let happens_to_be_diff (sw_consts : (int * Lambda.lambda) list) : int option =
let happens_to_be_diff (sw_consts : (int * Lambda.lambda) list) sw_names : int option =
match sw_consts with
| ( a,
Lconst (Const_pointer (a0, Pt_constructor _) | Const_base (Const_int a0))
Lconst (Const_base (Const_int a0))
)
:: ( b,
Lconst
(Const_pointer (b0, Pt_constructor _) | Const_base (Const_int b0)) )
(Const_base (Const_int b0)) )
:: rest
when no_over_flow a && no_over_flow a0 && no_over_flow b && no_over_flow b0
when sw_names = None && no_over_flow a && no_over_flow a0 && no_over_flow b && no_over_flow b0
->
let diff = a0 - a in
if b0 - b = diff then
if
Ext_list.for_all rest (fun (x, lam) ->
match lam with
| Lconst
( Const_pointer (x0, Pt_constructor _)
| Const_base (Const_int x0) )
( Const_base (Const_int x0) )
when no_over_flow x0 && no_over_flow x ->
x0 - x = diff
| _ -> false)
Expand Down Expand Up @@ -701,8 +700,9 @@ let convert (exports : Set_ident.t) (lam : Lambda.lambda) :
sw_numblocks = 0;
sw_consts;
sw_numconsts;
sw_names;
} -> (
match happens_to_be_diff sw_consts with
match happens_to_be_diff sw_consts sw_names with
| Some 0 -> e
| Some i ->
prim ~primitive:Paddint
Expand All @@ -712,7 +712,7 @@ let convert (exports : Set_ident.t) (lam : Lambda.lambda) :
Lam.const (Const_int { i = Int32.of_int i; comment = None });
]
Location.none
| None ->
| _ ->
Lam.switch e
{
sw_failaction = None;
Expand Down
24 changes: 15 additions & 9 deletions jscomp/ml/matching.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2348,19 +2348,25 @@ let combine_constructor sw_names loc arg ex_pat cstr partial ctx def
match
(cstr.cstr_consts, cstr.cstr_nonconsts, consts, nonconsts)
with
| (1, 1, [0, act1], [0, act2]) ->
(* Typically, match on lists, will avoid isint primitive in that
case *)
| (1, 1, [0, act1], [0, act2])
when cstr.cstr_name = "::" || cstr.cstr_name = "[]" || Datarepr.constructor_has_optional_shape cstr
->
(* Typically, match on lists, will avoid isint primitive in that
case *)
let arg =
if !Config.bs_only && Datarepr.constructor_has_optional_shape cstr then
Lprim(is_not_none_bs_primitve , [arg], loc)
else arg
in
Lifthenelse(arg, act2, act1)
| (2,0, [(i1,act1); (_,act2)],[]) ->
if i1 = 0 then Lifthenelse(arg, act2, act1)
else Lifthenelse (arg,act1,act2)
| (n,0,_,[]) -> (* The type defines constant constructors only *)
| (2,0, [(i1,act1); (_,act2)],[]) when
(match act1, act2 with
| Lconst (Const_pointer (_, Pt_constructor _ )), _ -> false
| _, Lconst (Const_pointer (_, Pt_constructor _ )) -> false
| _ -> true) ->
if i1 = 0 then Lifthenelse(arg, act2, act1)
else Lifthenelse (arg, act1, act2)
| (n,0,_,[]) when false (* relies on tag being an int *) -> (* The type defines constant constructors only *)
call_switcher loc fail_opt arg 0 (n-1) consts sw_names
| (n, _, _, _) ->
let act0 =
Expand All @@ -2373,15 +2379,15 @@ let combine_constructor sw_names loc arg ex_pat cstr partial ctx def
else None
| None,_ -> same_actions nonconsts in
match act0 with
| Some act ->
| Some act when false (* relies on tag being an int *) ->
Lifthenelse
(Lprim (Pisint, [arg], loc),
call_switcher loc
fail_opt arg
0 (n-1) consts sw_names,
act)
(* Emit a switch, as bytecode implements this sophisticated instruction *)
| None ->
| _ ->
let sw =
{sw_numconsts = cstr.cstr_consts; sw_consts = consts;
sw_numblocks = cstr.cstr_nonconsts; sw_blocks = nonconsts;
Expand Down
73 changes: 55 additions & 18 deletions jscomp/test/adt_optimize_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,66 @@


function f(x) {
return x + 1 | 0;
switch (x) {
case /* A */0 :
return 1;
case /* B */1 :
return 2;
case /* C */2 :
return 3;

}
}

function f_0(x) {
return x - 1 | 0;
switch (x) {
case /* A */0 :
return -1;
case /* B */1 :
return 0;
case /* C */2 :
return 1;

}
}

function f2(param) {
if (param >= 3) {
return /* T003 */3;
} else {
return param;
}
switch (param) {
case 0 :
return /* T000 */0;
case 1 :
return /* T001 */1;
case 2 :
return /* T002 */2;

}
}

function f3(param) {
return param;
switch (param) {
case /* X0 */0 :
return /* Y0 */0;
case /* X1 */1 :
return /* Y1 */1;
case /* X2 */2 :
return /* Y2 */2;
case /* X3 */3 :
return /* Y3 */3;
case /* X4 */4 :
return /* Y4 */4;

}
}

function f4(param) {
return 3;
}

function f5(param) {
if (typeof param === "number") {
if (/* tag */typeof param === "number") {
switch (param) {
case /* A */0 :
return 1;
Expand All @@ -49,19 +84,21 @@ function f5(param) {
}

function f6(param) {
if (typeof param === "number") {
if (param >= 2) {
return 2;
} else {
return 0;
}
} else {
if (/* tag */typeof param !== "number") {
return 1;
}
switch (param) {
case /* A */0 :
case /* B */1 :
return 0;
case /* F */2 :
return 2;

}
}

function f7(param) {
if (typeof param === "number") {
if (/* tag */typeof param === "number") {
switch (param) {
case /* A */0 :
return 1;
Expand All @@ -85,7 +122,7 @@ function f7(param) {
}

function f8(param) {
if (typeof param === "number") {
if (/* tag */typeof param === "number") {
switch (param) {
case /* T60 */0 :
case /* T61 */1 :
Expand All @@ -105,7 +142,7 @@ function f8(param) {
}

function f9(param) {
if (typeof param === "number") {
if (/* tag */typeof param === "number") {
if (param === /* T63 */3) {
return 3;
} else {
Expand All @@ -124,7 +161,7 @@ function f9(param) {
}

function f10(param) {
if (typeof param === "number") {
if (/* tag */typeof param === "number") {
switch (param) {
case /* T60 */0 :
return 0;
Expand All @@ -150,7 +187,7 @@ function f10(param) {
}

function f11(x) {
if (typeof x === "number") {
if (/* tag */typeof x === "number") {
return 2;
}
if (x.TAG === /* D */0) {
Expand Down
Loading