Skip to content

Commit 4983f19

Browse files
cristianoccknitt
authored andcommitted
Refine Stdlib.Option metadata handling
1 parent da6509e commit 4983f19

File tree

12 files changed

+153
-171
lines changed

12 files changed

+153
-171
lines changed

analysis/reanalyze/src/Arnold.ml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ module FindFunctionsCalled = struct
534534
let super = Tast_mapper.default in
535535
let expr (self : Tast_mapper.mapper) (e : Typedtree.expression) =
536536
(match e.exp_desc with
537-
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)}} ->
537+
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)}; _} ->
538538
let functionName = Path.name callee in
539539
callees := !callees |> StringSet.add functionName
540540
| _ -> ());
@@ -579,7 +579,8 @@ module ExtendFunctionTable = struct
579579
| Some
580580
{
581581
exp_desc =
582-
Texp_apply {funct = {exp_desc = Texp_ident (path, {loc}, _)}; args};
582+
Texp_apply
583+
{funct = {exp_desc = Texp_ident (path, {loc}, _)}; args; _};
583584
}
584585
when kindOpt <> None ->
585586
let checkArg ((argLabel : Asttypes.Noloc.arg_label), _argOpt) =
@@ -620,7 +621,7 @@ module ExtendFunctionTable = struct
620621
calls a progress function"
621622
functionName printPos id_pos;
622623
})))
623-
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)}; args}
624+
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)}; args; _}
624625
when callee |> FunctionTable.isInFunctionInTable ~functionTable ->
625626
let functionName = Path.name callee in
626627
args
@@ -668,8 +669,8 @@ module CheckExpressionWellFormed = struct
668669
| Texp_ident (path, {loc}, _) ->
669670
checkIdent ~path ~loc;
670671
e
671-
| Texp_apply {funct = {exp_desc = Texp_ident (functionPath, _, _)}; args}
672-
->
672+
| Texp_apply
673+
{funct = {exp_desc = Texp_ident (functionPath, _, _)}; args; _} ->
673674
let functionName = Path.name functionPath in
674675
args
675676
|> List.iter (fun ((argLabel : Asttypes.Noloc.arg_label), argOpt) ->
@@ -850,7 +851,7 @@ module Compile = struct
850851
and create a function call with the appropriate arguments *)
851852
assert false
852853
| None -> expr |> expression ~ctx |> evalArgs ~args ~ctx)
853-
| Texp_apply {funct = expr; args} ->
854+
| Texp_apply {funct = expr; args; _} ->
854855
expr |> expression ~ctx |> evalArgs ~args ~ctx
855856
| Texp_let
856857
( Recursive,

analysis/reanalyze/src/Exception.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@ let traverseAst () =
304304
let exceptions = [arg] |> raiseArgs in
305305
currentEvents := {Event.exceptions; loc; kind = Raises} :: !currentEvents;
306306
arg |> snd |> iterExprOpt self
307-
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)} as e; args} ->
307+
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)} as e; args; _}
308+
->
308309
let calleeName = Path.name callee in
309310
if calleeName |> isRaise then
310311
let exceptions = args |> raiseArgs in

analysis/reanalyze/src/SideEffects.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ let rec exprNoSideEffects (expr : Typedtree.expression) =
2626
| Texp_ident _ | Texp_constant _ -> true
2727
| Texp_construct (_, _, el) -> el |> List.for_all exprNoSideEffects
2828
| Texp_function _ -> true
29-
| Texp_apply {funct = {exp_desc = Texp_ident (path, _, _)}; args}
29+
| Texp_apply {funct = {exp_desc = Texp_ident (path, _, _)}; args; _}
3030
when path |> pathIsWhitelistedForSideEffects ->
3131
args |> List.for_all (fun (_, eo) -> eo |> exprOptNoSideEffects)
3232
| Texp_apply _ -> false

compiler/ml/printtyped.ml

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -260,16 +260,6 @@ and expression_extra i ppf x attrs =
260260
| Texp_newtype s ->
261261
line i ppf "Texp_newtype \"%s\"\n" s;
262262
attributes i ppf attrs
263-
| Texp_stdlib_option_call {call_kind; payload_not_nested; _} ->
264-
let kind =
265-
match call_kind with
266-
| Stdlib_option_forEach -> "forEach"
267-
| Stdlib_option_map {result_cannot_contain_undefined = _} -> "map"
268-
| Stdlib_option_flatMap -> "flatMap"
269-
in
270-
line i ppf "Texp_stdlib_option_call %s payload_not_nested=%b\n" kind
271-
payload_not_nested;
272-
attributes i ppf attrs
273263

274264
and expression i ppf x =
275265
line i ppf "expression %a\n" fmt_location x.exp_loc;
@@ -298,11 +288,12 @@ and expression i ppf x =
298288
line i ppf "%a" Ident.print param;
299289
arg_label i ppf p;
300290
case i ppf case_
301-
| Texp_apply {funct = e; args = l; partial} ->
291+
| Texp_apply {funct = e; args = l; partial; stdlib_option_call; _} ->
302292
if partial then line i ppf "partial\n";
303293
line i ppf "Texp_apply\n";
304294
expression i ppf e;
305-
list i label_x_expression ppf l
295+
list i label_x_expression ppf l;
296+
if stdlib_option_call then line i ppf "stdlib_option_call\n" else ()
306297
| Texp_match (e, l1, l2, _partial) ->
307298
line i ppf "Texp_match\n";
308299
expression i ppf e;

compiler/ml/rec_check.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ let rec classify_expression : Typedtree.expression -> sd =
199199
| Texp_while _ | Texp_pack _ | Texp_function _ | Texp_extension_constructor _
200200
->
201201
Static
202-
| Texp_apply {funct = {exp_desc = Texp_ident (_, _, vd)}} when is_ref vd ->
202+
| Texp_apply {funct = {exp_desc = Texp_ident (_, _, vd)}; _} when is_ref vd ->
203203
Static
204204
| Texp_apply _ | Texp_match _ | Texp_ifthenelse _ | Texp_send _ | Texp_field _
205205
| Texp_assert _ | Texp_try _ ->
@@ -233,10 +233,10 @@ let rec expression : Env.env -> Typedtree.expression -> Use.t =
233233
(discard (expression env e3)))
234234
| Texp_constant _ -> Use.empty
235235
| Texp_apply
236-
{funct = {exp_desc = Texp_ident (_, _, vd)}; args = [(_, Some arg)]}
236+
{funct = {exp_desc = Texp_ident (_, _, vd)}; args = [(_, Some arg)]; _}
237237
when is_ref vd ->
238238
Use.guard (expression env arg)
239-
| Texp_apply {funct = e; args} ->
239+
| Texp_apply {funct = e; args; _} ->
240240
let arg env (_, eo) = option expression env eo in
241241
Use.(join (inspect (expression env e)) (inspect (list arg env args)))
242242
| Texp_tuple exprs -> Use.guard (list expression env exprs)

compiler/ml/tast_iterator.ml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,11 @@ let pat sub {pat_extra; pat_desc; pat_env; _} =
137137
| Tpat_alias (p, _, _) -> sub.pat sub p
138138

139139
let expr sub {exp_extra; exp_desc; exp_env; _} =
140-
let rec extra = function
140+
let extra = function
141141
| Texp_constraint cty -> sub.typ sub cty
142142
| Texp_coerce cty2 -> sub.typ sub cty2
143143
| Texp_newtype _ -> ()
144144
| Texp_open (_, _, _, _) -> ()
145-
| Texp_stdlib_option_call info -> stdlib_option_call info
146-
and stdlib_option_call {callback; _} = stdlib_option_callback callback
147-
and stdlib_option_callback = function
148-
| Stdlib_option_inline_lambda {body; _} -> sub.expr sub body
149-
| Stdlib_option_inline_ident expr -> sub.expr sub expr
150145
in
151146
List.iter (fun (e, _, _) -> extra e) exp_extra;
152147
sub.env sub exp_env;
@@ -157,7 +152,7 @@ let expr sub {exp_extra; exp_desc; exp_env; _} =
157152
sub.value_bindings sub (rec_flag, list);
158153
sub.expr sub exp
159154
| Texp_function {case; _} -> sub.case sub case
160-
| Texp_apply {funct = exp; args = list} ->
155+
| Texp_apply {funct = exp; args = list; _} ->
161156
sub.expr sub exp;
162157
List.iter (fun (_, o) -> Option.iter (sub.expr sub) o) list
163158
| Texp_match (exp, list1, list2, _) ->

compiler/ml/tast_mapper.ml

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -180,27 +180,12 @@ let pat sub x =
180180
{x with pat_extra; pat_desc; pat_env}
181181

182182
let expr sub x =
183-
let map_stdlib_option_callback sub = function
184-
| Stdlib_option_inline_lambda {param; body} ->
185-
Stdlib_option_inline_lambda {param; body = sub.expr sub body}
186-
| Stdlib_option_inline_ident expr ->
187-
Stdlib_option_inline_ident (sub.expr sub expr)
188-
in
189-
let map_stdlib_option_call sub {callback; call_kind; payload_not_nested} =
190-
{
191-
callback = map_stdlib_option_callback sub callback;
192-
call_kind;
193-
payload_not_nested;
194-
}
195-
in
196183
let extra = function
197184
| Texp_constraint cty -> Texp_constraint (sub.typ sub cty)
198185
| Texp_coerce cty2 -> Texp_coerce (sub.typ sub cty2)
199186
| Texp_open (ovf, path, loc, env) ->
200187
Texp_open (ovf, path, loc, sub.env sub env)
201188
| Texp_newtype _ as d -> d
202-
| Texp_stdlib_option_call info ->
203-
Texp_stdlib_option_call (map_stdlib_option_call sub info)
204189
in
205190
let exp_extra = List.map (tuple3 extra id id) x.exp_extra in
206191
let exp_env = sub.env sub x.exp_env in
@@ -213,11 +198,14 @@ let expr sub x =
213198
| Texp_function {arg_label; arity; param; case; partial; async} ->
214199
Texp_function
215200
{arg_label; arity; param; case = sub.case sub case; partial; async}
216-
| Texp_apply {funct = exp; args = list; partial; transformed_jsx} ->
201+
| Texp_apply
202+
{funct = exp; args = list; partial; transformed_jsx; stdlib_option_call}
203+
->
217204
Texp_apply
218205
{
219206
funct = sub.expr sub exp;
220207
args = List.map (tuple2 id (opt (sub.expr sub))) list;
208+
stdlib_option_call;
221209
partial;
222210
transformed_jsx;
223211
}

compiler/ml/translcore.ml

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -646,13 +646,111 @@ let rec cut n l =
646646

647647
let try_ids = Hashtbl.create 8
648648

649-
let stdlib_option_call_extra exp =
650-
let rec aux = function
651-
| [] -> None
652-
| (Texp_stdlib_option_call info, _, _) :: _ -> Some info
653-
| _ :: rest -> aux rest
654-
in
655-
aux exp.exp_extra
649+
(* Recompute metadata needed for inlining Stdlib.Option helpers at translation
650+
time; the typed tree only marks such applications with a boolean flag. *)
651+
type stdlib_option_call_kind =
652+
| Stdlib_option_forEach
653+
| Stdlib_option_map of {result_cannot_contain_undefined: bool}
654+
| Stdlib_option_flatMap
655+
656+
type stdlib_option_callback =
657+
| Stdlib_option_inline_lambda of {param: Ident.t; body: expression}
658+
| Stdlib_option_inline_ident of expression
659+
660+
type stdlib_option_call = {
661+
callback: stdlib_option_callback;
662+
call_kind: stdlib_option_call_kind;
663+
payload_not_nested: bool;
664+
}
665+
666+
type stdlib_option_fun_kind =
667+
| Stdlib_option_fun_forEach
668+
| Stdlib_option_fun_map
669+
| Stdlib_option_fun_flatMap
670+
671+
let stdlib_option_fun_of_path env path =
672+
let canonical = Env.normalize_path_prefix None env path in
673+
match Path.flatten canonical with
674+
| `Contains_apply -> None
675+
| `Ok (head, rest) ->
676+
let segments = Ident.name head :: rest in
677+
let matches fname =
678+
match segments with
679+
| ["Stdlib"; "Option"; name] -> String.equal name fname
680+
| ["Stdlib_Option"; name] -> String.equal name fname
681+
| _ -> false
682+
in
683+
if matches "forEach" then Some Stdlib_option_fun_forEach
684+
else if matches "map" then Some Stdlib_option_fun_map
685+
else if matches "flatMap" then Some Stdlib_option_fun_flatMap
686+
else None
687+
688+
let inline_lambda_callback (expr : expression) : stdlib_option_callback option =
689+
match expr.exp_desc with
690+
| Texp_function {arg_label = Nolabel; case; partial = Total; async = false; _}
691+
when Option.is_none case.c_guard -> (
692+
match case.c_lhs.pat_desc with
693+
| Tpat_var (param, _) ->
694+
Some (Stdlib_option_inline_lambda {param; body = case.c_rhs})
695+
| _ -> None)
696+
| _ -> None
697+
698+
let inline_ident_callback (expr : expression) : stdlib_option_callback option =
699+
match expr.exp_desc with
700+
| Texp_ident _ -> Some (Stdlib_option_inline_ident expr)
701+
| _ -> None
702+
703+
let callback_return_type env (expr : expression) =
704+
match (Ctype.expand_head env expr.exp_type).desc with
705+
| Tarrow (_, ret_ty, _, _) -> Some ret_ty
706+
| _ -> None
707+
708+
let detect_stdlib_option_call env (funct : expression)
709+
(args : (Noloc.arg_label * expression option) list) :
710+
stdlib_option_call option =
711+
match funct.exp_desc with
712+
| Texp_ident (path, _, _) -> (
713+
match stdlib_option_fun_of_path env path with
714+
| None -> None
715+
| Some fun_kind -> (
716+
match args with
717+
| [(Nolabel, Some opt_expr); (Nolabel, Some callback_expr)] -> (
718+
let callback_info =
719+
match inline_lambda_callback callback_expr with
720+
| Some info -> Some info
721+
| None -> inline_ident_callback callback_expr
722+
in
723+
match callback_info with
724+
| None -> None
725+
| Some callback ->
726+
let payload_not_nested =
727+
match (Ctype.expand_head env opt_expr.exp_type).desc with
728+
| Tconstr (path, [payload_ty], _)
729+
when Path.same path Predef.path_option ->
730+
Typeopt.type_cannot_contain_undefined payload_ty env
731+
| _ -> false
732+
in
733+
let call_kind =
734+
match fun_kind with
735+
| Stdlib_option_fun_forEach -> Stdlib_option_forEach
736+
| Stdlib_option_fun_map ->
737+
let result_cannot_contain_undefined =
738+
match callback with
739+
| Stdlib_option_inline_lambda {body; _} ->
740+
Typeopt.type_cannot_contain_undefined body.exp_type
741+
body.exp_env
742+
| Stdlib_option_inline_ident cb -> (
743+
match callback_return_type cb.exp_env cb with
744+
| Some ret_ty ->
745+
Typeopt.type_cannot_contain_undefined ret_ty cb.exp_env
746+
| None -> false)
747+
in
748+
Stdlib_option_map {result_cannot_contain_undefined}
749+
| Stdlib_option_fun_flatMap -> Stdlib_option_flatMap
750+
in
751+
Some {callback; call_kind; payload_not_nested})
752+
| _ -> None))
753+
| _ -> None
656754

657755
let lambda_none = Lconst (Const_pointer (0, Pt_shape_none))
658756

@@ -765,11 +863,15 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
765863
(Lprim
766864
(Pccall (set_transformed_jsx d ~transformed_jsx), argl, e.exp_loc))
767865
| _ -> wrap (Lprim (prim, argl, e.exp_loc))))
768-
| Texp_apply {funct; args = oargs; partial; transformed_jsx} -> (
866+
| Texp_apply
867+
{funct; args = oargs; partial; transformed_jsx; stdlib_option_call} -> (
769868
let inlined, funct =
770869
Translattribute.get_and_remove_inlined_attribute funct
771870
in
772-
let option_call_info = stdlib_option_call_extra e in
871+
let option_call_info =
872+
if stdlib_option_call then detect_stdlib_option_call e.exp_env funct oargs
873+
else None
874+
in
773875
let uncurried_partial_application =
774876
(* In case of partial application foo(args, ...) when some args are missing,
775877
get the arity *)

0 commit comments

Comments
 (0)