Skip to content

Commit ec88aa5

Browse files
committed
Refine tail position property
1 parent 89fc213 commit ec88aa5

File tree

8 files changed

+231
-162
lines changed

8 files changed

+231
-162
lines changed

jscomp/core/js_output.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ let output_of_expression
5454
(continuation : continuation)
5555
(exp : J.expression) ~(no_effects: bool Lazy.t) =
5656
match continuation with
57-
| EffectCall ReturnFalse ->
57+
| EffectCall Not_tail ->
5858
if Lazy.force no_effects
5959
then dummy
6060
else {block = []; value = Some exp ; output_finished = False}
6161
| Declare (kind, n)->
6262
make [ S.define_variable ~kind n exp]
6363
| Assign n ->
6464
make [S.assign n exp ]
65-
| EffectCall (ReturnTrue _) ->
65+
| EffectCall (Maybe_tail _) ->
6666
make [S.return_stmt exp] ~output_finished:True
6767
| NeedValue _ ->
6868
{block = []; value = Some exp; output_finished = False }
@@ -72,8 +72,8 @@ let output_of_block_and_expression
7272
(continuation : continuation)
7373
(block : J.block) exp : t =
7474
match continuation with
75-
| EffectCall ReturnFalse -> make block ~value:exp
76-
| EffectCall (ReturnTrue _) ->
75+
| EffectCall Not_tail -> make block ~value:exp
76+
| EffectCall (Maybe_tail _) ->
7777
make (Ext_list.append_one block (S.return_stmt exp)) ~output_finished:True
7878
| Declare (kind,n) ->
7979
make (Ext_list.append_one block (S.define_variable ~kind n exp))

jscomp/core/lam_compile.ml

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ module S = Js_stmt_make
2828

2929
let method_cache_id = ref 1 (*TODO: move to js runtime for re-entrant *)
3030

31+
let change_return_type_in_try
32+
(x : Lam_compile_context.tail_type)
33+
: Lam_compile_context.tail_type =
34+
match x with
35+
| Maybe_tail (Tail_with_name _ | Tail_no_name_lambda) ->
36+
Maybe_tail Tail_in_try
37+
| Not_tail | Maybe_tail Tail_in_try
38+
-> x
3139

3240
(* assume outer is [Lstaticcatch] *)
3341
let rec flat_catches (acc : Lam_compile_context.handler list) (x : Lam.t)
@@ -159,7 +167,7 @@ and compile_external_field_apply
159167
let dummy = [], [] in
160168
if args_lambda = [] then dummy
161169
else
162-
let arg_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
170+
let arg_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
163171
Ext_list.fold_right args_lambda dummy (fun arg_lambda (args_code, args) ->
164172
match compile_lambda arg_cxt arg_lambda with
165173
| {block; value = Some b} ->
@@ -253,7 +261,7 @@ and compile_recursive_let ~all_bindings
253261
let output =
254262
compile_lambda
255263
{ cxt with
256-
continuation = EffectCall (ReturnTrue (Some ret ));
264+
continuation = EffectCall (Maybe_tail (Tail_with_name ret ));
257265
jmp_table = Lam_compile_context.empty_handler_map} body in
258266
let result =
259267
if ret.triggered then
@@ -317,7 +325,7 @@ and compile_recursive_let ~all_bindings
317325
*)
318326
(* Ext_log.err "@[recursive value %s/%d@]@." id.name id.stamp; *)
319327
begin
320-
match compile_lambda {cxt with continuation = NeedValue ReturnFalse } arg with
328+
match compile_lambda {cxt with continuation = NeedValue Not_tail } arg with
321329
| { block = b; value = Some v} ->
322330
(* TODO: check recursive value ..
323331
could be improved for simple cases
@@ -534,7 +542,7 @@ and compile_switch switch_arg sw (lambda_cxt : Lam_compile_context.t) =
534542
else Default x in
535543
let compile_whole (cxt : Lam_compile_context.t ) =
536544
match compile_lambda
537-
{cxt with continuation = NeedValue ReturnFalse}
545+
{cxt with continuation = NeedValue Not_tail}
538546
switch_arg
539547
with
540548
| {value = None; _} -> assert false
@@ -598,7 +606,7 @@ and compile_stringswitch l cases default (lambda_cxt : Lam_compile_context.t) =
598606
*)
599607
match
600608
compile_lambda
601-
{lambda_cxt with continuation = NeedValue ReturnFalse } l
609+
{lambda_cxt with continuation = NeedValue Not_tail } l
602610
with
603611
| {value = None } -> assert false
604612
| {block ; value = Some e} ->
@@ -731,7 +739,7 @@ and compile_sequand
731739
if Lam_compile_context.continuation_is_return lambda_cxt.continuation then
732740
compile_lambda lambda_cxt (Lam.sequand l r )
733741
else
734-
let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
742+
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
735743
match compile_lambda new_cxt l with
736744
| { value = None } -> assert false
737745
| {block = l_block; value = Some l_expr} ->
@@ -780,7 +788,7 @@ and compile_sequor
780788
if Lam_compile_context.continuation_is_return lambda_cxt.continuation then
781789
compile_lambda lambda_cxt (Lam.sequor l r)
782790
else
783-
let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
791+
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
784792
match compile_lambda new_cxt l with
785793
| {value = None } -> assert false
786794
| {block = l_block; value = Some l_expr} ->
@@ -830,7 +838,7 @@ and compile_sequor
830838
(Sine OCaml expression can be really complex..)
831839
*)
832840
and compile_while (predicate : Lam.t) (body : Lam.t) (lambda_cxt : Lam_compile_context.t) =
833-
match compile_lambda {lambda_cxt with continuation = NeedValue ReturnFalse } predicate
841+
match compile_lambda {lambda_cxt with continuation = NeedValue Not_tail } predicate
834842
with
835843
| { value = None} -> assert false
836844
| { block; value = Some e} ->
@@ -845,7 +853,7 @@ and compile_while (predicate : Lam.t) (body : Lam.t) (lambda_cxt : Lam_compile_c
845853
e
846854
(Js_output.output_as_block @@
847855
compile_lambda
848-
{lambda_cxt with continuation = EffectCall ReturnFalse}
856+
{lambda_cxt with continuation = EffectCall Not_tail}
849857
body)
850858
] in
851859
Js_output.output_of_block_and_expression lambda_cxt.continuation block E.unit
@@ -864,7 +872,7 @@ and compile_while (predicate : Lam.t) (body : Lam.t) (lambda_cxt : Lam_compile_c
864872

865873
and compile_for
866874
id start finish direction body (lambda_cxt : Lam_compile_context.t) =
867-
let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
875+
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
868876
let block =
869877
match compile_lambda new_cxt start,
870878
compile_lambda new_cxt finish with
@@ -883,7 +891,7 @@ and compile_for
883891
*)
884892
let block_body =
885893
Js_output.output_as_block
886-
(compile_lambda {lambda_cxt with continuation = EffectCall ReturnFalse}
894+
(compile_lambda {lambda_cxt with continuation = EffectCall Not_tail}
887895
body) in
888896
match b1,b2 with
889897
| _,[] ->
@@ -916,7 +924,7 @@ and compile_assign id (lambda : Lam.t) (lambda_cxt : Lam_compile_context.t) =
916924
]
917925
| _ ->
918926
match compile_lambda
919-
{lambda_cxt with continuation = NeedValue ReturnFalse}
927+
{lambda_cxt with continuation = NeedValue Not_tail}
920928
lambda
921929
with
922930
| {value = None} -> assert false
@@ -959,10 +967,14 @@ and compile_trywith lam id catch (lambda_cxt : Lam_compile_context.t) =
959967
let context = {lambda_cxt with continuation = Assign v} in
960968
Js_output.make (S.declare_variable ~kind:Variable v ::
961969
aux context context) ~value:(E.var v )
962-
| EffectCall (ReturnTrue (Some _)) ->
963-
Js_output.make (aux lambda_cxt {lambda_cxt with continuation = EffectCall (ReturnTrue None)} )
964-
| EffectCall _ ->
965-
Js_output.make (aux lambda_cxt lambda_cxt)
970+
| EffectCall return_type ->
971+
let new_return_type = change_return_type_in_try return_type in
972+
if new_return_type == return_type then
973+
Js_output.make (aux lambda_cxt lambda_cxt)
974+
else
975+
Js_output.make (aux lambda_cxt {lambda_cxt with continuation = EffectCall new_return_type} )
976+
977+
966978

967979
(* Note that in [Texp_apply] for [%sendcache] the cache might not be used
968980
see {!CamlinternalOO.send_meth} and {!Translcore.transl_exp0} the branch
@@ -1012,7 +1024,7 @@ and compile_send (meth_kind : Lam_compat.meth_kind)
10121024
(met : Lam.t)
10131025
(obj : Lam.t) (args : Lam.t list) loc
10141026
(lambda_cxt : Lam_compile_context.t) =
1015-
let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
1027+
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
10161028
match Ext_list.split_map (met :: obj :: args) (fun x ->
10171029
match x with
10181030
| Lprim {primitive = Pccall {prim_name ; _}; args = []}
@@ -1079,7 +1091,7 @@ and compile_ifthenelse
10791091
(t_branch : Lam.t)
10801092
(f_branch : Lam.t)
10811093
(lambda_cxt : Lam_compile_context.t) =
1082-
match compile_lambda {lambda_cxt with continuation = NeedValue ReturnFalse } predicate with
1094+
match compile_lambda {lambda_cxt with continuation = NeedValue Not_tail } predicate with
10831095
| {value = None } -> assert false
10841096
| {block = b; value = Some e} ->
10851097
match lambda_cxt.continuation with
@@ -1108,7 +1120,7 @@ and compile_ifthenelse
11081120
~value:(E.var id)
11091121
)
11101122
| Declare (kind,id) ->
1111-
let declare_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
1123+
let declare_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
11121124
(match
11131125
compile_lambda declare_cxt t_branch,
11141126
compile_lambda declare_cxt f_branch with
@@ -1140,7 +1152,7 @@ and compile_ifthenelse
11401152
compile_lambda context1 f_branch with
11411153

11421154
(* see PR#83 *)
1143-
| ReturnFalse , {block = []; value = Some out1},
1155+
| Not_tail , {block = []; value = Some out1},
11441156
{block = []; value = Some out2} ->
11451157
(match Js_exp_make.remove_pure_sub_exp out1 ,
11461158
Js_exp_make.remove_pure_sub_exp out2 with
@@ -1152,7 +1164,7 @@ and compile_ifthenelse
11521164
Js_output.make (Ext_list.append_one b (S.if_ e [S.exp out1]))
11531165
| None, Some out2 ->
11541166
Js_output.make (Ext_list.append_one b (S.if_ (E.not e) [S.exp out2])))
1155-
| ReturnFalse , {block = []; value = Some out1}, _ ->
1167+
| Not_tail , {block = []; value = Some out1}, _ ->
11561168
(* assert branch
11571169
TODO: here we re-compile two branches since
11581170
its context is different -- could be improved
@@ -1171,7 +1183,7 @@ and compile_ifthenelse
11711183
(compile_lambda lambda_cxt f_branch))]
11721184
)
11731185

1174-
| ReturnFalse , _, {block = []; value = Some out2} ->
1186+
| Not_tail , _, {block = []; value = Some out2} ->
11751187
let else_ =
11761188
if Js_analyzer.no_side_effect_expression out2 then
11771189
None
@@ -1184,7 +1196,7 @@ and compile_ifthenelse
11841196
(Js_output.output_as_block (
11851197
compile_lambda lambda_cxt t_branch))
11861198
?else_))
1187-
| ReturnTrue _, {block = []; value = Some out1},
1199+
| Maybe_tail _, {block = []; value = Some out1},
11881200
{block = []; value = Some out2} ->
11891201
Js_output.make
11901202
(Ext_list.append_one b (S.return_stmt (E.econd e out1 out2)))
@@ -1228,7 +1240,7 @@ and compile_apply
12281240
1. check arity, can be simplified for pure expression
12291241
2. no need create names
12301242
*)
1231-
let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
1243+
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
12321244
let [@warning "-8" (* non-exhaustive pattern*)] (args_code, fn_code:: args) =
12331245
Ext_list.fold_right (fn::args_lambda) ([],[]) (fun x (args_code, fn_code )->
12341246
match compile_lambda new_cxt x with
@@ -1237,7 +1249,7 @@ and compile_apply
12371249
) in
12381250
match fn, lambda_cxt.continuation with
12391251
| (Lvar fn_id,
1240-
(EffectCall (ReturnTrue (Some ret)) | NeedValue (ReturnTrue (Some ret))))
1252+
(EffectCall (Maybe_tail (Tail_with_name ret)) | NeedValue (Maybe_tail (Tail_with_name ret))))
12411253
when Ident.same ret.id fn_id ->
12421254
ret.triggered <- true;
12431255
(* Here we mark [finished] true, since the continuation
@@ -1289,7 +1301,7 @@ and compile_prim (prim_info : Lam.prim_info) (lambda_cxt : Lam_compile_context.t
12891301
-> (* should be before Lglobal_global *)
12901302
compile_external_field lambda_cxt id n lambda_cxt.meta.env
12911303
| {primitive = Praise ; args = [ e ]; _} ->
1292-
(match compile_lambda {lambda_cxt with continuation = NeedValue ReturnFalse} e with
1304+
(match compile_lambda {lambda_cxt with continuation = NeedValue Not_tail} e with
12931305
| {block ; value = Some v} ->
12941306
Js_output.make
12951307
(Ext_list.append_one block (S.throw_stmt v))
@@ -1320,7 +1332,7 @@ and compile_prim (prim_info : Lam.prim_info) (lambda_cxt : Lam_compile_context.t
13201332
either a getter {[ x #. height ]} or {[ x ## method_call ]}
13211333
*)
13221334
let property = Lam_methname.translate ~loc name in
1323-
(match compile_lambda {lambda_cxt with continuation = NeedValue ReturnFalse} obj
1335+
(match compile_lambda {lambda_cxt with continuation = NeedValue Not_tail} obj
13241336
with
13251337
| {value = None} -> assert false
13261338
| {block; value = Some b } ->
@@ -1348,7 +1360,7 @@ and compile_prim (prim_info : Lam.prim_info) (lambda_cxt : Lam_compile_context.t
13481360
args = [obj]} as fn;
13491361
arg]
13501362
->
1351-
let need_value_no_return_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
1363+
let need_value_no_return_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
13521364
let obj_output = compile_lambda need_value_no_return_cxt obj in
13531365
let arg_output = compile_lambda need_value_no_return_cxt arg in
13541366
let cont obj_block arg_block obj_code =
@@ -1420,7 +1432,7 @@ and compile_prim (prim_info : Lam.prim_info) (lambda_cxt : Lam_compile_context.t
14201432
*)
14211433
(Js_output.output_as_block
14221434
( compile_lambda
1423-
{ lambda_cxt with continuation = EffectCall ( ReturnTrue None);
1435+
{ lambda_cxt with continuation = EffectCall ( Maybe_tail Tail_no_name_lambda);
14241436
jmp_table = Lam_compile_context.empty_handler_map}
14251437
body)))
14261438
| _ -> assert false)
@@ -1435,7 +1447,7 @@ and compile_prim (prim_info : Lam.prim_info) (lambda_cxt : Lam_compile_context.t
14351447
let args_block, args_expr =
14361448
if args = [] then [], []
14371449
else
1438-
let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
1450+
let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
14391451
Ext_list.split_map args (fun x ->
14401452
match compile_lambda new_cxt x with
14411453
| {block ; value = Some b} -> block,b
@@ -1448,7 +1460,7 @@ and compile_prim (prim_info : Lam.prim_info) (lambda_cxt : Lam_compile_context.t
14481460
| {primitive; args; loc} ->
14491461
let args_block, args_expr =
14501462
if args = [] then [], []
1451-
else let new_cxt = {lambda_cxt with continuation = NeedValue ReturnFalse} in
1463+
else let new_cxt = {lambda_cxt with continuation = NeedValue Not_tail} in
14521464
Ext_list.split_map args (fun x ->
14531465
match compile_lambda new_cxt x with
14541466
| {block ; value = Some b} -> block,b
@@ -1473,7 +1485,7 @@ and compile_lambda
14731485
(Js_output.output_as_block
14741486
( compile_lambda
14751487
{ lambda_cxt with
1476-
continuation = EffectCall (ReturnTrue None); (* Refine*)
1488+
continuation = EffectCall (Maybe_tail Tail_no_name_lambda);
14771489
jmp_table = Lam_compile_context.empty_handler_map}
14781490
body)))
14791491
| Lapply appinfo ->
@@ -1516,7 +1528,7 @@ and compile_lambda
15161528
compile_prim prim_info lambda_cxt
15171529
| Lsequence (l1,l2) ->
15181530
let output_l1 =
1519-
compile_lambda {lambda_cxt with continuation = EffectCall ReturnFalse} l1 in
1531+
compile_lambda {lambda_cxt with continuation = EffectCall Not_tail} l1 in
15201532
let output_l2 =
15211533
compile_lambda lambda_cxt l2 in
15221534
Js_output.append_output output_l1 output_l2

jscomp/core/lam_compile_context.ml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,17 @@ type return_label = {
4646
mutable triggered : bool
4747
}
4848

49-
type return_type =
50-
| ReturnFalse
51-
| ReturnTrue of return_label option
49+
type maybe_tail =
50+
| Tail_in_try
51+
| Tail_no_name_lambda
52+
| Tail_with_name of return_label
53+
54+
type tail_type =
55+
| Not_tail
56+
| Maybe_tail of maybe_tail
5257
(* Note [return] does indicate it is a tail position in most cases
5358
however, in an exception handler, return may not be in tail position
54-
to fix #1701 we play a trick that (ReturnTrue None)
59+
to fix #1701 we play a trick that (Maybe_tail None)
5560
would never trigger tailcall, however, it preserves [return]
5661
semantics
5762
*)
@@ -61,18 +66,18 @@ type return_type =
6166
type let_kind = Lam_compat.let_kind
6267

6368
type continuation =
64-
| EffectCall of return_type
65-
| NeedValue of return_type
69+
| EffectCall of tail_type
70+
| NeedValue of tail_type
6671
| Declare of let_kind * J.ident (* bound value *)
6772
| Assign of J.ident (* when use [Assign], var is not needed, since it's already declared *)
6873

6974
type jmp_table = value HandlerMap.t
7075

7176
let continuation_is_return ( x : continuation) =
7277
match x with
73-
| EffectCall (ReturnTrue _) | NeedValue (ReturnTrue _)
78+
| EffectCall (Maybe_tail _) | NeedValue (Maybe_tail _)
7479
-> true
75-
| EffectCall ReturnFalse | NeedValue ReturnFalse
80+
| EffectCall Not_tail | NeedValue Not_tail
7681
| Declare _ | Assign _
7782
-> false
7883

jscomp/core/lam_compile_context.mli

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,23 @@ type value = {
5757

5858
type let_kind = Lam_compat.let_kind
5959

60-
type return_type =
61-
| ReturnFalse
62-
| ReturnTrue of return_label option (* anonoymous function does not have identifier *)
60+
type maybe_tail =
61+
| Tail_in_try
62+
| Tail_no_name_lambda
63+
| Tail_with_name of return_label
64+
65+
type tail_type =
66+
| Not_tail
67+
| Maybe_tail of maybe_tail
68+
(* anonoymous function does not have identifier *)
6369

6470
(* delegate to the callee to generate expression
6571
Invariant: [output] should return a trailing expression
6672
*)
6773

6874
type continuation =
69-
| EffectCall of return_type
70-
| NeedValue of return_type
75+
| EffectCall of tail_type
76+
| NeedValue of tail_type
7177
| Declare of let_kind * J.ident (* bound value *)
7278
| Assign of J.ident
7379
(** when use [Assign], var is not needed, since it's already declared

0 commit comments

Comments
 (0)