Skip to content

Commit 29e27e4

Browse files
authored
Fix partial application for uncurried functions with labeled args (#6198)
Partial application for uncurried functions used normal application. There's an issue with the code generated in the presence of labeled arguments, ad the function generated is curried. This PR changes the translation from typed ast to lambda for the uncurried partial application case, by writing a single function with all the leftover arguments. So it correctly represents an uncurried function. Fixex #6164
1 parent 39fdfe8 commit 29e27e4

6 files changed

+191
-14
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#### :bug: Bug Fix
2828

2929
- Make "rescript format" work with node 10 again and set minimum required node version to 10 in package.json. https://github.com/rescript-lang/rescript-compiler/pull/6186
30+
- Fix partial application for uncurried functions with labeled args https://github.com/rescript-lang/rescript-compiler/pull/6198
3031

3132
# 11.0.0-alpha.4
3233

jscomp/ml/ast_uncurried.ml

+6
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,9 @@ let uncurried_type_get_arity ~env typ =
106106
| Tconstr (Pident { name = "function$" }, [ _t; tArity ], _) ->
107107
type_to_arity tArity
108108
| _ -> assert false
109+
110+
let uncurried_type_get_arity_opt ~env typ =
111+
match (Ctype.expand_head env typ).desc with
112+
| Tconstr (Pident { name = "function$" }, [ _t; tArity ], _) ->
113+
Some (type_to_arity tArity)
114+
| _ -> None

jscomp/ml/translcore.ml

+39-7
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,14 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
762762
let inlined, funct =
763763
Translattribute.get_and_remove_inlined_attribute funct
764764
in
765-
transl_apply ~inlined (transl_exp funct) oargs e.exp_loc
765+
let uncurried_partial_application =
766+
let uncurried_partial_app = Ext_list.exists e.exp_attributes (fun ({txt },_) -> txt = "res.partial") in
767+
if uncurried_partial_app then
768+
let arity_opt = Ast_uncurried.uncurried_type_get_arity_opt ~env:funct.exp_env funct.exp_type in
769+
arity_opt
770+
else
771+
None in
772+
transl_apply ~inlined ~uncurried_partial_application (transl_exp funct) oargs e.exp_loc
766773
| Texp_match (arg, pat_expr_list, exn_pat_expr_list, partial) ->
767774
transl_match e arg pat_expr_list exn_pat_expr_list partial
768775
| Texp_try (body, pat_expr_list) ->
@@ -978,7 +985,7 @@ and transl_cases_try cases =
978985
in
979986
List.map transl_case_try cases
980987

981-
and transl_apply ?(inlined = Default_inline) lam sargs loc =
988+
and transl_apply ?(inlined = Default_inline) ?(uncurried_partial_application=None) lam sargs loc =
982989
let lapply funct args =
983990
match funct with
984991
(* Attention: This may not be what we need to change the application arity*)
@@ -1028,11 +1035,36 @@ and transl_apply ?(inlined = Default_inline) lam sargs loc =
10281035
| (Some arg, optional) :: l -> build_apply lam ((arg, optional) :: args) l
10291036
| [] -> lapply lam (List.rev_map fst args)
10301037
in
1031-
(build_apply lam []
1032-
(List.map
1033-
(fun (l, x) -> (may_map transl_exp x, Btype.is_optional l))
1034-
sargs)
1035-
: Lambda.lambda)
1038+
match uncurried_partial_application with
1039+
| Some arity when arity > List.length sargs ->
1040+
let extra_arity = arity - List.length sargs in
1041+
let none_ids = ref [] in
1042+
let args = Ext_list.filter_map sargs (function
1043+
| _, Some e ->
1044+
Some (transl_exp e)
1045+
| _, None ->
1046+
let id_arg = Ident.create "none" in
1047+
none_ids := id_arg :: !none_ids;
1048+
Some (Lvar id_arg)) in
1049+
let extra_ids = ref [] in
1050+
extra_ids := Ident.create "extra" :: !extra_ids;
1051+
let extra_ids = Array.init extra_arity (fun _ -> Ident.create "extra") |> Array.to_list in
1052+
let extra_args = Ext_list.map extra_ids (fun id -> Lvar id) in
1053+
let ap_args = args @ extra_args in
1054+
let l0 = Lapply { ap_func = lam; ap_args; ap_inlined = inlined; ap_loc = loc } in
1055+
Lfunction
1056+
{
1057+
params = List.rev_append !none_ids extra_ids ;
1058+
body = l0;
1059+
attr = default_function_attribute;
1060+
loc;
1061+
}
1062+
| _ ->
1063+
(build_apply lam []
1064+
(List.map
1065+
(fun (l, x) -> (may_map transl_exp x, Btype.is_optional l))
1066+
sargs)
1067+
: Lambda.lambda)
10361068

10371069
and transl_function loc partial param cases =
10381070
match cases with

jscomp/ml/typecore.ml

+5-5
Original file line numberDiff line numberDiff line change
@@ -3035,7 +3035,7 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
30353035
(fully_applied, newT)
30363036
| _ -> (false, newT)
30373037
in
3038-
let rec type_unknown_args max_arity (args : lazy_args) omitted ty_fun (syntax_args : sargs)
3038+
let rec type_unknown_args max_arity ~(args : lazy_args) omitted ty_fun (syntax_args : sargs)
30393039
: targs * _ =
30403040
match syntax_args with
30413041
| [] ->
@@ -3050,14 +3050,14 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
30503050
| Tarrow (Optional l,t1,t2,_) ->
30513051
ignored := (Optional l,t1,ty_fun.level) :: !ignored;
30523052
let arg = Optional l, Some (fun () -> option_none (instance env t1) Location.none) in
3053-
type_unknown_args max_arity (arg::args) omitted t2 []
3053+
type_unknown_args max_arity ~args:(arg::args) omitted t2 []
30543054
| _ -> collect_args ())
30553055
else
30563056
collect_args ()
30573057
| [(Nolabel, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)})]
30583058
when uncurried && omitted = [] && args <> [] && List.length args = List.length !ignored ->
30593059
(* foo(. ) treated as empty application if all args are optional (hence ignored) *)
3060-
type_unknown_args max_arity args omitted ty_fun []
3060+
type_unknown_args max_arity ~args omitted ty_fun []
30613061
| (l1, sarg1) :: sargl ->
30623062
let (ty1, ty2) =
30633063
let ty_fun = expand_head env ty_fun in
@@ -3097,7 +3097,7 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
30973097
unify_exp env arg1 (type_option(newvar()));
30983098
arg1
30993099
in
3100-
type_unknown_args max_arity ((l1, Some arg1) :: args) omitted ty2 sargl
3100+
type_unknown_args max_arity ~args:((l1, Some arg1) :: args) omitted ty2 sargl
31013101
in
31023102
let rec type_args max_arity args omitted ~ty_fun ty_fun0 ~(sargs : sargs) =
31033103
match expand_head env ty_fun, expand_head env ty_fun0 with
@@ -3130,7 +3130,7 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
31303130
in
31313131
type_args max_arity ((l,arg)::args) omitted ~ty_fun ty_fun0 ~sargs
31323132
| _ ->
3133-
type_unknown_args max_arity args omitted ty_fun0 sargs (* This is the hot path for non-labeled function*)
3133+
type_unknown_args max_arity ~args omitted ty_fun0 sargs (* This is the hot path for non-labeled function*)
31343134
in
31353135
let () =
31363136
let ls, tvar = list_labels env funct.exp_type in

jscomp/test/UncurriedAlways.js

+107-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/UncurriedAlways.res

+33
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,36 @@ let inl = () => ()
3232

3333
@inline
3434
let inl2 = (x,y) => x+y
35+
36+
module AllLabels = {
37+
let foo = (~x, ~y, ~z) => (x, y, z)
38+
39+
let ptl = foo(~y="y", ...)
40+
41+
let a1 = ptl(~x="x", ~z="z")
42+
Js.log2("a1:", a1)
43+
}
44+
45+
module OptAtEnd = {
46+
let foo = (~x, ~y, ~z, ~d="d=0") => (x, y, z, d)
47+
48+
let ptl = foo(~y="y", ...)
49+
50+
let b1 = ptl(~x="x", ~z="z")
51+
Js.log2("b1:", b1)
52+
let b2 = ptl(~x="x", ~z="z", ~d="d<-100")
53+
Js.log2("b2:", b2)
54+
}
55+
56+
module OptMixed = {
57+
let foo = (~d1="d1=0", ~x, ~d2="d2=0", ~y, ~d3="d3=0", ~z, ~d4="d4=0", ~w, ~d5="d5=0") => (d1, x, d2, y, d3, z, d4, w, d5)
58+
59+
let ptl = foo(~y="y", ~w="w", ...)
60+
61+
let c1 = ptl(~x="x", ~z="z")
62+
Js.log2("c1:", c1)
63+
let c2 = ptl(~x="x", ~z="z", ~d1="d1<-100")
64+
Js.log2("c2:", c2)
65+
let c3 = ptl(~x="x", ~z="z", ~d2="d2<-200", ~d4="d4<-400")
66+
Js.log2("c3:", c3)
67+
}

0 commit comments

Comments
 (0)