From 1cf77a42c670ae964d94a24fbaf1f477cc3fc921 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 31 Oct 2024 17:30:32 +0100 Subject: [PATCH 1/3] small simplification: null or undefined --- compiler/core/js_dump.ml | 10 ++++++++++ tests/tests/src/UntaggedVariants.mjs | 2 +- tests/tests/src/core/Core_NullableTests.mjs | 6 +++--- tests/tests/src/variantsMatching.mjs | 6 +++--- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/compiler/core/js_dump.ml b/compiler/core/js_dump.ml index 4807fd0484..792a4d3c86 100644 --- a/compiler/core/js_dump.ml +++ b/compiler/core/js_dump.ml @@ -700,6 +700,16 @@ and expression_desc cxt ~(level : int) f x : cxt = | Float _ -> "- " | _ -> "-"); expression ~level:13 cxt f e) + | Bin + ( Or, + {expression_desc = Bin (EqEqEq, e1, {expression_desc = Null})}, + {expression_desc = Bin (EqEqEq, e2, {expression_desc = Undefined _})} ) + | Bin + ( Or, + {expression_desc = Bin (EqEqEq, e1, {expression_desc = Undefined _})}, + {expression_desc = Bin (EqEqEq, e2, {expression_desc = Null})} ) + when e1 = e2 -> + expression_desc cxt ~level:(level : int) f (Is_null_or_undefined e1) | Bin (op, e1, e2) -> let out, lft, rght = Js_op_util.op_prec op in let need_paren = diff --git a/tests/tests/src/UntaggedVariants.mjs b/tests/tests/src/UntaggedVariants.mjs index 6043df57ac..2c9a8d5d05 100644 --- a/tests/tests/src/UntaggedVariants.mjs +++ b/tests/tests/src/UntaggedVariants.mjs @@ -80,7 +80,7 @@ let Truthy = { }; function classify$1(x) { - if (x === null || x === undefined) { + if (x == null) { if (x === null) { return "null"; } else { diff --git a/tests/tests/src/core/Core_NullableTests.mjs b/tests/tests/src/core/Core_NullableTests.mjs index c97c71a499..55643759b6 100644 --- a/tests/tests/src/core/Core_NullableTests.mjs +++ b/tests/tests/src/core/Core_NullableTests.mjs @@ -7,7 +7,7 @@ function shouldHandleNullableValues() { let tUndefined = undefined; let tValue = "hello"; let tmp; - tmp = tNull === null || tNull === undefined ? tNull === null : false; + tmp = (tNull == null) ? tNull === null : false; Test.run([ [ "Core_NullableTests.res", @@ -18,7 +18,7 @@ function shouldHandleNullableValues() { "Should handle null" ], tmp, (prim0, prim1) => prim0 === prim1, true); let tmp$1; - tmp$1 = (tUndefined === null || tUndefined === undefined) && tUndefined !== null; + tmp$1 = (tUndefined == null) && tUndefined !== null; Test.run([ [ "Core_NullableTests.res", @@ -29,7 +29,7 @@ function shouldHandleNullableValues() { "Should handle undefined" ], tmp$1, (prim0, prim1) => prim0 === prim1, true); let tmp$2; - tmp$2 = tValue === null || tValue === undefined ? false : tValue === "hello"; + tmp$2 = (tValue == null) ? false : tValue === "hello"; Test.run([ [ "Core_NullableTests.res", diff --git a/tests/tests/src/variantsMatching.mjs b/tests/tests/src/variantsMatching.mjs index 913305dc65..e4b281726f 100644 --- a/tests/tests/src/variantsMatching.mjs +++ b/tests/tests/src/variantsMatching.mjs @@ -216,9 +216,9 @@ function isUndefined$1(x) { } function plus$2(x, y) { - if (x === null || x === undefined) { + if (x == null) { return y; - } else if (y === null || y === undefined) { + } else if (y == null) { return x; } else { return x + y | 0; @@ -226,7 +226,7 @@ function plus$2(x, y) { } function kind(x) { - if (x === null || x === undefined) { + if (x == null) { if (x === null) { return "null"; } else { From c1b266fd7f6949a7ccf2c11daf8acef9ce599079 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 31 Oct 2024 17:45:55 +0100 Subject: [PATCH 2/3] Simplifications for string and number constants. --- compiler/core/js_exp_make.ml | 87 +++++++++++++++++++++++----- tests/tests/src/UntaggedVariants.mjs | 2 +- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/compiler/core/js_exp_make.ml b/compiler/core/js_exp_make.ml index 7b99ae9e21..151ed9b1db 100644 --- a/compiler/core/js_exp_make.ml +++ b/compiler/core/js_exp_make.ml @@ -711,16 +711,20 @@ let rec push_negation (e : t) : t option = Type check optimizations: - [(typeof x === "boolean") && (x === true/false)] -> [x === true/false] - - [(typeof x ==="boolean" | "string" | "number") && (x === boolean/null/undefined)] -> [false] - - [(Array.isArray(x)) && (x === boolean/null/undefined)] -> [false] + - [(typeof x === "string") && (x === "abc")] -> [x === "abc"] + - [(typeof x === "number") && (x === 123)] -> [x === 123] + - [(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" | "string" | "number") && (x !== boolean/null/undefined)] -> [typeof x === ...] - - [(Array.isArray(x)) && (x !== boolean/null/undefined)] -> [Array.isArray(x)] + - [(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 === ...] + - [(Array.isArray(x)) && (x !== boolean/null/undefined/123/"hello")] -> [Array.isArray(x)] Equality optimizations: - [e && e] -> [e] - - [(x === boolean/null/undefined) && (x === boolean/null/undefined)] -> [false] (when not equal) + - [(x === boolean/null/undefined/123/"hello") && (x === boolean/null/undefined/123/"hello")] -> [false] (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, @@ -778,6 +782,33 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = {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}}, @@ -785,17 +816,17 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = Bin ( EqEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ) ) + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) | ( Bin ( EqEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ), + {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: case boolean / Bool _ is handled above *) + (* Note: cases boolean / Bool _, number / Number _, string / Str _ are handled above *) Some false_ | ( Call ( {expression_desc = Str {txt = "Array.isArray"}}, @@ -804,11 +835,11 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = Bin ( EqEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ) ) + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) | ( Bin ( EqEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ), + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), Call ( {expression_desc = Str {txt = "Array.isArray"}}, [{expression_desc = Var ia}], @@ -827,6 +858,30 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = {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}}, @@ -835,7 +890,7 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = Bin ( NotEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ) ) + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) | ( Bin ( NotEqEq, {expression_desc = Var ib}, @@ -846,7 +901,7 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = {expression_desc = Str {txt = "boolean" | "string" | "number"}} ) as typeof) ) when Js_op_util.same_vident ia ib -> - (* Note: case boolean / Bool _ is handled above *) + (* Note: cases boolean / Bool _, number / Number _, string / Str _ are handled above *) Some {expression_desc = typeof; comment = None} | ( (Call ( {expression_desc = Str {txt = "Array.isArray"}}, @@ -855,11 +910,11 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = Bin ( NotEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ) ) + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) | ( Bin ( NotEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ), + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), (Call ( {expression_desc = Str {txt = "Array.isArray"}}, [{expression_desc = Var ia}], @@ -870,11 +925,11 @@ let rec simplify_and (e1 : t) (e2 : t) : t option = | ( Bin ( EqEqEq, {expression_desc = Var ia}, - {expression_desc = Bool _ | Null | Undefined _} ), + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ), Bin ( EqEqEq, {expression_desc = Var ib}, - {expression_desc = Bool _ | Null | Undefined _} ) ) + {expression_desc = Bool _ | Null | Undefined _ | Number _ | Str _} ) ) when Js_op_util.same_vident ia ib -> (* Note: case x = y is handled above *) Some false_ diff --git a/tests/tests/src/UntaggedVariants.mjs b/tests/tests/src/UntaggedVariants.mjs index 2c9a8d5d05..3dfee9a956 100644 --- a/tests/tests/src/UntaggedVariants.mjs +++ b/tests/tests/src/UntaggedVariants.mjs @@ -404,7 +404,7 @@ function check$1(s) { console.log("Nope..."); return; } - if (typeof match$3 === "string" && match$3 === "My name is") { + if (match$3 === "My name is") { let match$4 = match$2[1]; if (match$4 === undefined || match$4 === null || match$4 === false || match$4 === true) { console.log("Nope..."); From 529d960aa09d1f584da4b77d45127019b0a14c43 Mon Sep 17 00:00:00 2001 From: Cristiano Calcagno Date: Thu, 31 Oct 2024 17:50:10 +0100 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ec1477e07..9455ea479e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Improve error message when passing an object where a record is expected. https://github.com/rescript-lang/rescript-compiler/pull/7101 - Improve code generation or pattern matching of untagged variants. https://github.com/rescript-lang/rescript-compiler/pull/7128 - Improve negation handling in combination with and/or to simplify generated code (especially coming out of pattern matching). https://github.com/rescript-lang/rescript-compiler/pull/7138 +- 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 #### :house: Internal