Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#### :bug: Bug fix

- Fix the side-effect analysis treating bigint exponentiation and bounds-checked array and string reads as pure, which let dead-code elimination drop an unused one that throws: `let _ = 2n ** -1n` no longer raised. https://github.com/rescript-lang/rescript/pull/8617
- 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 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
Expand Down Expand Up @@ -70,6 +71,7 @@

- 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
- Split `lambda.ml` into the IR and its traversals, static exits and path translation, so the module defining `Lambda.t` no longer reaches into `Env` or `Path`. https://github.com/rescript-lang/rescript/pull/8618
- 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
Expand Down
16 changes: 15 additions & 1 deletion compiler/core/js_analyzer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,21 @@ let rec no_side_effect_expression_desc (x : J.expression_desc) =
&& Ext_list.for_all_snd kvs no_side_effect
| String_append (a, b) | Seq (a, b) -> no_side_effect a && no_side_effect b
| Length e | Caml_block_tag (e, _) | Typeof e -> no_side_effect e
| Bin (op, a, b) -> op <> Eq && no_side_effect a && no_side_effect b
| Bin (Eq, _, _) -> false
| Bin (((Pow | Div | Mod) as op), a, b) ->
(* On BigInt operands these throw: [**] on a negative exponent, [/] and
[%] on a zero divisor. The operand types are not known here, so only a
literal right operand that cannot throw is taken as pure. *)
let safe_literal =
match b.expression_desc with
| Number (BigInt {positive; value}) ->
if op = Pow then positive else value <> "0"
| Number (Int {i}) -> op = Pow || i <> 0l
| Number (Float _) -> true
| _ -> false
in
safe_literal && no_side_effect a
| Bin (_, a, b) -> no_side_effect a && no_side_effect b
| Tagged_template (call_expr, _, values) ->
no_side_effect call_expr && Ext_list.for_all values no_side_effect
| Js_not e | Js_bnot e -> no_side_effect e
Expand Down
19 changes: 13 additions & 6 deletions compiler/core/lam_analysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ let rec no_side_effects (lam : Lambda.t) : bool =
match args with
| [_; Lconst cst] -> not_zero_constant cst
| _ -> false)
| Ppowbigint -> (
(* Raises on a negative exponent, so pure only when the exponent is a
nonnegative constant. *)
match args with
| [_; Lconst (Const_bigint (true, _))] -> true
| _ -> false)
| Pcreate_extension _ | Ptypeof | Pis_null | Pis_not_none | Psome
| Psome_not_nest | Pis_undefined | Pis_null_undefined | Pnull_to_opt
| Pnull_undefined_to_opt | Pjs_object_create _ | Pimport _
Expand All @@ -67,14 +73,13 @@ let rec no_side_effects (lam : Lambda.t) : bool =
| Ppowfloat | Pdivfloat | Pmodfloat | Pfloatcomp _ | Pjscomp _ | Pfloatorder
| Pfloatmin | Pfloatmax
(* bigint primitives *)
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Ppowbigint
| Pnotbigint | Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
| Pnegbigint | Paddbigint | Psubbigint | Pmulbigint | Pnotbigint
| Pandbigint | Porbigint | Pxorbigint | Plslbigint | Pasrbigint
| Pbigintcomp _ | Pbigintorder | Pbigintmin | Pbigintmax
(* string primitives *)
| Pstringlength | Pstringrefu | Pstringrefs | Pstringcomp _ | Pstringorder
| Pstringmin | Pstringmax
| Pstringlength | Pstringcomp _ | Pstringorder | Pstringmin | Pstringmax
(* array primitives *)
| Pmakearray | Parraylength | Parrayrefu | Parrayrefs
| Pmakearray | Parraylength | Parrayrefu
(* list primitives *)
| Pmakelist
(* dict primitives *)
Expand All @@ -99,7 +104,9 @@ let rec no_side_effects (lam : Lambda.t) : bool =
(* TODO *)
| Praw_js_code _
(* byte swap *)
| Parraysets | Parraysetu | Praise | Psetfield _ ->
| Parraysets | Parraysetu | Praise | Psetfield _
(* bounds-checked reads throw when the index is out of range *)
| Parrayrefs | Pstringrefs | Pstringrefu ->
false)
| Llet (_, _, arg, body) -> no_side_effects arg && no_side_effects body
| Lswitch (_, _) -> false
Expand Down
4 changes: 2 additions & 2 deletions compiler/core/lam_compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1283,7 +1283,7 @@ let compile output_prefix =
(lambda_cxt : Lam_compile_context.t) =
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
let emitted_id =
if Set_ident.mem (Lambda.free_variables body) id then id
if Set_ident.mem (Lambda_traverse.free_variables body) id then id
else Ext_ident.create_tmp ~name:"_for_of" ()
in
let block =
Expand All @@ -1306,7 +1306,7 @@ let compile output_prefix =
(body : Lambda.t) (lambda_cxt : Lam_compile_context.t) =
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
let emitted_id =
if Set_ident.mem (Lambda.free_variables body) id then id
if Set_ident.mem (Lambda_traverse.free_variables body) id then id
else Ext_ident.create_tmp ~name:"_for_await_of" ()
in
let block =
Expand Down
2 changes: 1 addition & 1 deletion compiler/core/lam_compile_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ let required_modules (lam : Lambda.t) : Lam_module_ident.Hash_set.t =
| Lglobal_module id ->
Lam_module_ident.Hash_set.add required (Lam_module_ident.of_ml id)
| _ -> ());
Lambda.iter collect lam
Lambda_traverse.iter collect lam
in
collect lam;
required
Expand Down
9 changes: 5 additions & 4 deletions compiler/core/lam_dce.ml
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,23 @@ let remove export_idents (rest : Lam_group.t list) : Lam_group.t list =
Ext_list.fold_left rest export_idents (fun acc x ->
match x with
| Single (kind, id, lam) -> (
Hash_ident.add ident_free_vars id (Lambda.free_variables lam);
Hash_ident.add ident_free_vars id (Lambda_traverse.free_variables lam);
match kind with
| Alias | StrictOpt -> acc
| Strict | Variable -> id :: acc)
| Recursive bindings ->
Ext_list.fold_left bindings acc (fun acc (id, lam) ->
Hash_ident.add ident_free_vars id (Lambda.free_variables lam);
Hash_ident.add ident_free_vars id
(Lambda_traverse.free_variables lam);
match lam with
| Lfunction _ -> acc
| _ -> id :: acc)
| Nop lam ->
if Lam_analysis.no_side_effects lam then acc
else
(* its free varaibles here will be defined above *)
Set_ident.fold (Lambda.free_variables lam) acc (fun x acc ->
x :: acc))
Set_ident.fold (Lambda_traverse.free_variables lam) acc
(fun x acc -> x :: acc))
in
let visited = transitive_closure initial_idents ident_free_vars in
Ext_list.fold_left rest [] (fun acc x ->
Expand Down
4 changes: 2 additions & 2 deletions compiler/core/lam_exit_code.ml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ let has_exit_code lam exits =
| Lfunction _ -> false
(* static exit can not cross function boundary *)
| Lstaticraise (p, _) when exits p -> true
| _ -> Lambda.shallow_exists aux lam
| _ -> Lambda_traverse.shallow_exists aux lam
in
aux lam

let rec has_exit (lam : Lambda.t) =
match lam with
| Lfunction _ -> false
| Lstaticraise (_, _) -> true
| _ -> Lambda.shallow_exists has_exit lam
| _ -> Lambda_traverse.shallow_exists has_exit lam
2 changes: 1 addition & 1 deletion compiler/core/lam_pass_exits.ml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ let subst_helper (subst : subst_tbl) (query : int -> int) (lam : Lambda.t) :
Ext_list.fold_right2 xs ys Ident.empty (fun x y t ->
Ident.add x (Lambda.var y) t)
in
Ext_list.fold_right2 ys ls (Lambda.subst_lambda env handler)
Ext_list.fold_right2 ys ls (Lambda_traverse.subst_lambda env handler)
(fun y l r -> Lambda.let_ Strict y l r)
| None -> Lambda.staticraise i ls)
| Lvar _ | Lconst _ -> lam
Expand Down
4 changes: 2 additions & 2 deletions compiler/core/lam_pass_guard_raises.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ let rec guard_raises (lam : Lambda.t) : Lambda.t =
| Lifthenelse (a, (Lprim {primitive = Praise} as b), c) -> (
match c with
(* A constant alternative is already as flat as it gets. *)
| Lconst _ -> Lambda.shallow_map_sharing guard_raises lam
| Lconst _ -> Lambda_traverse.shallow_map_sharing guard_raises lam
| _ ->
Lambda.seq
(Lambda.if_ (guard_raises a) b Lambda.lambda_unit)
(guard_raises c))
| _ -> Lambda.shallow_map_sharing guard_raises lam
| _ -> Lambda_traverse.shallow_map_sharing guard_raises lam
8 changes: 5 additions & 3 deletions compiler/core/lam_pass_sroa.ml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ let rec analyze block uses (lam : Lambda.t) =
else false
| _ ->
not
(Lambda.shallow_exists (fun child -> not (analyze block uses child)) lam)
(Lambda_traverse.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
Expand All @@ -68,7 +70,7 @@ let rec rewrite block fields uses (lam : Lambda.t) =
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
| _ -> Lambda_traverse.shallow_map_sharing (rewrite block fields uses) lam

let fields_for_block block info field_count =
let fallback () =
Expand Down Expand Up @@ -141,4 +143,4 @@ let rec simplify (lam : Lambda.t) =
| _ ->
if init' == init && body' == body then lam
else Lambda.let_ kind block init' body')
| _ -> Lambda.shallow_map_sharing simplify lam
| _ -> Lambda_traverse.shallow_map_sharing simplify lam
2 changes: 1 addition & 1 deletion compiler/core/polyvar_pattern_match.ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ let convert (xs : input) : output =
let os : value list ref = ref [] in
xs
|> List.iteri (fun i (hash, (name, act)) ->
match Lambda.make_key act with
match Lambda_traverse.make_key act with
| None -> os := {stamp = i; hash_names_act = ([(hash, name)], act)} :: !os
| Some key ->
Coll.add_or_update coll key
Expand Down
Loading
Loading