From 4975e3646a9f56bd8f0dcb0ca16207490276ca47 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 7 Nov 2024 14:21:01 +0100 Subject: [PATCH 1/6] More boolean simplifications in generated code. https://github.com/rescript-lang/rescript-compiler/issues/7142 --- compiler/core/js_exp_make.ml | 507 +++++++++++++----------- tests/tests/src/core/Core_JsonTests.mjs | 4 +- 2 files changed, 287 insertions(+), 224 deletions(-) diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index 151ed9b1db..0cf85416d6 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -719,7 +719,8 @@ let rec push_negation (e : t) : t option = - [(typeof x === "boolean") && (x !== true/false)] -> unchanged - [(typeof x === "string") && (x !== "abc")] -> unchanged - [(typeof x === "number") && (x !== 123)] -> unchanged - - [(typeof x === "boolean" | "string" | "number") && (x !== boolean/null/undefined/123/"hello")] -> [typeof x === ...] + - [(typeof x === "object") && (x !== null)] -> unchanged + - [(typeof x === "boolean" | "string" | "number" | "object") && (x !== boolean/null/undefined/123/"hello")] -> [typeof x === ...] - [(Array.isArray(x)) && (x !== boolean/null/undefined/123/"hello")] -> [Array.isArray(x)] Equality optimizations: @@ -730,210 +731,256 @@ let rec push_negation (e : t) : t option = attempting to reduce it to a simpler form. If no simplification is possible, returns [None]. *) -let rec simplify_and (e1 : t) (e2 : t) : t option = +let rec simplify_and ~n (e1 : t) (e2 : t) : t option = if debug then - Printf.eprintf "simplify_and %s %s\n" (!string_of_expression e1) - (!string_of_expression e2); - match (e1.expression_desc, e2.expression_desc) with - | Bool false, _ -> Some false_ - | _, Bool false -> Some false_ - | Bool true, _ -> Some e2 - | _, Bool true -> Some e1 - | Bin (And, a, b), _ -> ( - let ao = simplify_and a e2 in - let bo = simplify_and b e2 in - match (ao, bo) with - | None, _ | _, None -> None - | Some a_, Some b_ -> ( - match simplify_and a_ b_ with - | None -> Some {expression_desc = Bin (And, a_, b_); comment = None} - | Some e -> Some e)) - | Bin (Or, a, b), _ -> ( - let ao = simplify_and a e2 in - let bo = simplify_and b e2 in - match (ao, bo) with - | None, _ | _, None -> None - | Some a_, Some b_ -> ( - match simplify_or a_ b_ with - | None -> Some {expression_desc = Bin (Or, a_, b_); comment = None} - | Some e -> Some e)) - | ( Bin - ( ((EqEqEq | NotEqEq) as op1), - {expression_desc = Var i1}, - {expression_desc = Bool b1} ), - Bin - ( ((EqEqEq | NotEqEq) as op2), - {expression_desc = Var i2}, - {expression_desc = Bool b2} ) ) - when Js_op_util.same_vident i1 i2 -> - let op_eq = op1 = op2 in - let consistent = if op_eq then b1 = b2 else b1 <> b2 in - if consistent then Some e1 else Some false_ - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean"}} ), - (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) as b) - ) - | ( (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) as b), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean"}} ) ) - when Js_op_util.same_vident ia ib -> - Some {expression_desc = b; comment = None} - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "string"}} ), - (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Str _}) as s) - ) - | ( (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Str _}) as s), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "string"}} ) ) - when Js_op_util.same_vident ia ib -> - Some {expression_desc = s; comment = None} - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "number"}} ), - (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Number _}) as - i) ) - | ( (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Number _}) as - i), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "number"}} ) ) - when Js_op_util.same_vident ia ib -> - Some {expression_desc = i; comment = None} - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean" | "string" | "number"}} ), - Bin - ( EqEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) - | ( Bin - ( EqEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean" | "string" | "number"}} ) ) - when Js_op_util.same_vident ia ib -> - (* Note: cases boolean / Bool _, number / Number _, string / Str _ are handled above *) - Some false_ - | ( Call - ( {expression_desc = Str {txt = "Array.isArray"}}, - [{expression_desc = Var ia}], - _ ), - Bin - ( EqEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) - | ( Bin - ( EqEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), - Call - ( {expression_desc = Str {txt = "Array.isArray"}}, - [{expression_desc = Var ia}], - _ ) ) - when Js_op_util.same_vident ia ib -> - Some false_ - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean"}} ), - Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) ) - | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean"}} ) ) - when Js_op_util.same_vident ia ib -> - None - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "string"}} ), - Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Str _}) ) - | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Str _}), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "string"}} ) ) - when Js_op_util.same_vident ia ib -> - None - | ( Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "number"}} ), - Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Number _}) ) - | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Number _}), - Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "number"}} ) ) - when Js_op_util.same_vident ia ib -> - None - | ( (Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean" | "string" | "number"}} ) as - typeof), - Bin - ( NotEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) - | ( Bin - ( NotEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ), - (Bin - ( EqEqEq, - {expression_desc = Typeof {expression_desc = Var ia}}, - {expression_desc = Str {txt = "boolean" | "string" | "number"}} ) as - typeof) ) - when Js_op_util.same_vident ia ib -> - (* Note: cases boolean / Bool _, number / Number _, string / Str _ are handled above *) - Some {expression_desc = typeof; comment = None} - | ( (Call - ( {expression_desc = Str {txt = "Array.isArray"}}, - [{expression_desc = Var ia}], - _ ) as is_array), - Bin - ( NotEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) - | ( Bin - ( NotEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), - (Call - ( {expression_desc = Str {txt = "Array.isArray"}}, - [{expression_desc = Var ia}], - _ ) as is_array) ) - when Js_op_util.same_vident ia ib -> - Some {expression_desc = is_array; comment = None} - | x, y when x = y -> Some e1 - | ( Bin - ( EqEqEq, - {expression_desc = Var ia}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), - Bin - ( EqEqEq, - {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) - when Js_op_util.same_vident ia ib -> - (* Note: case x = y is handled above *) - Some false_ - | _ -> None + Printf.eprintf "%s simplify_and %s %s\n" + (String.make (n * 2) ' ') + (!string_of_expression e1) (!string_of_expression e2); + let res = + match (e1.expression_desc, e2.expression_desc) with + | Bool false, _ -> Some false_ + | _, Bool false -> Some false_ + | Bool true, _ -> Some e2 + | _, Bool true -> Some e1 + | Bin (And, a, b), _ -> ( + match simplify_and ~n:(n + 1) a b with + | Some e -> simplify_and_force ~n:(n + 1) e e2 + | None -> ( + let ao = simplify_and ~n:(n + 1) a e2 in + let bo = simplify_and ~n:(n + 1) b e2 in + match (ao, bo) with + | None, None -> None + | Some a_, None -> simplify_and_force ~n:(n + 1) a_ e2 + | None, Some b_ -> simplify_and_force ~n:(n + 1) e1 b_ + | Some a_, Some b_ -> simplify_and_force ~n:(n + 1) a_ b_)) + | _, Bin (And, a, b) -> + simplify_and ~n:(n + 1) + {expression_desc = Bin (And, e1, a); comment = None} + b + | Bin (Or, a, b), _ -> ( + let ao = simplify_and ~n:(n + 1) a e2 in + let bo = simplify_and ~n:(n + 1) b e2 in + match (ao, bo) with + | None, _ | _, None -> None + | Some a_, Some b_ -> simplify_or_force ~n:(n + 1) a_ b_) + | ( Bin + ( ((EqEqEq | NotEqEq) as op1), + {expression_desc = Var i1}, + {expression_desc = Bool b1} ), + Bin + ( ((EqEqEq | NotEqEq) as op2), + {expression_desc = Var i2}, + {expression_desc = Bool b2} ) ) + when Js_op_util.same_vident i1 i2 -> + let op_eq = op1 = op2 in + let consistent = if op_eq then b1 = b2 else b1 <> b2 in + if consistent then Some e1 else Some false_ + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ), + (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) as + b) ) + | ( (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) as + b), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ) ) + when Js_op_util.same_vident ia ib -> + Some {expression_desc = b; comment = None} + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "string"}} ), + (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Str _}) as + s) ) + | ( (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Str _}) as s), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "string"}} ) ) + when Js_op_util.same_vident ia ib -> + Some {expression_desc = s; comment = None} + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "number"}} ), + (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Number _}) + as i) ) + | ( (Bin (EqEqEq, {expression_desc = Var ib}, {expression_desc = Number _}) + as i), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "number"}} ) ) + when Js_op_util.same_vident ia ib -> + Some {expression_desc = i; comment = None} + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean" | "string" | "number"}} ), + Bin + ( EqEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ) ) + | ( Bin + ( EqEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean" | "string" | "number"}} ) ) + when Js_op_util.same_vident ia ib -> + (* Note: cases boolean / Bool _, number / Number _, string / Str _ are handled above *) + Some false_ + | ( Call + ( {expression_desc = Str {txt = "Array.isArray"}}, + [{expression_desc = Var ia}], + _ ), + Bin + ( EqEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ) ) + | ( Bin + ( EqEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ), + Call + ( {expression_desc = Str {txt = "Array.isArray"}}, + [{expression_desc = Var ia}], + _ ) ) + when Js_op_util.same_vident ia ib -> + Some false_ + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ), + Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) ) + | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ) ) + when Js_op_util.same_vident ia ib -> + None + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "string"}} ), + Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Str _}) ) + | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Str _}), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "string"}} ) ) + when Js_op_util.same_vident ia ib -> + None + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "number"}} ), + Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Number _}) + ) + | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Number _}), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "number"}} ) ) + when Js_op_util.same_vident ia ib -> + None + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "object"}} ), + Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Null}) ) + | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Null}), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "object"}} ) ) + when Js_op_util.same_vident ia ib -> + None + | ( (Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + { + expression_desc = + Str {txt = "boolean" | "string" | "number" | "object"}; + } ) as typeof), + Bin + ( NotEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ) ) + | ( Bin + ( NotEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _} ), + (Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + { + expression_desc = + Str {txt = "boolean" | "string" | "number" | "object"}; + } ) as typeof) ) + when Js_op_util.same_vident ia ib -> + (* Note: cases boolean / Bool _, number / Number _, string / Str _, object / Null are handled above *) + Some {expression_desc = typeof; comment = None} + | ( (Call + ( {expression_desc = Str {txt = "Array.isArray"}}, + [{expression_desc = Var ia}], + _ ) as is_array), + Bin + ( NotEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ) ) + | ( Bin + ( NotEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ), + (Call + ( {expression_desc = Str {txt = "Array.isArray"}}, + [{expression_desc = Var ia}], + _ ) as is_array) ) + when Js_op_util.same_vident ia ib -> + Some {expression_desc = is_array; comment = None} + | x, y when x = y -> Some e1 + | ( Bin + ( EqEqEq, + {expression_desc = Var ia}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ), + Bin + ( EqEqEq, + {expression_desc = Var ib}, + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + ) ) + when Js_op_util.same_vident ia ib -> + (* Note: case x = y is handled above *) + Some false_ + | _ -> None + in + (if debug then + match res with + | None -> () + | Some e -> + Printf.eprintf "%s = %s\n" + (String.make (n * 2) ' ') + (!string_of_expression e)); + res + +and simplify_and_force ~n (e1 : t) (e2 : t) : t option = + match simplify_and ~n e1 e2 with + | None -> Some {expression_desc = Bin (And, e1, e2); comment = None} + | x -> x (** [simplify_or e1 e2] attempts to simplify the boolean OR expression [e1 || e2]. @@ -955,23 +1002,39 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = This transformation allows reuse of [simplify_and]'s more extensive optimizations in the context of OR expressions. *) -and simplify_or (e1 : t) (e2 : t) : t option = +and simplify_or ~n (e1 : t) (e2 : t) : t option = if debug then - Printf.eprintf "simplify_or %s %s\n" (!string_of_expression e1) - (!string_of_expression e2); - - match (e1.expression_desc, e2.expression_desc) with - | Bool true, _ -> Some true_ - | _, Bool true -> Some true_ - | Bool false, _ -> Some e2 - | _, Bool false -> Some e1 - | _ -> ( - match (push_negation e1, push_negation e2) with - | Some e1_, Some e2_ -> ( - match simplify_and e1_ e2_ with - | Some e -> push_negation e - | None -> None) - | _ -> None) + Printf.eprintf "%ssimplify_or %s %s\n" + (String.make (n * 2) ' ') + (!string_of_expression e1) (!string_of_expression e2); + + let res = + match (e1.expression_desc, e2.expression_desc) with + | Bool true, _ -> Some true_ + | _, Bool true -> Some true_ + | Bool false, _ -> Some e2 + | _, Bool false -> Some e1 + | _ -> ( + match (push_negation e1, push_negation e2) with + | Some e1_, Some e2_ -> ( + match simplify_and ~n:(n + 1) e1_ e2_ with + | Some e -> push_negation e + | None -> None) + | _ -> None) + in + (if debug then + match res with + | None -> () + | Some e -> + Printf.eprintf "%s = %s\n" + (String.make (n * 2) ' ') + (!string_of_expression e)); + res + +and simplify_or_force ~n (e1 : t) (e2 : t) : t option = + match simplify_or ~n e1 e2 with + | None -> Some {expression_desc = Bin (Or, e1, e2); comment = None} + | x -> x let and_ ?comment (e1 : t) (e2 : t) : t = match (e1.expression_desc, e2.expression_desc) with @@ -989,7 +1052,7 @@ let and_ ?comment (e1 : t) (e2 : t) : t = when Js_op_util.same_vident i j -> e2 | _, _ -> ( - match simplify_and e1 e2 with + match simplify_and ~n:0 e1 e2 with | Some e -> e | None -> {expression_desc = Bin (And, e1, e2); comment}) @@ -1003,7 +1066,7 @@ let or_ ?comment (e1 : t) (e2 : t) = when Js_op_util.same_vident i j -> {e2 with expression_desc = Bin (Or, r, l)} | _, _ -> ( - match simplify_or e1 e2 with + match simplify_or ~n:0 e1 e2 with | Some e -> e | None -> {expression_desc = Bin (Or, e1, e2); comment}) diff --git a/tests/tests/src/core/Core_JsonTests.mjs b/tests/tests/src/core/Core_JsonTests.mjs index a830175589..68bf67a627 100644 --- a/tests/tests/src/core/Core_JsonTests.mjs +++ b/tests/tests/src/core/Core_JsonTests.mjs @@ -7,9 +7,9 @@ function decodeJsonTest() { let decodedCorrectly; if (typeof json === "object" && !Array.isArray(json)) { let match = json["someProp"]; - if (match !== undefined && typeof match === "object" && !Array.isArray(match)) { + if (typeof match === "object" && !Array.isArray(match)) { let match$1 = match["thirdProp"]; - if (match$1 !== undefined && Array.isArray(match$1) && match$1.length === 2) { + if (Array.isArray(match$1) && match$1.length === 2) { let match$2 = match$1[0]; if (typeof match$2 === "boolean" && match$2) { let match$3 = match$1[1]; From 80bcd0cf7abbbb95d20e611e38314eb21395d72a Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 7 Nov 2024 15:01:44 +0100 Subject: [PATCH 2/6] typeof bool && x --- compiler/core/js_exp_make.ml | 44 ++++++++++++++++++++++--- tests/tests/src/core/Core_JsonTests.mjs | 4 +-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index 0cf85416d6..a6ab0e01ba 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -716,7 +716,9 @@ let rec push_negation (e : t) : t option = - [(typeof x === "boolean" | "string" | "number") && (x === boolean/null/undefined/123/"hello")] -> [false] - [(Array.isArray(x)) && (x === boolean/null/undefined/123/"hello")] -> [false] - - [(typeof x === "boolean") && (x !== true/false)] -> unchanged + - [(typeof x === "boolean") && (x)] -> [(x == true)] + - [(typeof x === "boolean") && (!x)] -> [(x == false)] + - [(typeof x === "boolean") && (x !== true/false)] -> [(x == false/true)] - [(typeof x === "string") && (x !== "abc")] -> unchanged - [(typeof x === "number") && (x !== 123)] -> unchanged - [(typeof x === "object") && (x !== null)] -> unchanged @@ -861,14 +863,48 @@ let rec simplify_and ~n (e1 : t) (e2 : t) : t option = ( EqEqEq, {expression_desc = Typeof {expression_desc = Var ia}}, {expression_desc = Str {txt = "boolean"}} ), - Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}) ) - | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool _}), + Var ib ) + | ( Var ib, Bin ( EqEqEq, {expression_desc = Typeof {expression_desc = Var ia}}, {expression_desc = Str {txt = "boolean"}} ) ) when Js_op_util.same_vident ia ib -> - None + Some + { + expression_desc = + Bin (EqEqEq, {expression_desc = Var ib; comment = None}, true_); + comment = None; + } + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ), + Js_not {expression_desc = Var ib} ) + | ( Js_not {expression_desc = Var ib}, + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ) ) + when Js_op_util.same_vident ia ib -> + Some + { + expression_desc = + Bin (EqEqEq, {expression_desc = Var ib; comment = None}, false_); + comment = None; + } + | ( Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ), + Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool b}) ) + | ( Bin (NotEqEq, {expression_desc = Var ib}, {expression_desc = Bool b}), + Bin + ( EqEqEq, + {expression_desc = Typeof {expression_desc = Var ia}}, + {expression_desc = Str {txt = "boolean"}} ) ) + when Js_op_util.same_vident ia ib -> + Some {expression_desc = Bool (not b); comment = None} | ( Bin ( EqEqEq, {expression_desc = Typeof {expression_desc = Var ia}}, diff --git a/tests/tests/src/core/Core_JsonTests.mjs b/tests/tests/src/core/Core_JsonTests.mjs index 68bf67a627..1359163aef 100644 --- a/tests/tests/src/core/Core_JsonTests.mjs +++ b/tests/tests/src/core/Core_JsonTests.mjs @@ -11,9 +11,9 @@ function decodeJsonTest() { let match$1 = match["thirdProp"]; if (Array.isArray(match$1) && match$1.length === 2) { let match$2 = match$1[0]; - if (typeof match$2 === "boolean" && match$2) { + if (match$2 === true) { let match$3 = match$1[1]; - decodedCorrectly = typeof match$3 === "boolean" && !match$3; + decodedCorrectly = match$3 === false; } else { decodedCorrectly = false; } From 3cb4af3430b07be3a6966cfb9beeeddad04d43f3 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 7 Nov 2024 15:18:07 +0100 Subject: [PATCH 3/6] cleanup --- compiler/core/js_exp_make.ml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index a6ab0e01ba..7691ee0b8f 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -745,16 +745,16 @@ let rec simplify_and ~n (e1 : t) (e2 : t) : t option = | Bool true, _ -> Some e2 | _, Bool true -> Some e1 | Bin (And, a, b), _ -> ( - match simplify_and ~n:(n + 1) a b with - | Some e -> simplify_and_force ~n:(n + 1) e e2 - | None -> ( - let ao = simplify_and ~n:(n + 1) a e2 in - let bo = simplify_and ~n:(n + 1) b e2 in - match (ao, bo) with - | None, None -> None - | Some a_, None -> simplify_and_force ~n:(n + 1) a_ e2 - | None, Some b_ -> simplify_and_force ~n:(n + 1) e1 b_ - | Some a_, Some b_ -> simplify_and_force ~n:(n + 1) a_ b_)) + let ao = simplify_and ~n:(n + 1) a e2 in + let bo = simplify_and ~n:(n + 1) b e2 in + match (ao, bo) with + | None, None -> ( + match simplify_and ~n:(n + 1) a b with + | None -> None + | Some e -> simplify_and_force ~n:(n + 1) e e2) + | Some a_, None -> simplify_and_force ~n:(n + 1) a_ e2 + | None, Some b_ -> simplify_and_force ~n:(n + 1) e1 b_ + | Some a_, Some b_ -> simplify_and_force ~n:(n + 1) a_ b_) | _, Bin (And, a, b) -> simplify_and ~n:(n + 1) {expression_desc = Bin (And, e1, a); comment = None} From a4054bacdc67986f224054a9ffe896f9953258df Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 7 Nov 2024 15:27:06 +0100 Subject: [PATCH 4/6] Fix and_, strengthen or_ --- compiler/core/js_exp_make.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index 7691ee0b8f..6775d78705 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -752,8 +752,8 @@ let rec simplify_and ~n (e1 : t) (e2 : t) : t option = match simplify_and ~n:(n + 1) a b with | None -> None | Some e -> simplify_and_force ~n:(n + 1) e e2) - | Some a_, None -> simplify_and_force ~n:(n + 1) a_ e2 - | None, Some b_ -> simplify_and_force ~n:(n + 1) e1 b_ + | Some a_, None -> simplify_and_force ~n:(n + 1) a_ b + | None, Some b_ -> simplify_and_force ~n:(n + 1) a b_ | Some a_, Some b_ -> simplify_and_force ~n:(n + 1) a_ b_) | _, Bin (And, a, b) -> simplify_and ~n:(n + 1) @@ -763,7 +763,10 @@ let rec simplify_and ~n (e1 : t) (e2 : t) : t option = let ao = simplify_and ~n:(n + 1) a e2 in let bo = simplify_and ~n:(n + 1) b e2 in match (ao, bo) with - | None, _ | _, None -> None + | None, _ | _, None -> ( + match simplify_or ~n:(n + 1) a b with + | None -> None + | Some e -> simplify_and_force ~n:(n + 1) e e2) | Some a_, Some b_ -> simplify_or_force ~n:(n + 1) a_ b_) | ( Bin ( ((EqEqEq | NotEqEq) as op1), From 490cf13d514df488f24536a3d00557d8bd0fbe7b Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 7 Nov 2024 15:59:46 +0100 Subject: [PATCH 5/6] x === v1 && x !== v2 --- compiler/core/js_exp_make.ml | 18 ++++++++++++++++++ tests/tests/src/core/Core_NullableTests.mjs | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index 6775d78705..cc8d357043 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -728,6 +728,8 @@ let rec push_negation (e : t) : t option = Equality optimizations: - [e && e] -> [e] - [(x === boolean/null/undefined/123/"hello") && (x === boolean/null/undefined/123/"hello")] -> [false] (when not equal) + - [(x === boolean/null/undefined/123/"hello") && (x !== boolean/null/undefined/123/"hello")] -> [false] (when equal) + - [(x === boolean/null/undefined/123/"hello") && (x !== boolean/null/undefined/123/"hello")] -> [(x === ...)] (when not equal) Note: The function preserves the semantics of the original expression while attempting to reduce it to a simpler form. If no simplification is possible, @@ -763,6 +765,10 @@ let rec simplify_and ~n (e1 : t) (e2 : t) : t option = let ao = simplify_and ~n:(n + 1) a e2 in let bo = simplify_and ~n:(n + 1) b e2 in match (ao, bo) with + | Some {expression_desc = Bool false}, None -> + Some {expression_desc = Bin (And, b, e2); comment = None} + | None, Some {expression_desc = Bool false} -> + Some {expression_desc = Bin (And, a, e2); comment = None} | None, _ | _, None -> ( match simplify_or ~n:(n + 1) a b with | None -> None @@ -1005,6 +1011,18 @@ let rec simplify_and ~n (e1 : t) (e2 : t) : t option = when Js_op_util.same_vident ia ib -> (* Note: case x = y is handled above *) Some false_ + | ( Bin + ( ((EqEqEq | NotEqEq) as op1), + {expression_desc = Var ia}, + ({expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + as v1) ), + Bin + ( ((EqEqEq | NotEqEq) as op2), + {expression_desc = Var ib}, + ({expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} + as v2) ) ) + when Js_op_util.same_vident ia ib && op1 != op2 -> + if v1 = v2 then Some false_ else if op1 = EqEqEq then Some e1 else Some e2 | _ -> None in (if debug then diff --git a/tests/tests/src/core/Core_NullableTests.mjs b/tests/tests/src/core/Core_NullableTests.mjs index 55643759b6..7228bd972c 100644 --- a/tests/tests/src/core/Core_NullableTests.mjs +++ b/tests/tests/src/core/Core_NullableTests.mjs @@ -18,7 +18,7 @@ function shouldHandleNullableValues() { "Should handle null" ], tmp, (prim0, prim1) => prim0 === prim1, true); let tmp$1; - tmp$1 = (tUndefined == null) && tUndefined !== null; + tmp$1 = tUndefined === undefined; Test.run([ [ "Core_NullableTests.res", From 9b08fe929eb8aa06a0ab58f1731de24315eddedf Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Fri, 8 Nov 2024 11:14:44 +0100 Subject: [PATCH 6/6] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf761eae4c..ba3fd69f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ - optimize JavaScript code generation by using x == null checks and improving type-based optimizations for string/number literals. https://github.com/rescript-lang/rescript-compiler/pull/7141 - Improve pattern matching on optional fields. https://github.com/rescript-lang/rescript-compiler/pull/7143 https://github.com/rescript-lang/rescript-compiler/pull/7144 - Optimize compilation of switch statements for untagged variants when there are no literal cases. https://github.com/rescript-lang/rescript-compiler/pull/7135 +- Further improve boolean optimizations. https://github.com/rescript-lang/rescript-compiler/pull/7149 #### :house: Internal