Skip to content

Commit 8180bde

Browse files
cristianocclaude
andcommitted
Route mk_builtin and apply through the folding constructors
lambda.mli says a term can only be built through the constructors, seven of which normalize as they build. That held for every client and not for the module itself: mk_builtin and apply constructed Lprim directly, which they could do only because they own the type. mk_builtin is the path every builtin operator takes, so nothing folded at production. [1. < (1. < 1. ? 1. : 10.)] left translation with both float comparisons intact; it now folds to a constant. apply's eta reduction substituted the arguments into the primitive call and left the result unfolded, so [((a, b) => a + b)(1, 2)] became [(+ 1 2)] rather than [3]. apply was defined above prim and could not call it; nothing between them refers to apply, so it moves below. The effect was invisible because the optimizer passes rebuild every node through the constructors, and were folding this incidentally on the way past. It showed up while measuring what a pass allocates when it changes nothing: converting one pass to a sharing traversal made its output four nodes larger on ppx_apply_test, which is the beta reduction above going unfolded. Generated JavaScript is unchanged - the folding happened either way, just later and repeatedly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8g8qwBARAcvW9MyuKQq8H
1 parent afd4041 commit 8180bde

1 file changed

Lines changed: 58 additions & 57 deletions

File tree

compiler/ml/lambda.ml

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -766,61 +766,6 @@ let rec is_eta_conversion_exn params inner_args outer_args : t list =
766766
| [], [], [] -> []
767767
| _, _, _ -> raise_notrace Not_simple_form
768768

769-
let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
770-
match fn with
771-
| Lfunction
772-
{
773-
params;
774-
body =
775-
Lprim
776-
{
777-
primitive =
778-
( Pnull_to_opt | Pnull_undefined_to_opt | Pis_null
779-
| Pis_null_undefined | Ptypeof ) as wrap;
780-
args =
781-
[Lprim ({primitive = _; args = inner_args} as primitive_call)];
782-
};
783-
} -> (
784-
match is_eta_conversion_exn params inner_args args with
785-
| args ->
786-
let loc = ap_info.ap_loc in
787-
Lprim
788-
{primitive = wrap; args = [Lprim {primitive_call with args; loc}]; loc}
789-
| exception Not_simple_form ->
790-
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
791-
| Lfunction
792-
{
793-
params;
794-
body = Lprim ({primitive = _; args = inner_args} as primitive_call);
795-
} -> (
796-
match is_eta_conversion_exn params inner_args args with
797-
| args -> Lprim {primitive_call with args; loc = ap_info.ap_loc}
798-
| exception _ ->
799-
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
800-
| Lfunction
801-
{
802-
params;
803-
body =
804-
Lsequence
805-
( Lprim ({primitive = _; args = inner_args} as primitive_call),
806-
(Lconst _ as const) );
807-
} -> (
808-
match is_eta_conversion_exn params inner_args args with
809-
| args ->
810-
Lsequence (Lprim {primitive_call with args; loc = ap_info.ap_loc}, const)
811-
| exception _ ->
812-
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
813-
(* | Lfunction {params;body} when Ext_list.same_length params args ->
814-
Ext_list.fold_right2 (fun p arg acc ->
815-
Llet(Strict,p,arg,acc)
816-
) params args body *)
817-
(* TODO: more rigirous analysis on [let_kind] *))
818-
| Llet (kind, id, e, (Lfunction _ as fn)) ->
819-
Llet (kind, id, e, apply fn args ap_info ~ap_transformed_jsx)
820-
(* | Llet (kind0, id0, e0, Llet (kind,id, e, (Lfunction _ as fn))) ->
821-
Llet(kind0,id0,e0,Llet (kind, id, e, apply fn args loc status)) *)
822-
| _ -> Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
823-
824769
let rec eq_approx (l1 : t) (l2 : t) =
825770
match l1 with
826771
| Lglobal_module i1 -> (
@@ -1082,6 +1027,62 @@ let prim ~primitive:(prim : primitive) ~args loc : t =
10821027
*)
10831028
| _ -> default ())
10841029

1030+
let rec apply ?(ap_transformed_jsx = false) fn args (ap_info : ap_info) : t =
1031+
match fn with
1032+
| Lfunction
1033+
{
1034+
params;
1035+
body =
1036+
Lprim
1037+
{
1038+
primitive =
1039+
( Pnull_to_opt | Pnull_undefined_to_opt | Pis_null
1040+
| Pis_null_undefined | Ptypeof ) as wrap;
1041+
args =
1042+
[Lprim ({primitive = _; args = inner_args} as primitive_call)];
1043+
};
1044+
} -> (
1045+
match is_eta_conversion_exn params inner_args args with
1046+
| args ->
1047+
let loc = ap_info.ap_loc in
1048+
prim ~primitive:wrap
1049+
~args:[prim ~primitive:primitive_call.primitive ~args loc]
1050+
loc
1051+
| exception Not_simple_form ->
1052+
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
1053+
| Lfunction
1054+
{
1055+
params;
1056+
body = Lprim ({primitive = _; args = inner_args} as primitive_call);
1057+
} -> (
1058+
match is_eta_conversion_exn params inner_args args with
1059+
| args -> prim ~primitive:primitive_call.primitive ~args ap_info.ap_loc
1060+
| exception _ ->
1061+
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx})
1062+
| Lfunction
1063+
{
1064+
params;
1065+
body =
1066+
Lsequence
1067+
( Lprim ({primitive = _; args = inner_args} as primitive_call),
1068+
(Lconst _ as const) );
1069+
} -> (
1070+
match is_eta_conversion_exn params inner_args args with
1071+
| args ->
1072+
seq (prim ~primitive:primitive_call.primitive ~args ap_info.ap_loc) const
1073+
| exception _ ->
1074+
Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
1075+
(* | Lfunction {params;body} when Ext_list.same_length params args ->
1076+
Ext_list.fold_right2 (fun p arg acc ->
1077+
Llet(Strict,p,arg,acc)
1078+
) params args body *)
1079+
(* TODO: more rigirous analysis on [let_kind] *))
1080+
| Llet (kind, id, e, (Lfunction _ as fn)) ->
1081+
let_ kind id e (apply fn args ap_info ~ap_transformed_jsx)
1082+
(* | Llet (kind0, id0, e0, Llet (kind,id, e, (Lfunction _ as fn))) ->
1083+
Llet(kind0,id0,e0,Llet (kind, id, e, apply fn args loc status)) *)
1084+
| _ -> Lapply {ap_func = fn; ap_args = args; ap_info; ap_transformed_jsx}
1085+
10851086
let not_ loc x : t =
10861087
match x with
10871088
| Lprim ({primitive = Pintcomp Cneq} as prim) ->
@@ -1265,7 +1266,7 @@ let sequand l r = if_ l r lambda_false
12651266

12661267
let mk_builtin b args loc =
12671268
match b with
1268-
| Primitive p -> Lprim {primitive = p; args; loc}
1269+
| Primitive p -> prim ~primitive:p ~args loc
12691270
| Constant c -> (
12701271
match args with
12711272
| [] -> Lconst c
@@ -1280,7 +1281,7 @@ let mk_builtin b args loc =
12801281
| _ -> assert false)
12811282
| Eliminated Ignore -> (
12821283
match args with
1283-
| [arg] -> Lsequence (arg, lambda_unit)
1284+
| [arg] -> seq arg lambda_unit
12841285
| _ -> assert false)
12851286

12861287
let default_function_attribute =

0 commit comments

Comments
 (0)