Skip to content

Commit d4e5c8e

Browse files
authored
Fix issue in functors with more than one argument (which are curried): emit nested function always. (rescript-lang#7273)
1 parent 5ce146f commit d4e5c8e

6 files changed

+131
-99
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- Fix issue with type environment for unified ops. https://github.com/rescript-lang/rescript/pull/7277
2424
- Fix completion for application with tagged template. https://github.com/rescript-lang/rescript/pull/7278
2525
- Fix error message for arity in the presence of optional arguments. https://github.com/rescript-lang/rescript/pull/7284
26+
- Fix issue in functors with more than one argument (which are curried): emit nested function always. https://github.com/rescript-lang/rescript/pull/7273
2627

2728
#### :house: Internal
2829

compiler/ml/translmod.ml

+45-77
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
open Typedtree
2020

21-
type error = Conflicting_inline_attributes | Fragile_pattern_in_toplevel
21+
type error = Fragile_pattern_in_toplevel
2222

2323
exception Error of Location.t * error
2424

@@ -78,37 +78,30 @@ let rec apply_coercion loc strict (restr : Typedtree.module_coercion) arg =
7878
| Tcoerce_functor (cc_arg, cc_res) ->
7979
let param = Ident.create "funarg" in
8080
let carg = apply_coercion loc Alias cc_arg (Lvar param) in
81-
apply_coercion_result loc strict arg [param] [carg] cc_res
81+
apply_coercion_result loc strict arg param carg cc_res
8282
| Tcoerce_primitive {pc_loc; pc_desc; pc_env; pc_type} ->
8383
Translcore.transl_primitive pc_loc pc_desc pc_env pc_type
8484
| Tcoerce_alias (path, cc) ->
8585
Lambda.name_lambda strict arg (fun _ ->
8686
apply_coercion loc Alias cc (Lambda.transl_normal_path path))
8787

88-
and apply_coercion_result loc strict funct params args cc_res =
89-
match cc_res with
90-
| Tcoerce_functor (cc_arg, cc_res) ->
91-
let param = Ident.create "funarg" in
92-
let arg = apply_coercion loc Alias cc_arg (Lvar param) in
93-
apply_coercion_result loc strict funct (param :: params) (arg :: args)
94-
cc_res
95-
| _ ->
96-
Lambda.name_lambda strict funct (fun id ->
97-
Lfunction
98-
{
99-
params = List.rev params;
100-
attr = {Lambda.default_function_attribute with is_a_functor = true};
101-
loc;
102-
body =
103-
apply_coercion loc Strict cc_res
104-
(Lapply
105-
{
106-
ap_loc = loc;
107-
ap_func = Lvar id;
108-
ap_args = List.rev args;
109-
ap_inlined = Default_inline;
110-
});
111-
})
88+
and apply_coercion_result loc strict funct param arg cc_res =
89+
Lambda.name_lambda strict funct (fun id ->
90+
Lfunction
91+
{
92+
params = [param];
93+
attr = {Lambda.default_function_attribute with is_a_functor = true};
94+
loc;
95+
body =
96+
apply_coercion loc Strict cc_res
97+
(Lapply
98+
{
99+
ap_loc = loc;
100+
ap_func = Lvar id;
101+
ap_args = [arg];
102+
ap_inlined = Default_inline;
103+
});
104+
})
112105

113106
and wrap_id_pos_list loc id_pos_list get_field lam =
114107
let fv = Lambda.free_variables lam in
@@ -210,64 +203,41 @@ let rec bound_value_identifiers : Types.signature_item list -> Ident.t list =
210203
functor(s) being merged with. Such an attribute will be placed on the
211204
resulting merged functor. *)
212205

213-
let merge_inline_attributes (attr1 : Lambda.inline_attribute)
214-
(attr2 : Lambda.inline_attribute) loc =
215-
match (attr1, attr2) with
216-
| Lambda.Default_inline, _ -> attr2
217-
| _, Lambda.Default_inline -> attr1
218-
| _, _ ->
219-
if attr1 = attr2 then attr1
220-
else raise (Error (loc, Conflicting_inline_attributes))
221-
222-
let merge_functors mexp coercion root_path =
223-
let rec merge mexp coercion path acc inline_attribute =
224-
let finished = (acc, mexp, path, coercion, inline_attribute) in
225-
match mexp.mod_desc with
226-
| Tmod_functor (param, _, _, body) ->
227-
let inline_attribute' =
228-
Translattribute.get_inline_attribute mexp.mod_attributes
229-
in
230-
let arg_coercion, res_coercion =
231-
match coercion with
232-
| Tcoerce_none -> (Tcoerce_none, Tcoerce_none)
233-
| Tcoerce_functor (arg_coercion, res_coercion) ->
234-
(arg_coercion, res_coercion)
235-
| _ -> Misc.fatal_error "Translmod.merge_functors: bad coercion"
236-
in
237-
let loc = mexp.mod_loc in
238-
let path = functor_path path param in
239-
let inline_attribute =
240-
merge_inline_attributes inline_attribute inline_attribute' loc
241-
in
242-
merge body res_coercion path
243-
((param, loc, arg_coercion) :: acc)
244-
inline_attribute
245-
| _ -> finished
246-
in
247-
merge mexp coercion root_path [] Default_inline
206+
let get_functor_params mexp coercion root_path =
207+
match mexp.mod_desc with
208+
| Tmod_functor (param, _, _, body) ->
209+
let inline_attribute =
210+
Translattribute.get_inline_attribute mexp.mod_attributes
211+
in
212+
let arg_coercion, res_coercion =
213+
match coercion with
214+
| Tcoerce_none -> (Tcoerce_none, Tcoerce_none)
215+
| Tcoerce_functor (arg_coercion, res_coercion) ->
216+
(arg_coercion, res_coercion)
217+
| _ -> Misc.fatal_error "Translmod.get_functor_params: bad coercion"
218+
in
219+
let loc = mexp.mod_loc in
220+
let path = functor_path root_path param in
221+
((param, loc, arg_coercion), body, path, res_coercion, inline_attribute)
222+
| _ -> assert false
248223

249224
let export_identifiers : Ident.t list ref = ref []
250225

251226
let rec compile_functor mexp coercion root_path loc =
252-
let functor_params_rev, body, body_path, res_coercion, inline_attribute =
253-
merge_functors mexp coercion root_path
227+
let functor_param, body, body_path, res_coercion, inline_attribute =
228+
get_functor_params mexp coercion root_path
254229
in
255-
assert (functor_params_rev <> []);
256230
(* cf. [transl_module] *)
257-
let params, body =
258-
List.fold_left
259-
(fun (params, body) (param, loc, arg_coercion) ->
260-
let param' = Ident.rename param in
261-
let arg = apply_coercion loc Alias arg_coercion (Lvar param') in
262-
let params = param' :: params in
263-
let body = Lambda.Llet (Alias, Pgenval, param, arg, body) in
264-
(params, body))
265-
([], transl_module res_coercion body_path body)
266-
functor_params_rev
231+
let param, loc_, arg_coercion = functor_param in
232+
let param' = Ident.rename param in
233+
let arg = apply_coercion loc_ Alias arg_coercion (Lvar param') in
234+
let body =
235+
Lambda.Llet
236+
(Alias, Pgenval, param, arg, transl_module res_coercion body_path body)
267237
in
268238
Lambda.Lfunction
269239
{
270-
params;
240+
params = [param'];
271241
attr =
272242
{
273243
inline = inline_attribute;
@@ -513,8 +483,6 @@ let transl_implementation module_name (str, cc) =
513483
(* Error report *)
514484

515485
let report_error ppf = function
516-
| Conflicting_inline_attributes ->
517-
Format.fprintf ppf "@[Conflicting ``inline'' attributes@]"
518486
| Fragile_pattern_in_toplevel ->
519487
Format.fprintf ppf "@[Such fragile pattern not allowed in the toplevel@]"
520488

tests/tests/src/functors.mjs

+22-16
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,42 @@ function O(X) {
1010
};
1111
}
1212

13-
function F(X, Y) {
14-
let cow = x => Y.foo(X.foo(x));
15-
let sheep = x => 1 + Y.foo(X.foo(x)) | 0;
16-
return {
17-
cow: cow,
18-
sheep: sheep
13+
function F(X) {
14+
return Y => {
15+
let cow = x => Y.foo(X.foo(x));
16+
let sheep = x => 1 + Y.foo(X.foo(x)) | 0;
17+
return {
18+
cow: cow,
19+
sheep: sheep
20+
};
1921
};
2022
}
2123

22-
function F1(X, Y) {
23-
let sheep = x => 1 + Y.foo(X.foo(x)) | 0;
24-
return {
25-
sheep: sheep
24+
function F1(X) {
25+
return Y => {
26+
let sheep = x => 1 + Y.foo(X.foo(x)) | 0;
27+
return {
28+
sheep: sheep
29+
};
2630
};
2731
}
2832

29-
function F2(X, Y) {
30-
let sheep = x => 1 + Y.foo(X.foo(x)) | 0;
31-
return {
32-
sheep: sheep
33+
function F2(X) {
34+
return Y => {
35+
let sheep = x => 1 + Y.foo(X.foo(x)) | 0;
36+
return {
37+
sheep: sheep
38+
};
3339
};
3440
}
3541

3642
let M = {
37-
F: (funarg, funarg$1) => {
43+
F: funarg => (funarg$1 => {
3844
let sheep = x => 1 + funarg$1.foo(funarg.foo(x)) | 0;
3945
return {
4046
sheep: sheep
4147
};
42-
}
48+
})
4349
};
4450

4551
export {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Generated by ReScript, PLEASE EDIT WITH CARE
2+
3+
4+
function Make(T) {
5+
return Q => {
6+
let Eq = E => (A => ({}));
7+
return {
8+
Eq: Eq
9+
};
10+
};
11+
}
12+
13+
function Eq(E) {
14+
return A => ({});
15+
}
16+
17+
let M = {
18+
Eq: Eq
19+
};
20+
21+
let EQ = Eq({})({});
22+
23+
let MF = {
24+
F: funarg => (funarg => ({}))
25+
};
26+
27+
function UseF(X) {
28+
return Y => MF.F(X)(Y);
29+
}
30+
31+
export {
32+
Make,
33+
M,
34+
EQ,
35+
MF,
36+
UseF,
37+
}
38+
/* EQ Not a pure module */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Make = (T: {}, Q: {}) => {
2+
module Eq = (E: {}, A: {}) => {}
3+
}
4+
5+
module M = Make((), ())
6+
7+
module EQ = M.Eq((), ())
8+
9+
module MF: {
10+
module F: (X: {}, Y: {}) => {}
11+
} = {
12+
module F = (X: {}, Y: {}) => {
13+
let c = 12
14+
}
15+
}
16+
17+
module UseF = (X: {}, Y: {}) => MF.F(X, Y)

tests/tests/src/recmodule.mjs

+8-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ let UseCase = {
1818
MakeLayer: MakeLayer
1919
};
2020

21-
function MakeLayer$1(Deps, UC) {
22-
let presentLight = light => Deps.presentJson(light, 200);
23-
let handleGetLight = req => UC.getLight(req.params.id);
24-
return {
25-
handleGetLight: handleGetLight,
26-
presentLight: presentLight
21+
function MakeLayer$1(Deps) {
22+
return UC => {
23+
let presentLight = light => Deps.presentJson(light, 200);
24+
let handleGetLight = req => UC.getLight(req.params.id);
25+
return {
26+
handleGetLight: handleGetLight,
27+
presentLight: presentLight
28+
};
2729
};
2830
}
2931

0 commit comments

Comments
 (0)