diff --git a/CHANGELOG.md b/CHANGELOG.md index 30da409643..977198bd4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ - 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 - 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 #### :house: Internal diff --git a/compiler/core/lam_compile.ml b/compiler/core/lam_compile.ml index b773eff0ed..22b3ffa2b8 100644 --- a/compiler/core/lam_compile.ml +++ b/compiler/core/lam_compile.ml @@ -723,7 +723,7 @@ let compile output_prefix = (* [e] will be used twice *) let dispatch e = let is_a_literal_case = - if block_cases <> [] then + if untagged then E.is_a_literal_case ~literal_cases:(get_literal_cases sw_names) ~block_cases e @@ -732,21 +732,41 @@ let compile output_prefix = ~has_null_undefined_other:(has_null_undefined_other sw_names) e in - S.if_ is_a_literal_case - (compile_cases ~cxt ~switch_exp:e ~block_cases - ~default:sw_num_default ~get_tag:get_const_tag sw_consts) - ~else_: - (compile_cases ~untagged ~cxt - ~switch_exp:(if untagged then e else E.tag ~name:tag_name e) - ~block_cases ~default:sw_blocks_default - ~get_tag:get_block_tag sw_blocks) + let eq_default d1 d2 = + match (d1, d2) with + | Default lam1, Default lam2 -> Lam.eq_approx lam1 lam2 + | Complete, Complete -> true + | NonComplete, NonComplete -> true + | _ -> false + in + if + untagged + && List.length sw_consts = 0 + && eq_default sw_num_default sw_blocks_default + then + compile_cases ~untagged ~cxt + ~switch_exp:(if untagged then e else E.tag ~name:tag_name e) + ~block_cases ~default:sw_blocks_default ~get_tag:get_block_tag + sw_blocks + else + [ + S.if_ is_a_literal_case + (compile_cases ~cxt ~switch_exp:e ~block_cases + ~default:sw_num_default ~get_tag:get_const_tag sw_consts) + ~else_: + (compile_cases ~untagged ~cxt + ~switch_exp: + (if untagged then e else E.tag ~name:tag_name e) + ~block_cases ~default:sw_blocks_default + ~get_tag:get_block_tag sw_blocks); + ] in match e.expression_desc with - | J.Var _ -> [dispatch e] + | J.Var _ -> dispatch e | _ -> let v = Ext_ident.create_tmp () in (* Necessary avoid duplicated computation*) - [S.define_variable ~kind:Variable v e; dispatch (E.var v)]) + S.define_variable ~kind:Variable v e :: dispatch (E.var v)) in match lambda_cxt.continuation with (* Needs declare first *) diff --git a/tests/tests/src/UntaggedVariants.mjs b/tests/tests/src/UntaggedVariants.mjs index 3dfee9a956..95d20f5767 100644 --- a/tests/tests/src/UntaggedVariants.mjs +++ b/tests/tests/src/UntaggedVariants.mjs @@ -376,10 +376,6 @@ let TestFunctionCase = { let someJson = '[{"name": "Haan"}, {"name": "Mr"}, false]'; function check$1(s) { - if (s === undefined || s === null || s === false || s === true) { - console.log("Nope..."); - return; - } if (Array.isArray(s)) { if (s.length !== 3) { console.log("Nope..."); @@ -388,46 +384,40 @@ function check$1(s) { let match = s[0]; if (match === true) { let match$1 = s[1]; - if (match$1 === false) { - let match$2 = s[2]; - if (match$2 === undefined || match$2 === null || match$2 === false || match$2 === true) { - console.log("Nope..."); - return; - } - if (Array.isArray(match$2)) { - if (match$2.length !== 2) { - console.log("Nope..."); - return; - } - let match$3 = match$2[0]; - if (match$3 === undefined || match$3 === null || match$3 === false || match$3 === true) { - console.log("Nope..."); - return; - } - 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) { + if (match$1 === undefined || match$1 === null || match$1 === false || match$1 === true) { + if (match$1 === false) { + let match$2 = s[2]; + if (Array.isArray(match$2)) { + if (match$2.length !== 2) { console.log("Nope..."); return; } - if (typeof match$4 === "number") { - if (match$4 !== 10) { + let match$3 = match$2[0]; + if (typeof match$3 === "string") { + if (match$3 === "My name is") { + let match$4 = match$2[1]; + if (typeof match$4 === "number") { + if (match$4 !== 10) { + console.log("Nope..."); + } else { + console.log("yup"); + } + return; + } console.log("Nope..."); - } else { - console.log("yup"); + return; } + console.log("Nope..."); return; } console.log("Nope..."); return; - } else { - console.log("Nope..."); - return; } - } else { console.log("Nope..."); return; } + console.log("Nope..."); + return; } else { console.log("Nope..."); return; diff --git a/tests/tests/src/and_or_simplify.mjs b/tests/tests/src/and_or_simplify.mjs index 968359e1b8..dd7011f8b0 100644 --- a/tests/tests/src/and_or_simplify.mjs +++ b/tests/tests/src/and_or_simplify.mjs @@ -6,10 +6,10 @@ function check_null_eq_typeof(x) { } function check_null_neq_typeof(x) { - if (typeof x !== "boolean") { - return 4; - } else { + if (typeof x === "boolean") { return 3; + } else { + return 4; } } @@ -18,10 +18,10 @@ function check_undefined_eq_typeof(x) { } function check_undefined_neq_typeof(x) { - if (typeof x !== "boolean") { - return 4; - } else { + if (typeof x === "boolean") { return 3; + } else { + return 4; } } diff --git a/tests/tests/src/core/Core_JsonTests.mjs b/tests/tests/src/core/Core_JsonTests.mjs index 1f4ff3bfc5..a830175589 100644 --- a/tests/tests/src/core/Core_JsonTests.mjs +++ b/tests/tests/src/core/Core_JsonTests.mjs @@ -5,19 +5,17 @@ import * as Test from "./Test.mjs"; function decodeJsonTest() { let json = {"someProp":{"otherProp": null, "thirdProp": [true, false]}}; let decodedCorrectly; - if (json === null || !(typeof json === "object" && !Array.isArray(json))) { - decodedCorrectly = false; - } else { + if (typeof json === "object" && !Array.isArray(json)) { let match = json["someProp"]; - if (match !== undefined && !(match === null || !(typeof match === "object" && !Array.isArray(match)))) { + if (match !== undefined && typeof match === "object" && !Array.isArray(match)) { let match$1 = match["thirdProp"]; - if (match$1 !== undefined && !(match$1 === null || !(Array.isArray(match$1) && match$1.length === 2))) { + if (match$1 !== undefined && Array.isArray(match$1) && match$1.length === 2) { let match$2 = match$1[0]; - if (match$2 === null || !(typeof match$2 === "boolean" && match$2)) { - decodedCorrectly = false; - } else { + if (typeof match$2 === "boolean" && match$2) { let match$3 = match$1[1]; - decodedCorrectly = match$3 === null ? false : typeof match$3 === "boolean" && !match$3; + decodedCorrectly = typeof match$3 === "boolean" && !match$3; + } else { + decodedCorrectly = false; } } else { decodedCorrectly = false; @@ -25,6 +23,8 @@ function decodeJsonTest() { } else { decodedCorrectly = false; } + } else { + decodedCorrectly = false; } Test.run([ [ diff --git a/tests/tests/src/json_decorders.mjs b/tests/tests/src/json_decorders.mjs new file mode 100644 index 0000000000..cac0277cc7 --- /dev/null +++ b/tests/tests/src/json_decorders.mjs @@ -0,0 +1,59 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as $$Array from "rescript/lib/es6/Array.js"; + +function decodeUser(json) { + if (!(typeof json === "object" && !Array.isArray(json))) { + return; + } + let id = json.id; + if (typeof id !== "string") { + return; + } + let name = json.name; + if (typeof name !== "string") { + return; + } + let age = json.age; + if (typeof age !== "number") { + return; + } + let email = json.email; + let tmp; + tmp = typeof email === "string" ? email : undefined; + return { + id: id, + name: name, + age: age | 0, + email: tmp + }; +} + +function decodeGroup(json) { + if (!(typeof json === "object" && !Array.isArray(json))) { + return; + } + let id = json.id; + if (typeof id !== "string") { + return; + } + let name = json.name; + if (typeof name !== "string") { + return; + } + let users = json.users; + if (Array.isArray(users)) { + return { + id: id, + name: name, + users: $$Array.filterMap(users, decodeUser) + }; + } + +} + +export { + decodeUser, + decodeGroup, +} +/* No side effect */ diff --git a/tests/tests/src/json_decorders.res b/tests/tests/src/json_decorders.res new file mode 100644 index 0000000000..3f792eaaf0 --- /dev/null +++ b/tests/tests/src/json_decorders.res @@ -0,0 +1,45 @@ +type user = { + id: string, + name: string, + age: int, + email: option, +} + +type group = { + id: string, + name: string, + users: array, +} + +let decodeUser = json => { + switch json { + | JSON.Object(dict{ + "id": JSON.String(id), + "name": String(name), + "age": Number(age), + "email": ?email, + }) => + Some({ + id, + name, + age: age->Float.toInt, + email: switch email { + | Some(String(email)) => Some(email) + | _ => None + }, + }) + | _ => None + } +} + +let decodeGroup = json => { + switch json { + | JSON.Object(dict{"id": JSON.String(id), "name": String(name), "users": Array(users)}) => + Some({ + id, + name, + users: users->Array.filterMap(decodeUser), + }) + | _ => None + } +} diff --git a/tests/tests/src/pattern_match_json.mjs b/tests/tests/src/pattern_match_json.mjs index 378f82afeb..37b7157300 100644 --- a/tests/tests/src/pattern_match_json.mjs +++ b/tests/tests/src/pattern_match_json.mjs @@ -4,12 +4,6 @@ import * as Primitive_option from "rescript/lib/es6/Primitive_option.js"; function decodeGroup(group) { let id = group.id; - if (id == null) { - return [ - "e", - "f" - ]; - } if (typeof id !== "string") { return [ "e", @@ -17,15 +11,15 @@ function decodeGroup(group) { ]; } let name = group.name; - if (typeof name !== "string") { + if (typeof name === "string") { return [ - "e", - "f" + id, + name ]; } else { return [ - id, - name + "e", + "f" ]; } }