diff --git a/CHANGELOG.md b/CHANGELOG.md index 145b708706..ea2ed8bef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,6 @@ - Fix excessive parentheses and indentation in function assignments to refs, align record and array assignment formatting across refs and fields, and preserve function return-type parentheses and consistent JSX fragment layout in callbacks. https://github.com/rescript-lang/rescript/pull/8611 - Fix a recursive module with an empty signature discarding its right-hand side. Lambda-to-Lam conversion rewrote `Pupdate_mod` to unit when the module's shape had no fields, dropping the primitive's arguments - one of which is the right-hand side - so `module rec M: {} = { let () = Console.log("effect") }` emitted nothing for `M`. The elision now happens where the bindings are produced, with the right-hand side still in hand. https://github.com/rescript-lang/rescript/pull/8608 -- Fix `Int.Ref.increment` and `Int.Ref.decrement` evaluating their argument twice: `Int.Ref.increment(mkRef())` emitted `mkRef().contents = mkRef().contents + 1 | 0`. The `%incr` and `%decr` builtins lowered to an assignment that repeated the argument expression; they now bind the reference before the read-modify-write. Inlining decisions around an increment are taken on the code it stands for rather than on a single primitive node. https://github.com/rescript-lang/rescript/pull/8608 - Fix a compiler crash on a polymorphic variant whose numeric name exceeds the `int32` range. `#99999999999("a")` and the same name in a pattern failed with `Failure("Int32.of_string")` and no location, because the range check ran in the frontend AST pass and matched only payload-free expressions. It now runs in `Typecore`, next to the integer literal decoding whose overflow error it mirrors, and covers both label positions. A bare `type t = [#99999999999]` still compiles, since nothing decodes a row field name. https://github.com/rescript-lang/rescript/pull/8608 - Object typing errors now describe fields directly: assigning to a field without `@set` reports that the field is not settable and suggests the annotation, and missing-property errors name the field instead of a phantom `"x#="` member. https://github.com/rescript-lang/rescript/pull/8597 - Fix pattern matching for string literals with equivalent runtime values but different escape spellings, preserving source order and reporting redundant patterns. https://github.com/rescript-lang/rescript/pull/8606 @@ -70,6 +69,7 @@ #### :house: Internal - Normalize Lambda terms where they are built: a match guard stays structured data until its fallthrough is known, and `apply` and `mk_builtin` go through the folding constructors. https://github.com/rescript-lang/rescript/pull/8615 +- Replace non-escaping local mutable blocks with scalar bindings when all uses are direct field accesses, generalizing reference unboxing to multi-field records and references captured by JavaScript closures. https://github.com/rescript-lang/rescript/pull/8617 - Merge the duplicate Lam intermediate representation into Lambda, removing the conversion layer and obsolete supporting infrastructure. Lambda is now a single private, normalized representation, with generated JavaScript remaining semantically unchanged. https://github.com/rescript-lang/rescript/pull/8608 - Add genType and source map controls and output to the developer playground. https://github.com/rescript-lang/rescript/pull/8448 - Rework the object-type representation end to end: object rows are plain field chains carrying a per-field mutability state (no phantom setter members), object literals are typed directly and property access and assignment are first-class AST and Lambda nodes shared between the Lambda and JS pipelines, and dead class-system remnants (the field-presence lattice, the class-abbreviation memo on object types, method-send typing) are removed. https://github.com/rescript-lang/rescript/pull/8597 diff --git a/compiler/core/lam_compile_main.ml b/compiler/core/lam_compile_main.ml index 21b83d1140..db184955d3 100644 --- a/compiler/core/lam_compile_main.ml +++ b/compiler/core/lam_compile_main.ml @@ -324,6 +324,7 @@ let compile (output_prefix : string) export_idents hoisted (lam : Lambda.t) = |> d "before-simplify_lets" (* we should investigate a better way to put different passes : )*) |> Lam_pass_lets_dce.simplify_lets + |> d "simplify_lets" |> Lam_pass_sroa.simplify |> d "sroa" |> d "before-simplify-exits" (* |> (fun lam -> Lam_pass_collect.collect_info meta lam ; Lam_pass_remove_alias.simplify_alias meta lam) *) diff --git a/compiler/core/lam_pass_eliminate_ref.ml b/compiler/core/lam_pass_eliminate_ref.ml deleted file mode 100644 index 3830c09072..0000000000 --- a/compiler/core/lam_pass_eliminate_ref.ml +++ /dev/null @@ -1,104 +0,0 @@ -(***********************************************************************) -(* *) -(* OCaml *) -(* *) -(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) -(* *) -(* Copyright 1996 Institut National de Recherche en Informatique et *) -(* en Automatique. All rights reserved. This file is distributed *) -(* under the terms of the Q Public License version 1.0. *) -(* *) -(***********************************************************************) -(* Adapted for Javascript backend : Hongbo Zhang, *) - -exception Real_reference - -let rec eliminate_ref id (lam : Lambda.t) = - match lam with - (* we can do better escape analysis in Javascript backend *) - | Lvar v -> if Ident.same v id then raise_notrace Real_reference else lam - | Lprim {primitive = Pfield (0, _); args = [Lvar v]} when Ident.same v id -> - Lambda.var id - | Lfunction _ -> - if Lam_hit.hit_variable id lam then raise_notrace Real_reference else lam - (* In Javascript backend, its okay, we can reify it later - a failed case - {[ - for i = .. - let v = ref 0 - for j = .. - incr v - a[j] = ()=>{!v} - - ]} - here v is captured by a block, and it's a loop mutable value, - we have to generate - {[ - for i = .. - let v = ref 0 - (function (v){for j = .. - a[j] = ()=>{!v}}(v) - - ]} - now, v is a real reference - TODO: we can refine analysis in later - *) - (* Lfunction(kind, params, eliminate_ref id body) *) - | Lprim {primitive = Psetfield (0, _); args = [Lvar v; e]} - when Ident.same v id -> - Lambda.assign id (eliminate_ref id e) - | Lconst _ -> lam - | Lapply {ap_func = e1; ap_args = el; ap_info; ap_transformed_jsx} -> - Lambda.apply ~ap_transformed_jsx (eliminate_ref id e1) - (Ext_list.map el (eliminate_ref id)) - ap_info - | Llet (str, v, e1, e2) -> - Lambda.let_ str v (eliminate_ref id e1) (eliminate_ref id e2) - | Lletrec (idel, e2) -> - Lambda.letrec - (Ext_list.map idel (fun (v, e) -> (v, eliminate_ref id e))) - (eliminate_ref id e2) - | Lglobal_module _ -> lam - | Lprim {primitive; args; loc} -> - Lambda.prim ~primitive ~args:(Ext_list.map args (eliminate_ref id)) loc - | Lswitch (e, sw) -> - Lambda.switch (eliminate_ref id e) - { - sw_consts_full = sw.sw_consts_full; - sw_consts = - Ext_list.map sw.sw_consts (fun (n, e) -> (n, eliminate_ref id e)); - sw_blocks_full = sw.sw_blocks_full; - sw_blocks = - Ext_list.map sw.sw_blocks (fun (n, e) -> (n, eliminate_ref id e)); - sw_failaction = - (match sw.sw_failaction with - | None -> None - | Some x -> Some (eliminate_ref id x)); - sw_dispatch = sw.sw_dispatch; - } - | Lstringswitch (e, sw, default) -> - Lambda.stringswitch (eliminate_ref id e) - (Ext_list.map sw (fun (s, e) -> (s, eliminate_ref id e))) - (match default with - | None -> None - | Some x -> Some (eliminate_ref id x)) - | Lstaticraise (i, args) -> - Lambda.staticraise i (Ext_list.map args (eliminate_ref id)) - | Lstaticcatch (e1, i, e2) -> - Lambda.staticcatch (eliminate_ref id e1) i (eliminate_ref id e2) - | Ltrywith (e1, v, e2) -> - Lambda.try_ (eliminate_ref id e1) v (eliminate_ref id e2) - | Lifthenelse (e1, e2, e3) -> - Lambda.if_ (eliminate_ref id e1) (eliminate_ref id e2) (eliminate_ref id e3) - | Lsequence (e1, e2) -> Lambda.seq (eliminate_ref id e1) (eliminate_ref id e2) - | Lbreak -> Lambda.break - | Lcontinue -> Lambda.continue - | Lwhile (e1, e2) -> Lambda.while_ (eliminate_ref id e1) (eliminate_ref id e2) - | Lfor (v, e1, e2, dir, e3) -> - Lambda.for_ v (eliminate_ref id e1) (eliminate_ref id e2) dir - (eliminate_ref id e3) - | Lfor_of (v, e1, e2) -> - Lambda.for_of v (eliminate_ref id e1) (eliminate_ref id e2) - | Lfor_await_of (v, e1, e2) -> - Lambda.for_await_of v (eliminate_ref id e1) (eliminate_ref id e2) - | Lassign (v, e) -> Lambda.assign v (eliminate_ref id e) diff --git a/compiler/core/lam_pass_lets_dce.ml b/compiler/core/lam_pass_lets_dce.ml index 9ba1b75283..e7a6004278 100644 --- a/compiler/core/lam_pass_lets_dce.ml +++ b/compiler/core/lam_pass_lets_dce.ml @@ -22,22 +22,6 @@ let lets_helper (count_var : Ident.t -> Lam_pass_count.used_info) lam : Lambda.t | Llet ((Strict | Alias | StrictOpt), v, Lvar w, l2) -> Hash_ident.add subst v (simplif (Lambda.var w)); simplif l2 - | Llet - ( (Strict as kind), - v, - Lprim {primitive = Pmakeblock info as primitive; args = [linit]; loc}, - lbody ) - when not (Lambda.is_immutable_block info) -> ( - let slinit = simplif linit in - let slbody = simplif lbody in - try - (* TODO: record all references variables *) - Lam_util.refine_let ~kind:Variable v slinit - (Lam_pass_eliminate_ref.eliminate_ref v slbody) - with Lam_pass_eliminate_ref.Real_reference -> - Lam_util.refine_let ~kind v - (Lambda.prim ~primitive ~args:[slinit] loc) - slbody) | Llet (Alias, v, l1, l2) -> ( (* For alias, [l1] is pure, we can always inline, when captured, we should avoid recomputation @@ -89,27 +73,13 @@ let lets_helper (count_var : Ident.t -> Lam_pass_count.used_info) lam : Lambda.t not (used v) then simplif lbody (* GPR #1476 *) else + let l1 = simplif l1 in match l1 with - | Lprim {primitive = Pmakeblock info as primitive; args = [linit]; loc} - when not (Lambda.is_immutable_block info) -> ( - let slinit = simplif linit in - let slbody = simplif lbody in - try - (* TODO: record all references variables *) - Lam_util.refine_let ~kind:Variable v slinit - (Lam_pass_eliminate_ref.eliminate_ref v slbody) - with Lam_pass_eliminate_ref.Real_reference -> - Lam_util.refine_let ~kind v - (Lambda.prim ~primitive ~args:[slinit] loc) - slbody) - | _ -> ( - let l1 = simplif l1 in - match l1 with - | Lconst (Const_string s) -> - Hash_ident.add string_table v s; - (* we need move [simplif lbody] later, since adding Hash does have side effect *) - Lambda.let_ Alias v l1 (simplif lbody) - | _ -> Lam_util.refine_let ~kind v l1 (simplif lbody)) + | Lconst (Const_string s) -> + Hash_ident.add string_table v s; + (* we need move [simplif lbody] later, since adding Hash does have side effect *) + Lambda.let_ Alias v l1 (simplif lbody) + | _ -> Lam_util.refine_let ~kind v l1 (simplif lbody) (* TODO: check if it is correct rollback to [StrictOpt]? *)) | Llet (((Strict | Variable) as kind), v, l1, l2) -> ( if not (used v) then diff --git a/compiler/core/lam_pass_sroa.ml b/compiler/core/lam_pass_sroa.ml new file mode 100644 index 0000000000..eea1c41a71 --- /dev/null +++ b/compiler/core/lam_pass_sroa.ml @@ -0,0 +1,144 @@ +(***********************************************************************) +(* *) +(* OCaml *) +(* *) +(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) +(* *) +(* Copyright 1996 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) +(* Adapted for Javascript backend : Hongbo Zhang, *) + +(* Scalar replacement of aggregates (SROA) for local mutable blocks. + + A block can be replaced by mutable scalar bindings when every occurrence + of the block is a direct, statically indexed field read or write. JavaScript + closures capture bindings, so direct accesses from nested functions remain + eligible. Analysis is kept separate from rewriting so a failed eligibility + check cannot partially transform the term. *) + +type field_use = {mutable read: bool; mutable written: bool} + +let valid_field uses index = index >= 0 && index < Array.length uses + +(* Does the block appear only as direct, in-range field reads and writes? While + answering, record how every field is used. [analyze] and [rewrite] below are + a matched pair: [rewrite] handles exactly the occurrences [analyze] accepts, + and asserts on the rest. Extending one without the other is a compiler crash + rather than a type error, so keep their cases in step. *) +let rec analyze block uses (lam : Lambda.t) = + match lam with + | Lvar id -> not (Ident.same id block) + | Lassign (id, value) -> + (not (Ident.same id block)) && analyze block uses value + | Lprim {primitive = Pfield (index, _); args = [Lvar id]} + when Ident.same id block -> + if valid_field uses index then ( + uses.(index).read <- true; + true) + else false + | Lprim {primitive = Psetfield (index, _); args = [Lvar id; value]} + when Ident.same id block -> + if valid_field uses index then ( + uses.(index).written <- true; + analyze block uses value) + else false + | _ -> + not + (Lambda.shallow_exists (fun child -> not (analyze block uses child)) lam) + +let discard_value value body = + if Lam_analysis.no_side_effects value then body else Lambda.seq value body + +let rec rewrite block fields uses (lam : Lambda.t) = + match lam with + | Lprim {primitive = Pfield (index, _); args = [Lvar id]} + when Ident.same id block -> + Lambda.var fields.(index) + | Lprim {primitive = Psetfield (index, _); args = [Lvar id; value]} + when Ident.same id block -> + let value = rewrite block fields uses value in + if not uses.(index).read then discard_value value Lambda.lambda_unit + else Lambda.assign fields.(index) value + (* Unreachable: [analyze] rejected the block for both of these, so [replace] + never reaches the rewrite. They are kept as assertions rather than dropped + so that a future occurrence form added to [analyze] but not here fails + loudly instead of silently losing the write. *) + | Lvar id when Ident.same id block -> assert false + | Lassign (id, _) when Ident.same id block -> assert false + | _ -> Lambda.shallow_map_sharing (rewrite block fields uses) lam + +let fields_for_block block info field_count = + let fallback () = + Array.init field_count (fun index -> + if index = 0 then block else Ident.rename block) + in + if field_count = 1 then [|block|] + else + let names = + match info with + | Lambda.Blk_record {fields} | Lambda.Blk_record_inlined {fields} -> + if Array.length fields = field_count then + Some (Array.map (fun (name, _) -> name) fields) + else None + | Lambda.Blk_record_ext {fields} -> + if Array.length fields = field_count then Some fields else None + | Lambda.Blk_tuple | Lambda.Blk_constructor _ | Lambda.Blk_poly_var + | Lambda.Blk_module _ | Lambda.Blk_module_export _ | Lambda.Blk_extension + -> + None + in + match names with + | None -> fallback () + | Some names -> + Array.map (fun name -> Ident.create (Ident.name block ^ "_" ^ name)) names + +let replace ~block ~info ~initializers body = + match initializers with + | [] -> None + | _ -> + let field_count = List.length initializers in + let uses = + Array.init field_count (fun _ -> {read = false; written = false}) + in + if not (analyze block uses body) then None + else + let fields = fields_for_block block info field_count in + let body = rewrite block fields uses body in + let rec bind_fields index initializers body = + match initializers with + | [] -> body + | init :: rest -> + let body = bind_fields (index + 1) rest body in + let use = uses.(index) in + (* A never-read field needs no storage; its initializer and writes are + retained only when they have effects. A read-only field can use a + normal refined let, while a field that is both read and written + still needs a mutable scalar binding. *) + if not use.read then discard_value init body + else if not use.written then + Lam_util.refine_let ~kind:Strict fields.(index) init body + else Lambda.let_ Variable fields.(index) init body + in + Some (bind_fields 0 initializers body) + +let rec simplify (lam : Lambda.t) = + match lam with + | Llet (kind, block, init, body) -> ( + let init' = simplify init in + let body' = simplify body in + match (kind, init') with + | ( (Strict | StrictOpt), + Lambda.Lprim {primitive = Pmakeblock info; args = initializers} ) + when not (Lambda.is_immutable_block info) -> ( + match replace ~block ~info ~initializers body' with + | Some replacement -> replacement + | None -> + if init' == init && body' == body then lam + else Lambda.let_ kind block init' body') + | _ -> + if init' == init && body' == body then lam + else Lambda.let_ kind block init' body') + | _ -> Lambda.shallow_map_sharing simplify lam diff --git a/compiler/core/lam_pass_eliminate_ref.mli b/compiler/core/lam_pass_sroa.mli similarity index 64% rename from compiler/core/lam_pass_eliminate_ref.mli rename to compiler/core/lam_pass_sroa.mli index 7051b64fd6..60ee364331 100644 --- a/compiler/core/lam_pass_eliminate_ref.mli +++ b/compiler/core/lam_pass_sroa.mli @@ -1,5 +1,5 @@ (* Copyright (C) 2015-2016 Bloomberg Finance L.P. - * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript + * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -17,11 +17,23 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) -exception Real_reference +val replace : + block:Ident.t -> + info:Lambda.tag_info -> + initializers:Lambda.t list -> + Lambda.t -> + Lambda.t option +(** [replace ~block ~info ~initializers body] replaces a non-escaping local + block with independent scalar values. Fields that are never read need no + storage, but effects from their initializers and writes are preserved in + order. Read-only fields use immutable bindings; fields that are both read + and written use mutable bindings. Returns [None] when the block is used + other than by direct field access. *) -val eliminate_ref : Ident.t -> Lambda.t -> Lambda.t +val simplify : Lambda.t -> Lambda.t +(** Scalar-replace eligible local mutable blocks throughout a Lambda term. *) diff --git a/packages/@rescript/runtime/lib/es6/Primitive_object.mjs b/packages/@rescript/runtime/lib/es6/Primitive_object.mjs index ce2303c85e..ec42204bf0 100644 --- a/packages/@rescript/runtime/lib/es6/Primitive_object.mjs +++ b/packages/@rescript/runtime/lib/es6/Primitive_object.mjs @@ -258,26 +258,24 @@ function equal(a, b) { } else if ((a instanceof Date && b instanceof Date)) { return !(a > b || a < b); } else { - let result = { - contents: true - }; + let result = true; let do_key_a = key => { - if (!Object.prototype.hasOwnProperty.call(b, key)) { - result.contents = false; + if (Object.prototype.hasOwnProperty.call(b, key)) { return; } + result = false; }; let do_key_b = key => { - if (!Object.prototype.hasOwnProperty.call(a, key) || !equal(b[key], a[key])) { - result.contents = false; + if (Object.prototype.hasOwnProperty.call(a, key) && equal(b[key], a[key])) { return; } + result = false; }; for_in(a, do_key_a); - if (result.contents) { + if (result) { for_in(b, do_key_b); } - return result.contents; + return result; } } else { return false; diff --git a/packages/@rescript/runtime/lib/js/Primitive_object.cjs b/packages/@rescript/runtime/lib/js/Primitive_object.cjs index 00fbdc8b65..c74346f0d9 100644 --- a/packages/@rescript/runtime/lib/js/Primitive_object.cjs +++ b/packages/@rescript/runtime/lib/js/Primitive_object.cjs @@ -258,26 +258,24 @@ function equal(a, b) { } else if ((a instanceof Date && b instanceof Date)) { return !(a > b || a < b); } else { - let result = { - contents: true - }; + let result = true; let do_key_a = key => { - if (!Object.prototype.hasOwnProperty.call(b, key)) { - result.contents = false; + if (Object.prototype.hasOwnProperty.call(b, key)) { return; } + result = false; }; let do_key_b = key => { - if (!Object.prototype.hasOwnProperty.call(a, key) || !equal(b[key], a[key])) { - result.contents = false; + if (Object.prototype.hasOwnProperty.call(a, key) && equal(b[key], a[key])) { return; } + result = false; }; for_in(a, do_key_a); - if (result.contents) { + if (result) { for_in(b, do_key_b); } - return result.contents; + return result; } } else { return false; diff --git a/rewatch/testrepo/packages/dep02/src/Array.mjs b/rewatch/testrepo/packages/dep02/src/Array.mjs index 1d3a8c7bd3..d51ea7518e 100644 --- a/rewatch/testrepo/packages/dep02/src/Array.mjs +++ b/rewatch/testrepo/packages/dep02/src/Array.mjs @@ -108,9 +108,7 @@ function eqBy(_xs, _ys, fn) { } function takeWhile(t, fn) { - let a = { - contents: [] - }; + let a = []; let maxLength = t.length - 1 | 0; let iter = _idx => { while (true) { @@ -122,13 +120,13 @@ function takeWhile(t, fn) { if (!fn(item)) { return; } - a.contents = Belt_Array.concat(a.contents, [item]); + a = Belt_Array.concat(a, [item]); _idx = idx + 1 | 0; continue; }; }; iter(0); - return a.contents; + return a; } function distinct(t, eq) { diff --git a/tests/belt_tests/src/belt_hashset_int_test.mjs b/tests/belt_tests/src/belt_hashset_int_test.mjs index 159f97cb71..63f9b1662a 100644 --- a/tests/belt_tests/src/belt_hashset_int_test.mjs +++ b/tests/belt_tests/src/belt_hashset_int_test.mjs @@ -13,13 +13,11 @@ function add(x, y) { } function sum2(h) { - let v = { - contents: 0 - }; + let v = 0; Belt_HashSetInt.forEach(h, x => { - v.contents = v.contents + x | 0; + v = v + x | 0; }); - return v.contents; + return v; } Mocha.describe("Belt_hashset_int_test", () => { diff --git a/tests/belt_tests/src/belt_list_test.mjs b/tests/belt_tests/src/belt_list_test.mjs index 395ea5da21..e2ac66043a 100644 --- a/tests/belt_tests/src/belt_list_test.mjs +++ b/tests/belt_tests/src/belt_list_test.mjs @@ -8,23 +8,19 @@ import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.mjs"; import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.mjs"; function sum(xs) { - let v = { - contents: 0 - }; + let v = 0; Belt_List.forEach(xs, x => { - v.contents = v.contents + x | 0; + v = v + x | 0; }); - return v.contents; + return v; } function sum2(xs, ys) { - let v = { - contents: 0 - }; + let v = 0; Belt_List.forEach2(xs, ys, (x, y) => { - v.contents = (v.contents + x | 0) + y | 0; + v = (v + x | 0) + y | 0; }); - return v.contents; + return v; } Mocha.describe("Belt_list_test", () => { diff --git a/tests/belt_tests/src/bs_array_test.mjs b/tests/belt_tests/src/bs_array_test.mjs index 47393b577f..568f4f3faa 100644 --- a/tests/belt_tests/src/bs_array_test.mjs +++ b/tests/belt_tests/src/bs_array_test.mjs @@ -831,13 +831,11 @@ Mocha.describe("Bs_array_test", () => { ]); }); let sumUsingForEach = xs => { - let v = { - contents: 0 - }; + let v = 0; Belt_Array.forEach(xs, x => { - v.contents = v.contents + x | 0; + v = v + x | 0; }); - return v.contents; + return v; }; Mocha.test("bs_array_test_iteration_functions", () => { Test_utils.eq("File \"bs_array_test.res\", line 280, characters 7-14", sumUsingForEach([ @@ -869,16 +867,14 @@ Mocha.describe("Bs_array_test", () => { 0, 1 ], [1], (prim0, prim1) => prim0 === prim1)); - let c = { - contents: 0 - }; + let c = 0; Test_utils.ok("File \"bs_array_test.res\", line 286, characters 6-13", (Belt_Array.forEachWithIndex([ 1, 1, 1 ], (i, v) => { - c.contents = (c.contents + i | 0) + v | 0; - }), c.contents === 6)); + c = (c + i | 0) + v | 0; + }), c === 6)); }); let id = (loc, x) => { let u = x.slice(0); diff --git a/tests/belt_tests/src/bs_poly_set_test.mjs b/tests/belt_tests/src/bs_poly_set_test.mjs index ae85ac5ca0..06d07ee6c7 100644 --- a/tests/belt_tests/src/bs_poly_set_test.mjs +++ b/tests/belt_tests/src/bs_poly_set_test.mjs @@ -15,29 +15,25 @@ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.mj let IntCmp = Belt_Id.comparable(Primitive_int.compare); function testIterToList(xs) { - let v = { - contents: /* [] */0 - }; + let v = /* [] */0; Belt_Set.forEach(xs, x => { - v.contents = { + v = { hd: x, - tl: v.contents + tl: v }; }); - return Belt_List.reverse(v.contents); + return Belt_List.reverse(v); } function testIterToList2(xs) { - let v = { - contents: /* [] */0 - }; + let v = /* [] */0; Belt_SetDict.forEach(Belt_Set.getData(xs), x => { - v.contents = { + v = { hd: x, - tl: v.contents + tl: v }; }); - return Belt_List.reverse(v.contents); + return Belt_List.reverse(v); } Mocha.describe("Bs_poly_set_test", () => { diff --git a/tests/belt_tests/src/bs_queue_test.mjs b/tests/belt_tests/src/bs_queue_test.mjs index ba9db35354..ded1b8df07 100644 --- a/tests/belt_tests/src/bs_queue_test.mjs +++ b/tests/belt_tests/src/bs_queue_test.mjs @@ -626,11 +626,9 @@ Mocha.describe("Bs_queue_test", () => { for (let i = 1; i <= 10; ++i) { Belt_MutableQueue.add(q, i); } - let i$1 = { - contents: 1 - }; + let i$1 = 1; Belt_MutableQueue.forEach(q, j => { - if (i$1.contents !== j) { + if (i$1 !== j) { throw { RE_EXN_ID: "Assert_failure", _1: [ @@ -641,7 +639,7 @@ Mocha.describe("Bs_queue_test", () => { Error: new Error() }; } - i$1.contents = i$1.contents + 1 | 0; + i$1 = i$1 + 1 | 0; }); }); Mocha.test("queue transfer operations - empty to empty", () => { diff --git a/tests/belt_tests/src/test_string_map.mjs b/tests/belt_tests/src/test_string_map.mjs index e21e0922a4..ba35f9d455 100644 --- a/tests/belt_tests/src/test_string_map.mjs +++ b/tests/belt_tests/src/test_string_map.mjs @@ -9,17 +9,15 @@ function timing(label, f) { } function assertion_test() { - let m = { - contents: undefined - }; + let m; timing("building", () => { for (let i = 0; i <= 1000000; ++i) { - m.contents = Belt_MapString.set(m.contents, i.toString(), i.toString()); + m = Belt_MapString.set(m, i.toString(), i.toString()); } }); timing("querying", () => { for (let i = 0; i <= 1000000; ++i) { - Belt_MapString.get(m.contents, i.toString()); + Belt_MapString.get(m, i.toString()); } }); } diff --git a/tests/ounit_tests/ounit_sroa_tests.ml b/tests/ounit_tests/ounit_sroa_tests.ml new file mode 100644 index 0000000000..c61dd7feb0 --- /dev/null +++ b/tests/ounit_tests/ounit_sroa_tests.ml @@ -0,0 +1,200 @@ +open OUnit + +let loc = Location.none + +let read block index = + Lambda.prim + ~primitive:(Pfield (index, Fld_tuple)) + ~args:[Lambda.var block] + loc + +let write block index value = + Lambda.prim + ~primitive:(Psetfield (index, Fld_record_set (string_of_int index))) + ~args:[Lambda.var block; value] + loc + +let debugger = Lambda.prim ~primitive:Pdebugger ~args:[] loc + +let count_debuggers lam = + let count = ref 0 in + let rec loop (lam : Lambda.t) = + (match lam with + | Lprim {primitive = Pdebugger} -> incr count + | _ -> ()); + ignore + (Lambda.shallow_exists + (fun child -> + loop child; + false) + lam) + in + loop lam; + !count + +let contains_storage lam = + let rec loop (lam : Lambda.t) = + match lam with + | Llet _ | Lassign _ -> true + | _ -> Lambda.shallow_exists loop lam + in + loop lam + +let pair_info = + Lambda.Blk_record + { + fields = [|("left", false); ("right", false)|]; + mutable_flag = Asttypes.Mutable; + } + +let suites = + __FILE__ + >::: [ + ( "replaces multiple fields in initializer order" >:: fun _ -> + let block = Ident.create "pair" in + let initializers = + [ + Lambda.const (Lambda.const_int 10); + Lambda.const (Lambda.const_int 20); + ] + in + let body = + Lambda.seq (write block 1 (read block 0)) (read block 1) + in + match + Lam_pass_sroa.replace ~block ~info:pair_info ~initializers body + with + | Some + (Llet + ( Alias, + field0, + Lconst (Const_int 10l), + Llet + ( Variable, + field1, + Lconst (Const_int 20l), + Lsequence (Lassign (assigned, Lvar read), Lvar returned) + ) )) -> + assert_equal "pair_left" (Ident.name field0); + assert_equal "pair_right" (Ident.name field1); + assert_bool "field one gets its own binding" + (Ident.same field1 assigned); + assert_bool "the assignment reads field zero" + (Ident.same field0 read); + assert_bool "the result reads field one" + (Ident.same field1 returned) + | _ -> assert_failure "expected two scalar bindings" ); + ( "replaces fields captured by a closure" >:: fun _ -> + let block = Ident.create "pair" in + let closure = + Lambda.function_ ~loc ~attr:Lambda.default_function_attribute + ~params:[] + ~body:(Lambda.seq (write block 1 (read block 0)) (read block 1)) + in + match + Lam_pass_sroa.replace ~block ~info:pair_info + ~initializers: + [ + Lambda.const (Lambda.const_int 10); + Lambda.const (Lambda.const_int 20); + ] + closure + with + | Some + (Llet + ( Alias, + field0, + _, + Llet + ( Variable, + field1, + _, + Lfunction + { + body = + Lsequence + (Lassign (assigned, Lvar read), Lvar returned); + } ) )) -> + assert_bool "the closure writes field one" + (Ident.same field1 assigned); + assert_bool "the closure reads field zero" (Ident.same field0 read); + assert_bool "the closure reads field one" + (Ident.same field1 returned) + | _ -> assert_failure "expected scalar closure captures" ); + ( "preserves unrelated closure subtrees" >:: fun _ -> + let block = Ident.create "pair" in + let unrelated = + Lambda.function_ ~loc ~attr:Lambda.default_function_attribute + ~params:[] ~body:Lambda.lambda_unit + in + let body = Lambda.seq (read block 0) unrelated in + match + Lam_pass_sroa.replace ~block ~info:Lambda.ref_tag_info + ~initializers:[Lambda.const (Lambda.const_int 10)] + body + with + | Some (Llet (Alias, _, _, Lsequence (Lvar _, preserved_unrelated))) + -> + assert_bool "the unrelated subtree is physically shared" + (preserved_unrelated == unrelated) + | _ -> assert_failure "expected a scalar read followed by a closure" + ); + ( "keeps effectful read-only fields strict" >:: fun _ -> + let block = Ident.create "cell" in + let body = Lambda.seq (read block 0) (read block 0) in + match + Lam_pass_sroa.replace ~block ~info:Lambda.ref_tag_info + ~initializers:[debugger] body + with + | Some + (Llet + ( Strict, + field, + Lprim {primitive = Pdebugger}, + Lsequence (Lvar first, Lvar second) )) -> + assert_bool "both reads use the strict scalar" + (Ident.same field first && Ident.same field second) + | _ -> assert_failure "expected one strict scalar binding" ); + ( "removes write-only storage but preserves effects" >:: fun _ -> + let block = Ident.create "pair" in + let body = + Lambda.seq (write block 0 debugger) (write block 1 debugger) + in + match + Lam_pass_sroa.replace ~block ~info:pair_info + ~initializers:[Lambda.const (Lambda.const_int 10); debugger] + body + with + | Some replacement -> + assert_equal 3 (count_debuggers replacement); + assert_bool "write-only fields have no scalar storage" + (not (contains_storage replacement)) + | None -> assert_failure "expected write-only fields to scalarize" ); + ( "removes write-only storage captured by a closure" >:: fun _ -> + let block = Ident.create "cell" in + let closure = + Lambda.function_ ~loc ~attr:Lambda.default_function_attribute + ~params:[] ~body:(write block 0 debugger) + in + match + Lam_pass_sroa.replace ~block ~info:Lambda.ref_tag_info + ~initializers:[debugger] closure + with + | Some replacement -> + assert_equal 2 (count_debuggers replacement); + assert_bool "the closure captures no scalar storage" + (not (contains_storage replacement)) + | None -> assert_failure "expected the closure write to scalarize" ); + ( "rejects a whole-block use" >:: fun _ -> + let block = Ident.create "pair" in + assert_equal None + (Lam_pass_sroa.replace ~block ~info:Lambda.ref_tag_info + ~initializers:[Lambda.const (Lambda.const_int 10)] + (Lambda.var block)) ); + ( "rejects an out-of-bounds field" >:: fun _ -> + let block = Ident.create "pair" in + assert_equal None + (Lam_pass_sroa.replace ~block ~info:Lambda.ref_tag_info + ~initializers:[Lambda.const (Lambda.const_int 10)] + (read block 1)) ); + ] diff --git a/tests/ounit_tests/ounit_tests_main.ml b/tests/ounit_tests/ounit_tests_main.ml index 260c75e348..10c3a0cafb 100644 --- a/tests/ounit_tests/ounit_tests_main.ml +++ b/tests/ounit_tests/ounit_tests_main.ml @@ -20,6 +20,7 @@ let suites = Ounit_util_tests.suites; Ounit_rec_check_tests.suites; Ounit_lambda_constant_tests.suites; + Ounit_sroa_tests.suites; Ounit_ast_mapper0_tests.suites; Ounit_object_mutability_tests.suites; Ounit_pattern_printer_tests.suites; diff --git a/tests/tests/src/ari_regress_test.mjs b/tests/tests/src/ari_regress_test.mjs index 15fe3c580f..3b339197ba 100644 --- a/tests/tests/src/ari_regress_test.mjs +++ b/tests/tests/src/ari_regress_test.mjs @@ -3,13 +3,11 @@ import * as Mocha from "mocha"; import * as Test_utils from "./test_utils.mjs"; -let h = { - contents: 0 -}; +let h = 0; function g1(x, y) { let u = x + y | 0; - h.contents = h.contents + 1 | 0; + h = h + 1 | 0; return (xx, yy) => (xx + yy | 0) + u | 0; } @@ -23,7 +21,7 @@ Mocha.describe("Ari_regress_test", () => { Mocha.test("curry", () => Test_utils.eq("File \"ari_regress_test.res\", line 25, characters 7-14", 7, 7)); Mocha.test("curry2", () => Test_utils.eq("File \"ari_regress_test.res\", line 30, characters 6-13", 14, (v(1), v(1)))); Mocha.test("curry3", () => Test_utils.eq("File \"ari_regress_test.res\", line 40, characters 7-14", x, 14)); - Mocha.test("ref count", () => Test_utils.eq("File \"ari_regress_test.res\", line 44, characters 7-14", h.contents, 2)); + Mocha.test("ref count", () => Test_utils.eq("File \"ari_regress_test.res\", line 44, characters 7-14", h, 2)); }); /* Not a pure module */ diff --git a/tests/tests/src/bs_auto_uncurry_test.mjs b/tests/tests/src/bs_auto_uncurry_test.mjs index 46ec8a4fd5..0c6a074806 100644 --- a/tests/tests/src/bs_auto_uncurry_test.mjs +++ b/tests/tests/src/bs_auto_uncurry_test.mjs @@ -12,22 +12,20 @@ function hi (cb){ Mocha.describe("Bs_auto_uncurry_test", () => { Mocha.test("callback_test", () => { - let xs = { - contents: /* [] */0 - }; + let xs = /* [] */0; hi(x => { - xs.contents = { + xs = { hd: x, - tl: xs.contents + tl: xs }; }); hi(x => { - xs.contents = { + xs = { hd: x, - tl: xs.contents + tl: xs }; }); - Test_utils.eq("File \"bs_auto_uncurry_test.res\", line 20, characters 7-14", xs.contents, { + Test_utils.eq("File \"bs_auto_uncurry_test.res\", line 20, characters 7-14", xs, { hd: undefined, tl: { hd: undefined, diff --git a/tests/tests/src/cps_test.mjs b/tests/tests/src/cps_test.mjs index 008e854db0..d0fc1763ea 100644 --- a/tests/tests/src/cps_test.mjs +++ b/tests/tests/src/cps_test.mjs @@ -5,9 +5,7 @@ import * as Test_utils from "./test_utils.mjs"; import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.mjs"; function test_sum() { - let v = { - contents: 0 - }; + let v = 0; let f = (_n, _acc) => { while (true) { let acc = _acc; @@ -16,7 +14,7 @@ function test_sum() { return acc(); } _acc = () => { - v.contents = v.contents + n | 0; + v = v + n | 0; return acc(); }; _n = n - 1 | 0; @@ -24,36 +22,32 @@ function test_sum() { }; }; f(10, () => {}); - return v.contents; + return v; } function test_closure() { - let v = { - contents: 0 - }; + let v = 0; let arr = Stdlib_Array.make(6, x => x); for (let i = 0; i <= 5; ++i) { arr[i] = param => i; } arr.forEach(i => { - v.contents = v.contents + i(0) | 0; + v = v + i(0) | 0; }); - return v.contents; + return v; } function test_closure2() { - let v = { - contents: 0 - }; + let v = 0; let arr = Stdlib_Array.make(6, x => x); for (let i = 0; i <= 5; ++i) { let j = i + i | 0; arr[i] = param => j; } arr.forEach(i => { - v.contents = v.contents + i(0) | 0; + v = v + i(0) | 0; }); - return v.contents; + return v; } Mocha.describe("Cps_test", () => { diff --git a/tests/tests/src/for_loop_test.mjs b/tests/tests/src/for_loop_test.mjs index 9cd4c63ad4..bfdbf6a56f 100644 --- a/tests/tests/src/for_loop_test.mjs +++ b/tests/tests/src/for_loop_test.mjs @@ -8,90 +8,76 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.mjs"; Mocha.describe("For_loop_test", () => { Mocha.test("for_loop_test_3", () => { let for_3 = x => { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); for (let i = 0, i_finish = x.length; i < i_finish; ++i) { let j = (i << 1); arr[i] = () => { - v.contents = v.contents + j | 0; + v = v + j | 0; }; } arr.forEach(x => x()); - return v.contents; + return v; }; Test_utils.eq("File \"for_loop_test.res\", line 16, characters 7-14", 90, for_3(Stdlib_Array.make(10, 2))); }); Mocha.test("for_loop_test_4", () => { let for_4 = x => { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); for (let i = 0, i_finish = x.length; i < i_finish; ++i) { let j = (i << 1); let k = (j << 1); arr[i] = () => { - v.contents = v.contents + k | 0; + v = v + k | 0; }; } arr.forEach(x => x()); - return v.contents; + return v; }; Test_utils.eq("File \"for_loop_test.res\", line 31, characters 7-14", 180, for_4(Stdlib_Array.make(10, 2))); }); Mocha.test("for_loop_test_5", () => { let for_5 = (x, u) => { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); for (let i = 0, i_finish = x.length; i < i_finish; ++i) { let k = (u << 1) * u | 0; arr[i] = () => { - v.contents = v.contents + k | 0; + v = v + k | 0; }; } arr.forEach(x => x()); - return v.contents; + return v; }; Test_utils.eq("File \"for_loop_test.res\", line 46, characters 7-14", 2420, for_5(Stdlib_Array.make(10, 2), 11)); }); Mocha.test("for_loop_test_6", () => { let for_6 = (x, u) => { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); - let v4 = { - contents: 0 - }; - let v5 = { - contents: 0 - }; + let v4 = 0; + let v5 = 0; let inspect_3 = -1; - v4.contents = v4.contents + 1 | 0; + v4 = v4 + 1 | 0; for (let j = 0; j <= 1; ++j) { - v5.contents = v5.contents + 1 | 0; - let v2 = { - contents: 0 - }; + v5 = v5 + 1 | 0; + let v2 = 0; for (let i = 0, i_finish = x.length; i < i_finish; ++i) { let k = (u << 1) * u | 0; - let h = (v5.contents << 1); - v2.contents = v2.contents + 1 | 0; + let h = (v5 << 1); + v2 = v2 + 1 | 0; arr[i] = () => { - v.contents = (((((v.contents + k | 0) + v2.contents | 0) + v4.contents | 0) + v5.contents | 0) + h | 0) + u | 0; + v = (((((v + k | 0) + v2 | 0) + v4 | 0) + v5 | 0) + h | 0) + u | 0; }; } - inspect_3 = v2.contents; + inspect_3 = v2; } arr.forEach(x => x()); return [ - v.contents, - v4.contents, - v5.contents, + v, + v4, + v5, inspect_3 ]; }; @@ -104,83 +90,71 @@ Mocha.describe("For_loop_test", () => { }); Mocha.test("for_loop_test_7", () => { let for_7 = () => { - let v = { - contents: 0 - }; + let v = 0; let arr = Stdlib_Array.make(21, () => {}); for (let i = 0; i <= 6; ++i) { for (let j = 0; j <= 2; ++j) { arr[(i * 3 | 0) + j | 0] = () => { - v.contents = (v.contents + i | 0) + j | 0; + v = (v + i | 0) + j | 0; }; } } arr.forEach(f => f()); - return v.contents; + return v; }; Test_utils.eq("File \"for_loop_test.res\", line 91, characters 7-14", 84, for_7()); }); Mocha.test("for_loop_test_8", () => { let for_8 = () => { - let v = { - contents: 0 - }; + let v = 0; let arr = Stdlib_Array.make(21, () => {}); for (let i = 0; i <= 6; ++i) { let k = (i << 1); for (let j = 0; j <= 2; ++j) { let h = i + j | 0; arr[(i * 3 | 0) + j | 0] = () => { - v.contents = (((v.contents + i | 0) + j | 0) + h | 0) + k | 0; + v = (((v + i | 0) + j | 0) + h | 0) + k | 0; }; } } arr.forEach(f => f()); - return v.contents; + return v; }; Test_utils.eq("File \"for_loop_test.res\", line 110, characters 7-14", 294, for_8()); }); Mocha.test("for_loop_test_9", () => { let for_9 = () => { - let v = { - contents: /* [] */0 - }; + let v = /* [] */0; let collect = x => { - v.contents = { + v = { hd: x, - tl: v.contents + tl: v }; }; - let vv = { - contents: 0 - }; - let vv2 = { - contents: 0 - }; + let vv = 0; + let vv2 = 0; let arr = Stdlib_Array.make(4, () => {}); let arr2 = Stdlib_Array.make(2, () => {}); for (let i = 0; i <= 1; ++i) { - let v$1 = { - contents: 0 - }; - v$1.contents = v$1.contents + i | 0; + let v$1 = 0; + v$1 = v$1 + i | 0; for (let j = 0; j <= 1; ++j) { - v$1.contents = v$1.contents + 1 | 0; - collect(v$1.contents); + v$1 = v$1 + 1 | 0; + collect(v$1); arr[(i << 1) + j | 0] = () => { - vv.contents = vv.contents + v$1.contents | 0; + vv = vv + v$1 | 0; }; } arr2[i] = () => { - vv2.contents = vv2.contents + v$1.contents | 0; + vv2 = vv2 + v$1 | 0; }; } arr.forEach(f => f()); arr2.forEach(f => f()); return [[ - vv.contents, - Stdlib_List.toArray(Stdlib_List.reverse(v.contents)), - vv2.contents + vv, + Stdlib_List.toArray(Stdlib_List.reverse(v)), + vv2 ]]; }; Test_utils.eq("File \"for_loop_test.res\", line 158, characters 7-14", [[ diff --git a/tests/tests/src/gpr_858_unit2_test.mjs b/tests/tests/src/gpr_858_unit2_test.mjs index dddf174039..635ee3b033 100644 --- a/tests/tests/src/gpr_858_unit2_test.mjs +++ b/tests/tests/src/gpr_858_unit2_test.mjs @@ -1,15 +1,15 @@ // Generated by ReScript, PLEASE EDIT WITH CARE -let delayed = { - contents: () => {} -}; +function delayed() { + +} for (let i = 1; i <= 2; ++i) { let f = (n, x) => { if (x !== 0) { - let prev = delayed.contents; - delayed.contents = () => { + let prev = delayed; + delayed = () => { prev(); f(((n + 1 | 0) + i | 0) - i | 0, x - 1 | 0); }; @@ -31,6 +31,6 @@ for (let i = 1; i <= 2; ++i) { f(0, i); } -delayed.contents(); +delayed(); /* Not a pure module */ diff --git a/tests/tests/src/lazy_test.mjs b/tests/tests/src/lazy_test.mjs index f35fbe590c..6594bd0299 100644 --- a/tests/tests/src/lazy_test.mjs +++ b/tests/tests/src/lazy_test.mjs @@ -4,18 +4,16 @@ import * as Mocha from "mocha"; import * as Test_utils from "./test_utils.mjs"; import * as Stdlib_Lazy from "@rescript/runtime/lib/es6/Stdlib_Lazy.mjs"; -let u = { - contents: 3 -}; +let u = 3; let v = Stdlib_Lazy.make(() => { - u.contents = 32; + u = 32; }); function lazy_test() { - let h = u.contents; + let h = u; Stdlib_Lazy.get(v); - let g = u.contents; + let g = u; return [ h, g diff --git a/tests/tests/src/loop_regression_test.mjs b/tests/tests/src/loop_regression_test.mjs index badc684ce1..1018a52188 100644 --- a/tests/tests/src/loop_regression_test.mjs +++ b/tests/tests/src/loop_regression_test.mjs @@ -4,19 +4,15 @@ import * as Mocha from "mocha"; import * as Test_utils from "./test_utils.mjs"; function f() { - let v = { - contents: 0 - }; - let acc = { - contents: 0 - }; + let v = 0; + let acc = 0; let n = 10; while (true) { - if (v.contents > n) { - return acc.contents; + if (v > n) { + return acc; } - acc.contents = acc.contents + v.contents | 0; - v.contents = v.contents + 1 | 0; + acc = acc + v | 0; + v = v + 1 | 0; continue; }; } diff --git a/tests/tests/src/mario_game.mjs b/tests/tests/src/mario_game.mjs index 21be662726..8253ff6171 100644 --- a/tests/tests/src/mario_game.mjs +++ b/tests/tests/src/mario_game.mjs @@ -790,9 +790,7 @@ let Particle = { process: process }; -let id_counter = { - contents: Stdlib_Int.Constants.minValue -}; +let id_counter = Stdlib_Int.Constants.minValue; function setup_obj(gOpt, spdOpt, param) { let has_gravity = gOpt !== undefined ? gOpt : true; @@ -843,8 +841,8 @@ function make_type$2(x) { } function new_id() { - id_counter.contents = id_counter.contents + 1 | 0; - return id_counter.contents; + id_counter = id_counter + 1 | 0; + return id_counter; } function make$2(idOpt, dirOpt, spawnable, context, param) { @@ -1580,25 +1578,21 @@ let Viewport = { update: update }; -let pressed_keys = { - left: false, - right: false, - up: false, - down: false, - bbox: 0 -}; +let pressed_keys_left = false; -let collid_objs = { - contents: /* [] */0 -}; +let pressed_keys_right = false; -let particles = { - contents: /* [] */0 -}; +let pressed_keys_up = false; -let last_time = { - contents: 0 -}; +let pressed_keys_down = false; + +let pressed_keys_bbox = 0; + +let collid_objs = /* [] */0; + +let particles = /* [] */0; + +let last_time = 0; function calc_fps(t0, t1) { let delta = (t1 - t0) / 1000; @@ -2138,7 +2132,7 @@ function update_collidable(state, collid, all_collids) { vpt_adj_xy.x, vpt_adj_xy.y ]); - if (pressed_keys.bbox === 1) { + if (pressed_keys_bbox === 1) { render_bbox(spr, [ vpt_adj_xy.x, vpt_adj_xy.y @@ -2152,22 +2146,22 @@ function update_collidable(state, collid, all_collids) { function translate_keys() { let ctrls_0 = [ - pressed_keys.left, + pressed_keys_left, "CLeft" ]; let ctrls_1 = { hd: [ - pressed_keys.right, + pressed_keys_right, "CRight" ], tl: { hd: [ - pressed_keys.up, + pressed_keys_up, "CUp" ], tl: { hd: [ - pressed_keys.down, + pressed_keys_down, "CDown" ], tl: /* [] */0 @@ -2210,19 +2204,19 @@ function run_update_collid(state, collid, all_collids) { player = collid; } let evolved = update_collidable(state, player, all_collids); - collid_objs.contents = Stdlib_List.concat(collid_objs.contents, evolved); + collid_objs = Stdlib_List.concat(collid_objs, evolved); return player; } let obj = collid._2; let evolved$1 = update_collidable(state, collid, all_collids); if (!obj.kill) { - collid_objs.contents = { + collid_objs = { hd: collid, - tl: Stdlib_List.concat(collid_objs.contents, evolved$1) + tl: Stdlib_List.concat(collid_objs, evolved$1) }; } let new_parts = obj.kill ? kill(collid, state.ctx) : /* [] */0; - particles.contents = Stdlib_List.concat(particles.contents, new_parts); + particles = Stdlib_List.concat(particles, new_parts); return collid; } @@ -2250,10 +2244,10 @@ function update_loop(canvas, param, map_dim) { if (state.game_over === true) { return game_win(state.ctx); } - collid_objs.contents = /* [] */0; - particles.contents = /* [] */0; - let fps$1 = calc_fps(last_time.contents, time); - last_time.contents = time; + collid_objs = /* [] */0; + particles = /* [] */0; + let fps$1 = calc_fps(last_time, time); + last_time = time; clear_canvas(canvas); let vpos_x_int = state.vpt.pos.x / 5 | 0; let bgd_width = state.bgd.params.frame_size[0] | 0; @@ -2283,17 +2277,17 @@ function update_loop(canvas, param, map_dim) { x, y ]); - if (!part.kill) { - particles.contents = { - hd: part, - tl: particles.contents - }; + if (part.kill) { return; } + particles = { + hd: part, + tl: particles + }; }); fps(canvas, fps$1); hud(canvas, state$1.score, state$1.coins); - requestAnimationFrame(t => update_helper(t, state$1, player$1, collid_objs.contents, particles.contents)); + requestAnimationFrame(t => update_helper(t, state$1, player$1, collid_objs, particles)); }; update_helper(0, state, player, param[1], /* [] */0); } @@ -2303,19 +2297,19 @@ function keydown(evt) { if (match >= 41) { switch (match) { case 65 : - pressed_keys.left = true; + pressed_keys_left = true; break; case 66 : - pressed_keys.bbox = (pressed_keys.bbox + 1 | 0) % 2; + pressed_keys_bbox = (pressed_keys_bbox + 1 | 0) % 2; break; case 68 : - pressed_keys.right = true; + pressed_keys_right = true; break; case 83 : - pressed_keys.down = true; + pressed_keys_down = true; break; case 87 : - pressed_keys.up = true; + pressed_keys_up = true; break; } } else if (match >= 32) { @@ -2326,17 +2320,17 @@ function keydown(evt) { case 36 : break; case 37 : - pressed_keys.left = true; + pressed_keys_left = true; break; case 32 : case 38 : - pressed_keys.up = true; + pressed_keys_up = true; break; case 39 : - pressed_keys.right = true; + pressed_keys_right = true; break; case 40 : - pressed_keys.down = true; + pressed_keys_down = true; break; } } @@ -2348,22 +2342,18 @@ function keyup(evt) { if (match >= 68) { if (match !== 83) { if (match !== 87) { - if (match >= 69) { - - } else { - pressed_keys.right = false; + if (match < 69) { + pressed_keys_right = false; } } else { - pressed_keys.up = false; + pressed_keys_up = false; } } else { - pressed_keys.down = false; + pressed_keys_down = false; } } else if (match >= 41) { - if (match !== 65) { - - } else { - pressed_keys.left = false; + if (match === 65) { + pressed_keys_left = false; } } else if (match >= 32) { switch (match) { @@ -2373,17 +2363,17 @@ function keyup(evt) { case 36 : break; case 37 : - pressed_keys.left = false; + pressed_keys_left = false; break; case 32 : case 38 : - pressed_keys.up = false; + pressed_keys_up = false; break; case 39 : - pressed_keys.right = false; + pressed_keys_right = false; break; case 40 : - pressed_keys.down = false; + pressed_keys_down = false; break; } } diff --git a/tests/tests/src/move_ref_assignment.mjs b/tests/tests/src/move_ref_assignment.mjs index acd9050656..626ab5df48 100644 --- a/tests/tests/src/move_ref_assignment.mjs +++ b/tests/tests/src/move_ref_assignment.mjs @@ -3,15 +3,13 @@ let j = 1; -let k = { - c: 1 -}; +let k = 1; function upd() { - k.c = 3; + k = 3; } -if (k.c === 1) { +if (k === 1) { upd(); j = j + 2 | 0; console.log("correct"); diff --git a/tests/tests/src/sroa_test.mjs b/tests/tests/src/sroa_test.mjs new file mode 100644 index 0000000000..7364a631be --- /dev/null +++ b/tests/tests/src/sroa_test.mjs @@ -0,0 +1,179 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Mocha from "mocha"; +import * as Test_utils from "./test_utils.mjs"; +import * as Stdlib_List from "@rescript/runtime/lib/es6/Stdlib_List.mjs"; + +function localPair() { + let pair_left = 10; + let pair_right = 20; + pair_left = pair_left + 1 | 0; + pair_right = pair_left + pair_right | 0; + return pair_left + pair_right | 0; +} + +function capturedPair() { + let pair_left = 10; + let pair_right = 20; + let bumpLeft = () => { + pair_left = pair_left + 1 | 0; + }; + bumpLeft(); + bumpLeft(); + return pair_left + pair_right | 0; +} + +function initializationOrder() { + let seen = /* [] */0; + let initialize = value => { + seen = { + hd: value, + tl: seen + }; + return value; + }; + let pair_left = initialize(1); + let pair_right = initialize(2); + pair_left = pair_left + 1 | 0; + return [ + pair_left + pair_right | 0, + Stdlib_List.toArray(Stdlib_List.reverse(seen)) + ]; +} + +function consumePair(pair) { + return pair.left + pair.right | 0; +} + +function escapedPair() { + return consumePair({ + left: 10, + right: 20 + }); +} + +function fieldUseCleanup() { + let effects = /* [] */0; + let mark = value => { + effects = { + hd: value, + tl: effects + }; + return value; + }; + let fields_live = 1; + mark(2); + mark(3); + let fields_readOnly = mark(4); + mark(5); + mark(6); + fields_live = fields_live + fields_readOnly | 0; + return [ + fields_live, + Stdlib_List.toArray(Stdlib_List.reverse(effects)) + ]; +} + +function overwrittenBeforeRead() { + let effects = /* [] */0; + let mark = value => { + effects = { + hd: value, + tl: effects + }; + return value; + }; + let cell = mark(1); + cell = mark(2); + return [ + cell, + Stdlib_List.toArray(Stdlib_List.reverse(effects)) + ]; +} + +function capturedWriteOnly() { + let effects = /* [] */0; + let mark = value => { + effects = { + hd: value, + tl: effects + }; + return value; + }; + mark(1); + mark(2); + return Stdlib_List.toArray(Stdlib_List.reverse(effects)); +} + +function uncalledWriteOnlyClosure() { + let effects = /* [] */0; + let mark = value => { + effects = { + hd: value, + tl: effects + }; + return value; + }; + mark(1); + return Stdlib_List.toArray(Stdlib_List.reverse(effects)); +} + +function readOnlyFieldSnapshotsInitializer() { + let source = 1; + let cell = source; + source = 2; + return cell; +} + +Mocha.describe("Sroa_test", () => { + Mocha.test("scalarizes a local mutable record", () => Test_utils.eq("File \"sroa_test.res\", line 115, characters 53-60", 42, localPair())); + Mocha.test("shares scalar fields with closures", () => Test_utils.eq("File \"sroa_test.res\", line 116, characters 54-61", 32, capturedPair())); + Mocha.test("preserves initializer order", () => Test_utils.eq("File \"sroa_test.res\", line 117, characters 47-54", [ + 4, + [ + 1, + 2 + ] + ], initializationOrder())); + Mocha.test("retains an escaping record", () => Test_utils.eq("File \"sroa_test.res\", line 118, characters 46-53", 30, consumePair({ + left: 10, + right: 20 + }))); + Mocha.test("cleans up fields according to their uses", () => Test_utils.eq("File \"sroa_test.res\", line 120, characters 7-14", [ + 5, + [ + 2, + 3, + 4, + 5, + 6 + ] + ], fieldUseCleanup())); + Mocha.test("preserves overwritten initializer effects", () => Test_utils.eq("File \"sroa_test.res\", line 123, characters 7-14", [ + 2, + [ + 1, + 2 + ] + ], overwrittenBeforeRead())); + Mocha.test("removes write-only fields captured by closures", () => Test_utils.eq("File \"sroa_test.res\", line 126, characters 7-14", [ + 1, + 2 + ], capturedWriteOnly())); + Mocha.test("does not evaluate writes in uncalled closures", () => Test_utils.eq("File \"sroa_test.res\", line 129, characters 7-14", [1], uncalledWriteOnlyClosure())); + Mocha.test("read-only fields snapshot their initializer", () => Test_utils.eq("File \"sroa_test.res\", line 132, characters 7-14", 1, readOnlyFieldSnapshotsInitializer())); +}); + +export { + localPair, + capturedPair, + initializationOrder, + consumePair, + escapedPair, + fieldUseCleanup, + overwrittenBeforeRead, + capturedWriteOnly, + uncalledWriteOnlyClosure, + readOnlyFieldSnapshotsInitializer, +} +/* Not a pure module */ diff --git a/tests/tests/src/sroa_test.res b/tests/tests/src/sroa_test.res new file mode 100644 index 0000000000..725178766a --- /dev/null +++ b/tests/tests/src/sroa_test.res @@ -0,0 +1,134 @@ +open Mocha +open Test_utils + +type pair = { + mutable left: int, + mutable right: int, +} + +type fieldUses = { + mutable live: int, + mutable deadPure: int, + mutable deadEffect: int, + mutable writeOnly: int, + mutable readOnly: int, +} + +type cell = {mutable value: int} + +let localPair = () => { + let pair = {left: 10, right: 20} + pair.left = pair.left + 1 + pair.right = pair.left + pair.right + pair.left + pair.right +} + +let capturedPair = () => { + let pair = {left: 10, right: 20} + let bumpLeft = () => pair.left = pair.left + 1 + bumpLeft() + bumpLeft() + pair.left + pair.right +} + +let initializationOrder = () => { + let seen = ref(list{}) + let initialize = value => { + seen.contents = list{value, ...seen.contents} + value + } + let pair = {left: initialize(1), right: initialize(2)} + pair.left = pair.left + 1 + (pair.left + pair.right, List.toArray(List.reverse(seen.contents))) +} + +@inline(never) +let consumePair = pair => pair.left + pair.right + +let escapedPair = () => { + let pair = {left: 10, right: 20} + consumePair(pair) +} + +let fieldUseCleanup = () => { + let effects = ref(list{}) + let mark = value => { + effects.contents = list{value, ...effects.contents} + value + } + let fields = { + live: 1, + deadPure: 999, + deadEffect: mark(2), + writeOnly: mark(3), + readOnly: mark(4), + } + fields.writeOnly = mark(5) + fields.writeOnly = mark(6) + fields.live = fields.live + fields.readOnly + (fields.live, List.toArray(List.reverse(effects.contents))) +} + +let overwrittenBeforeRead = () => { + let effects = ref(list{}) + let mark = value => { + effects.contents = list{value, ...effects.contents} + value + } + let cell = {value: mark(1)} + cell.value = mark(2) + (cell.value, List.toArray(List.reverse(effects.contents))) +} + +let capturedWriteOnly = () => { + let effects = ref(list{}) + let mark = value => { + effects.contents = list{value, ...effects.contents} + value + } + let cell = {value: mark(1)} + let write = () => cell.value = mark(2) + write() + List.toArray(List.reverse(effects.contents)) +} + +let uncalledWriteOnlyClosure = () => { + let effects = ref(list{}) + let mark = value => { + effects.contents = list{value, ...effects.contents} + value + } + let cell = {value: mark(1)} + let write = () => cell.value = mark(2) + ignore(write) + List.toArray(List.reverse(effects.contents)) +} + +let readOnlyFieldSnapshotsInitializer = () => { + let source = ref(1) + let cell = {value: source.contents} + source.contents = 2 + cell.value +} + +describe(__MODULE__, () => { + test("scalarizes a local mutable record", () => eq(__LOC__, 42, localPair())) + test("shares scalar fields with closures", () => eq(__LOC__, 32, capturedPair())) + test("preserves initializer order", () => eq(__LOC__, (4, [1, 2]), initializationOrder())) + test("retains an escaping record", () => eq(__LOC__, 30, escapedPair())) + test("cleans up fields according to their uses", () => + eq(__LOC__, (5, [2, 3, 4, 5, 6]), fieldUseCleanup()) + ) + test("preserves overwritten initializer effects", () => + eq(__LOC__, (2, [1, 2]), overwrittenBeforeRead()) + ) + test("removes write-only fields captured by closures", () => + eq(__LOC__, [1, 2], capturedWriteOnly()) + ) + test("does not evaluate writes in uncalled closures", () => + eq(__LOC__, [1], uncalledWriteOnlyClosure()) + ) + test("read-only fields snapshot their initializer", () => + eq(__LOC__, 1, readOnlyFieldSnapshotsInitializer()) + ) +}) diff --git a/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs b/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs index c800fa3be6..4bab6002b7 100644 --- a/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs +++ b/tests/tests/src/stdlib/Stdlib_IteratorTests.mjs @@ -263,13 +263,11 @@ Test.run([ "Iterator next with omitted done" ], omittedDoneResult.contents, eq, "yield"); -let current = { - contents: 0 -}; +let current = 0; let createdIterator = Stdlib_Iterator.make(() => { - let value = current.contents; - current.contents = value + 1 | 0; + let value = current; + current = value + 1 | 0; if (value >= 2) { return Stdlib_Iterator.doneWithValue("done"); } else { @@ -301,13 +299,11 @@ Test.run([ "Creating your own iterator" ], createdProtocolResult.contents, eq, "protocol"); -let current$1 = { - contents: 0 -}; +let current$1 = 0; let createdIterableIterator = Stdlib_IterableIterator.make(() => { - let value = current$1.contents; - current$1.contents = value + 1 | 0; + let value = current$1; + current$1 = value + 1 | 0; if (value >= 2) { return Stdlib_Iterator.doneWithValue("done"); } else { @@ -512,25 +508,17 @@ Test.run([ "Async forEach" ], asyncResult.contents, eq, "second"); -let asyncResult$1 = { - contents: undefined -}; +let asyncResult$1; -let count = { - contents: 0 -}; +let count = 0; -let asyncIterableIteratorResult = { - contents: undefined -}; +let asyncIterableIteratorResult; -let asyncIterableIteratorCount = { - contents: 0 -}; +let asyncIterableIteratorCount = 0; let asyncIterator = Stdlib_AsyncIterator.make(async () => { - let currentCount = count.contents; - count.contents = currentCount + 1 | 0; + let currentCount = count; + count = currentCount + 1 | 0; if (currentCount === 3) { return Stdlib_AsyncIterator.doneWithValue(currentCount); } else { @@ -539,10 +527,10 @@ let asyncIterator = Stdlib_AsyncIterator.make(async () => { }); await Stdlib_AsyncIterator.forEach(asyncIterator, value => { - if (value === 2) { - asyncResult$1.contents = "done"; + if (value !== 2) { return; } + asyncResult$1 = "done"; }); Test.run([ @@ -553,7 +541,7 @@ Test.run([ 54 ], "Creating your own async iterator" -], asyncResult$1.contents, eq, "done"); +], asyncResult$1, eq, "done"); let asyncOmittedDoneValues = { contents: [] @@ -812,8 +800,8 @@ Test.run([ ], asyncGeneratorThrowErrorResult.contents, eq, "throwError"); let createdAsyncIterableIterator = Stdlib_AsyncIterableIterator.make(async () => { - let currentCount = asyncIterableIteratorCount.contents; - asyncIterableIteratorCount.contents = currentCount + 1 | 0; + let currentCount = asyncIterableIteratorCount; + asyncIterableIteratorCount = currentCount + 1 | 0; if (currentCount === 2) { return Stdlib_AsyncIterator.done(); } else { @@ -822,10 +810,10 @@ let createdAsyncIterableIterator = Stdlib_AsyncIterableIterator.make(async () => }); await Stdlib_AsyncIterableIterator.forEach(createdAsyncIterableIterator, value => { - if (value === 1) { - asyncIterableIteratorResult.contents = "iterable"; + if (value !== 1) { return; } + asyncIterableIteratorResult = "iterable"; }); Test.run([ @@ -836,7 +824,7 @@ Test.run([ 56 ], "Creating your own async iterable iterator" -], asyncIterableIteratorResult.contents, eq, "iterable"); +], asyncIterableIteratorResult, eq, "iterable"); export { eq, diff --git a/tests/tests/src/stdlib/Stdlib_PromiseTest.mjs b/tests/tests/src/stdlib/Stdlib_PromiseTest.mjs index dd251b0f37..fc1a804770 100644 --- a/tests/tests/src/stdlib/Stdlib_PromiseTest.mjs +++ b/tests/tests/src/stdlib/Stdlib_PromiseTest.mjs @@ -225,14 +225,12 @@ function thenAfterCatch() { } function testCatchFinally() { - let wasCalled = { - contents: false - }; + let wasCalled = false; Stdlib_Promise.$$catch(Promise.resolve(5).then(param => Promise.reject({ RE_EXN_ID: TestError, _1: "test" })).then(v => Promise.resolve(v)), param => Promise.resolve()).finally(() => { - wasCalled.contents = true; + wasCalled = true; }).then(v => { Test.run([ [ @@ -251,17 +249,15 @@ function testCatchFinally() { 59 ], "finally should have been called" - ], wasCalled.contents, equal, true); + ], wasCalled, equal, true); return Promise.resolve(); }); } function testResolveFinally() { - let wasCalled = { - contents: false - }; + let wasCalled = false; Promise.resolve(5).then(v => Promise.resolve(v + 5 | 0)).finally(() => { - wasCalled.contents = true; + wasCalled = true; }).then(v => { Test.run([ [ @@ -280,7 +276,7 @@ function testResolveFinally() { 59 ], "finally should have been called" - ], wasCalled.contents, equal, true); + ], wasCalled, equal, true); return Promise.resolve(); }); } @@ -306,14 +302,12 @@ let Catching = { }; function testParallel() { - let place = { - contents: 0 - }; + let place = 0; let delayedMsg = (ms, msg) => new Promise((resolve, param) => { setTimeout(() => { - place.contents = place.contents + 1 | 0; + place = place + 1 | 0; resolve([ - place.contents, + place, msg ]); }, ms); @@ -377,14 +371,12 @@ function testRace() { } function testParallel2() { - let place = { - contents: 0 - }; + let place = 0; let delayedMsg = (ms, msg) => new Promise((resolve, param) => { setTimeout(() => { - place.contents = place.contents + 1 | 0; + place = place + 1 | 0; resolve([ - place.contents, + place, msg ]); }, ms); @@ -418,14 +410,12 @@ function testParallel2() { } function testParallel3() { - let place = { - contents: 0 - }; + let place = 0; let delayedMsg = (ms, msg) => new Promise((resolve, param) => { setTimeout(() => { - place.contents = place.contents + 1 | 0; + place = place + 1 | 0; resolve([ - place.contents, + place, msg ]); }, ms); @@ -465,14 +455,12 @@ function testParallel3() { } function testParallel4() { - let place = { - contents: 0 - }; + let place = 0; let delayedMsg = (ms, msg) => new Promise((resolve, param) => { setTimeout(() => { - place.contents = place.contents + 1 | 0; + place = place + 1 | 0; resolve([ - place.contents, + place, msg ]); }, ms); @@ -518,14 +506,12 @@ function testParallel4() { } function testParallel5() { - let place = { - contents: 0 - }; + let place = 0; let delayedMsg = (ms, msg) => new Promise((resolve, param) => { setTimeout(() => { - place.contents = place.contents + 1 | 0; + place = place + 1 | 0; resolve([ - place.contents, + place, msg ]); }, ms); @@ -577,14 +563,12 @@ function testParallel5() { } function testParallel6() { - let place = { - contents: 0 - }; + let place = 0; let delayedMsg = (ms, msg) => new Promise((resolve, param) => { setTimeout(() => { - place.contents = place.contents + 1 | 0; + place = place + 1 | 0; resolve([ - place.contents, + place, msg ]); }, ms); diff --git a/tests/tests/src/stdlib/Stdlib_ResultTests.mjs b/tests/tests/src/stdlib/Stdlib_ResultTests.mjs index eac83648b3..1ee75a2ebc 100644 --- a/tests/tests/src/stdlib/Stdlib_ResultTests.mjs +++ b/tests/tests/src/stdlib/Stdlib_ResultTests.mjs @@ -7,14 +7,12 @@ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.mj let eq = Primitive_object.equal; function forEachIfOkCallFunction() { - let called = { - contents: [] - }; + let called = []; Stdlib_Result.forEach({ TAG: "Ok", _0: 3 }, i => { - called.contents.push(i); + called.push(i); }); Test.run([ [ @@ -24,20 +22,18 @@ function forEachIfOkCallFunction() { 72 ], "forEach: if ok, call function with ok value once" - ], called.contents, eq, [3]); + ], called, eq, [3]); } forEachIfOkCallFunction(); function forEachIfErrorDoNotCallFunction() { - let called = { - contents: [] - }; + let called = []; Stdlib_Result.forEach({ TAG: "Error", _0: 3 }, i => { - called.contents.push(i); + called.push(i); }); Test.run([ [ @@ -47,7 +43,7 @@ function forEachIfErrorDoNotCallFunction() { 63 ], "forEach: if error, do not call function" - ], called.contents, eq, []); + ], called, eq, []); } forEachIfErrorDoNotCallFunction(); diff --git a/tests/tests/src/test_for_loop.mjs b/tests/tests/src/test_for_loop.mjs index 4ec5550047..7968481784 100644 --- a/tests/tests/src/test_for_loop.mjs +++ b/tests/tests/src/test_for_loop.mjs @@ -14,79 +14,65 @@ function for_2(x) { } function for_3(x) { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); for (let i = 0, i_finish = x.length; i <= i_finish; ++i) { let j = (i << 1); arr[i] = () => { - v.contents = v.contents + j | 0; + v = v + j | 0; }; } arr.forEach(x => x()); - return v.contents; + return v; } function for_4(x) { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); for (let i = 0, i_finish = x.length; i <= i_finish; ++i) { let j = (i << 1); let k = (j << 1); arr[i] = () => { - v.contents = v.contents + k | 0; + v = v + k | 0; }; } arr.forEach(x => x()); - return v.contents; + return v; } function for_5(x, u) { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); for (let i = 0, i_finish = x.length; i <= i_finish; ++i) { let k = (u << 1) * u | 0; arr[i] = () => { - v.contents = v.contents + k | 0; + v = v + k | 0; }; } arr.forEach(x => x()); - return v.contents; + return v; } function for_6(x, u) { - let v = { - contents: 0 - }; + let v = 0; let arr = x.map(param => (() => {})); - let v4 = { - contents: 0 - }; - let v5 = { - contents: 0 - }; - v4.contents = v4.contents + 1 | 0; + let v4 = 0; + let v5 = 0; + v4 = v4 + 1 | 0; for (let j = 0; j <= 1; ++j) { - v5.contents = v5.contents + 1 | 0; - let v2 = { - contents: 0 - }; + v5 = v5 + 1 | 0; + let v2 = 0; for (let i = 0, i_finish = x.length; i <= i_finish; ++i) { let k = (u << 1) * u | 0; - let h = (v5.contents << 1); - v2.contents = v2.contents + 1 | 0; + let h = (v5 << 1); + v2 = v2 + 1 | 0; arr[i] = () => { - v.contents = (((((v.contents + k | 0) + v2.contents | 0) + u | 0) + v4.contents | 0) + v5.contents | 0) + h | 0; + v = (((((v + k | 0) + v2 | 0) + u | 0) + v4 | 0) + v5 | 0) + h | 0; }; } } arr.forEach(x => x()); - return v.contents; + return v; } export { diff --git a/tests/tests/src/test_ramification.mjs b/tests/tests/src/test_ramification.mjs index bcde916376..a77b877f31 100644 --- a/tests/tests/src/test_ramification.mjs +++ b/tests/tests/src/test_ramification.mjs @@ -31,14 +31,11 @@ function f(x) { } function f2(x) { - let v = 0; let y; if (x.TAG === "A") { - v = 1; let z = 33; y = z + 3 | 0; } else { - v = 1; let z$1 = 33; y = z$1 + 4 | 0; } @@ -46,15 +43,8 @@ function f2(x) { } function f3(x) { - let v = 0; let y; - if (x.TAG === "A") { - v = 1; - y = 3; - } else { - v = 1; - y = 4; - } + y = x.TAG === "A" ? 3 : 4; return y + 32 | 0; } diff --git a/tests/tests/src/test_simple_ref.mjs b/tests/tests/src/test_simple_ref.mjs index 68d40783de..ba711e800f 100644 --- a/tests/tests/src/test_simple_ref.mjs +++ b/tests/tests/src/test_simple_ref.mjs @@ -1,13 +1,11 @@ // Generated by ReScript, PLEASE EDIT WITH CARE -let v = { - contents: 0 -}; +let v = 0; function gen() { - v.contents = v.contents + 1 | 0; - return v.contents; + v = v + 1 | 0; + return v; } let h = { diff --git a/tests/tests/src/topsort_test.mjs b/tests/tests/src/topsort_test.mjs index 6bb0c9b498..93c25c6e49 100644 --- a/tests/tests/src/topsort_test.mjs +++ b/tests/tests/src/topsort_test.mjs @@ -262,20 +262,19 @@ if (!Primitive_object.equal(dfs2({ } function dfs3(nodes, graph) { - let visited = { - contents: /* [] */0 - }; + let visited = /* [] */0; let aux = (node, graph) => { - if (!Stdlib_List.has(visited.contents, node, (prim0, prim1) => prim0 === prim1)) { - visited.contents = { - hd: node, - tl: visited.contents - }; - return Stdlib_List.forEach(nexts(node, graph), x => aux(x, graph)); + if (Stdlib_List.has(visited, node, (prim0, prim1) => prim0 === prim1)) { + return; } + visited = { + hd: node, + tl: visited + }; + Stdlib_List.forEach(nexts(node, graph), x => aux(x, graph)); }; Stdlib_List.forEach(nodes, node => aux(node, graph)); - return Stdlib_List.reverse(visited.contents); + return Stdlib_List.reverse(visited); } if (!Primitive_object.equal(dfs3({ @@ -389,22 +388,20 @@ let grwork = { }; function unsafe_topsort(graph) { - let visited = { - contents: /* [] */0 - }; + let visited = /* [] */0; let sort_node = node => { - if (Stdlib_List.has(visited.contents, node, (prim0, prim1) => prim0 === prim1)) { + if (Stdlib_List.has(visited, node, (prim0, prim1) => prim0 === prim1)) { return; } let nodes = nexts(node, graph); Stdlib_List.forEach(nodes, sort_node); - visited.contents = { + visited = { hd: node, - tl: visited.contents + tl: visited }; }; Stdlib_List.forEach(graph, param => sort_node(param[0])); - return visited.contents; + return visited; } if (!Primitive_object.equal(unsafe_topsort(grwork), { @@ -440,9 +437,7 @@ if (!Primitive_object.equal(unsafe_topsort(grwork), { let Cycle = /* @__PURE__ */Primitive_exceptions.create("Topsort_test.Cycle"); function pathsort(graph) { - let visited = { - contents: /* [] */0 - }; + let visited = /* [] */0; let $plus$great = (node, path) => { if (Stdlib_List.has(path, node, (prim0, prim1) => prim0 === prim1)) { throw { @@ -461,17 +456,17 @@ function pathsort(graph) { }; let sort_nodes = (path, nodes) => Stdlib_List.forEach(nodes, node => sort_node(path, node)); let sort_node = (path, node) => { - if (!Stdlib_List.has(visited.contents, node, (prim0, prim1) => prim0 === prim1)) { - sort_nodes($plus$great(node, path), nexts(node, graph)); - visited.contents = { - hd: node, - tl: visited.contents - }; + if (Stdlib_List.has(visited, node, (prim0, prim1) => prim0 === prim1)) { return; } + sort_nodes($plus$great(node, path), nexts(node, graph)); + visited = { + hd: node, + tl: visited + }; }; Stdlib_List.forEach(graph, param => sort_node(/* [] */0, param[0])); - return visited.contents; + return visited; } if (!Primitive_object.equal(pathsort(grwork), { diff --git a/tests/tests/src/tuple_alloc.mjs b/tests/tests/src/tuple_alloc.mjs index 49361183d7..01ede70145 100644 --- a/tests/tests/src/tuple_alloc.mjs +++ b/tests/tests/src/tuple_alloc.mjs @@ -13,16 +13,14 @@ function incr(param) { v.contents = v.contents + 1 | 0; } -let vv = { - contents: 0 -}; +let vv = 0; function reset2() { - vv.contents = 0; + vv = 0; } function incr2() { - vv.contents = vv.contents + 1 | 0; + vv = vv + 1 | 0; } function f(a, b, d, e) {