Skip to content

Commit eb07cb5

Browse files
authored
New internal representation for uncurried types. (#5870)
* Rough test of different runtime representation for uncurried types. Only for arity 5. The representation of `(. int, int) => string` would be `Js.uncurried( ((int,int)=>int, [unit, unit])` where the second type parameter encodes the arity. Does not require to pre-declare all the arities. * More cases. * Remove code duplication. * Translcore has code to translate uncurried function definitions with `#fn_mk`. * Use variant for arity. * More cases where the uncurried representation is hardcoded. * Change representation: pass arity in an attribute. Using a constraint to determine the arity in a function definition is problematic as the constraint limits type propagation during inference. Instead, pass the arity via an attribute @res.arity on the Js.Uncurried variant. And specialize the type checker to handle that arity and use type unification to propagate the arity. * Use built-in type `uncurried$` with one variant `Uncurried$`. * Move arity 0 onto new representation. * Adapt error messages to new uncurried representation. * Rename `[#2]` to `[#Has_arity2]` * Rename uncurried$ to function$ * More robust cycle1 test w.r.t. stale artifacts. * Turn on new uncurried representation for all arities. * Update outcome printer tests to new uncurried representation. * Adapt genType to new uncurried representation. * Remove old uses of Js.Fn. * Unused `js_fn`. * Single source of truth for uncurried ast operations shared with syntax. * Update CHANGELOG.md
1 parent d715bfd commit eb07cb5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1933
-2400
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ These are only breaking changes for unformatted code.
5656
- Treat `@meth` annotation as making the type uncurried for backwards compatibitly with some examples https://github.com/rescript-lang/rescript-compiler/pull/5845
5757
- Process `@set` annotation for field update as generating an uncurried function https://github.com/rescript-lang/rescript-compiler/pull/5846
5858
- Treat uncurried application of primitives like curried application, which produces better output https://github.com/rescript-lang/rescript-compiler/pull/5851
59+
- New internal representation for uncurried functions using built-in type `function$<fun_type, arity>` this avoids having to declare all the possible arities ahead of time https://github.com/rescript-lang/rescript-compiler/pull/5870
5960

6061
# 10.1.1
6162

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ test-syntax-roundtrip: build
3434
bash ./scripts/testok.sh
3535

3636
test-gentype: build
37-
make -C jscomp/gentype_tests/typescript-react-example test
37+
make -C jscomp/gentype_tests/typescript-react-example clean test
3838

3939
test-all: test test-gentype
4040

jscomp/build_tests/cycle1/input.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const fs = require('fs')
55
const path = require('path')
66
var rescript_exe = require("../../../scripts/bin_path").rescript_exe
77

8+
cp.execSync(`${rescript_exe} clean -with-deps`, { cwd: __dirname, });
9+
810
var output = cp.spawnSync(rescript_exe, { encoding: "utf8", shell: true });
911

1012
assert(/is dangling/.test(output.stdout));

jscomp/frontend/ast_core_type.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ let get_uncurry_arity (ty : t) =
132132

133133
let get_curry_arity (ty : t) =
134134
match ty.ptyp_desc with
135-
| Ptyp_constr ({ txt = Ldot (Ldot (Lident "Js", "Fn"), _) }, [ t ]) ->
135+
| Ptyp_constr ({ txt = Lident "function$" }, [ t; _ ]) ->
136136
get_uncurry_arity_aux t 0
137137
| _ -> get_uncurry_arity_aux ty 0
138138

jscomp/frontend/ast_core_type_class_type.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ let typ_mapper (self : Bs_ast_mapper.mapper) (ty : Parsetree.core_type) =
7171
ptyp_desc =
7272
( Ptyp_arrow (label, args, body)
7373
| Ptyp_constr
74-
(* Js.Fn.xx is re-wrapped around only in case Nothing below *)
75-
( { txt = Ldot (Ldot (Lident "Js", "Fn"), _) },
76-
[ { ptyp_desc = Ptyp_arrow (label, args, body) } ] ) );
74+
(* function$<...> is re-wrapped around only in case Nothing below *)
75+
( { txt = Lident "function$" },
76+
[ { ptyp_desc = Ptyp_arrow (label, args, body) }; _ ] ) );
7777
(* let it go without regard label names,
7878
it will report error later when the label is not empty
7979
*)

jscomp/frontend/ast_external_process.ml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -868,15 +868,19 @@ let handle_attributes (loc : Bs_loc.t) (type_annotation : Parsetree.core_type)
868868
Location.raise_errorf ~loc
869869
"%@uncurry can not be applied to the whole definition";
870870
let prim_name_with_source = { name = prim_name; source = External } in
871-
let type_annotation, build_uncurried_type = match type_annotation.ptyp_desc with
872-
| Ptyp_constr ({txt = Ldot(Ldot(Lident "Js", "Fn"), arity_);_} as lid, [t]) ->
873-
t, fun ~arity x ->
874-
let arity = match arity with
875-
| Some arity -> "arity" ^ string_of_int arity
876-
| None -> arity_ in
877-
let lid = {lid with txt = Longident.Ldot(Ldot(Lident "Js", "Fn"), arity)} in
878-
{x with Parsetree.ptyp_desc = Ptyp_constr (lid, [x])}
879-
| _ -> type_annotation, fun ~arity:_ x -> x in
871+
let type_annotation, build_uncurried_type =
872+
match type_annotation.ptyp_desc with
873+
| Ptyp_constr (({ txt = Lident "function$"; _ } as lid), [ t; arity_ ]) ->
874+
( t,
875+
fun ~arity x ->
876+
let tArity =
877+
match arity with
878+
| Some arity -> Ast_uncurried.arityType ~loc arity
879+
| None -> arity_
880+
in
881+
{ x with Parsetree.ptyp_desc = Ptyp_constr (lid, [ x; tArity ]) } )
882+
| _ -> (type_annotation, fun ~arity:_ x -> x)
883+
in
880884
let result_type, arg_types_ty =
881885
(* Note this assumes external type is syntatic (no abstraction)*)
882886
Ast_core_type.list_of_arrow type_annotation

jscomp/frontend/ast_literal.ml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ module Lid = struct
5555

5656
let opaque : t = Ldot (js_internal, "opaque")
5757

58-
let js_fn : t = Ldot (Lident "Js", "Fn")
59-
6058
let js_oo : t = Lident "Js_OO"
6159

6260
let js_meth_callback : t = Ldot (js_oo, "Callback")

jscomp/frontend/ast_literal.mli

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ module Lid : sig
3939

4040
val type_int : t
4141

42-
val js_fn : t
43-
4442
val js_internal_full_apply : t
4543

4644
val opaque : t

jscomp/frontend/ast_typ_uncurry.ml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
2424

2525
type typ = Parsetree.core_type
26-
2726
type 'a cxt = Ast_helper.loc -> Bs_ast_mapper.mapper -> 'a
28-
2927
type uncurry_type_gen = (Asttypes.arg_label -> typ -> typ -> typ) cxt
3028

3129
module Typ = Ast_helper.Typ
@@ -63,10 +61,5 @@ let to_uncurry_type loc (mapper : Bs_ast_mapper.mapper)
6361
let fn_type = Typ.arrow ~loc label first_arg typ in
6462
let arity = Ast_core_type.get_uncurry_arity fn_type in
6563
match arity with
66-
| Some 0 ->
67-
Typ.constr { txt = Ldot (Ast_literal.Lid.js_fn, "arity0"); loc } [ typ ]
68-
| Some n ->
69-
Typ.constr
70-
{ txt = Ldot (Ast_literal.Lid.js_fn, "arity" ^ string_of_int n); loc }
71-
[ fn_type ]
64+
| Some arity -> Ast_uncurried.uncurriedType ~loc ~arity fn_type
7265
| None -> assert false

jscomp/frontend/ast_uncurry_gen.ml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ let to_method_callback loc (self : Bs_ast_mapper.mapper) label
6767
[ Typ.any ~loc () ]) );
6868
] )
6969

70-
let to_uncurry_fn loc (self : Bs_ast_mapper.mapper) (label : Asttypes.arg_label)
71-
pat body async : Parsetree.expression_desc =
70+
let to_uncurry_fn (e : Parsetree.expression) (self : Bs_ast_mapper.mapper)
71+
(label : Asttypes.arg_label) pat body async : Parsetree.expression =
72+
let loc = e.pexp_loc in
7273
Bs_syntaxerr.optional_err loc label;
7374
let rec aux acc (body : Parsetree.expression) =
7475
match Ast_attributes.process_attributes_rev body.pexp_attributes with
@@ -97,7 +98,9 @@ let to_uncurry_fn loc (self : Bs_ast_mapper.mapper) (label : Asttypes.arg_label)
9798
| _ -> len
9899
in
99100
Bs_syntaxerr.err_large_arity loc arity;
100-
let arity_s = string_of_int arity in
101-
Pexp_record
102-
( [ ({ txt = Ldot (Ast_literal.Lid.js_fn, "I" ^ arity_s); loc }, body) ],
103-
None )
101+
let fun_exp = Ast_uncurried.uncurriedFun ~loc ~arity body in
102+
{
103+
e with
104+
pexp_desc = fun_exp.pexp_desc;
105+
pexp_attributes = fun_exp.pexp_attributes @ e.pexp_attributes;
106+
}

jscomp/frontend/ast_uncurry_gen.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
2424

2525
val to_uncurry_fn :
26-
Location.t ->
26+
Parsetree.expression ->
2727
Bs_ast_mapper.mapper ->
2828
Asttypes.arg_label ->
2929
Parsetree.pattern ->
3030
Parsetree.expression ->
3131
bool -> (* async *)
32-
Parsetree.expression_desc
32+
Parsetree.expression
3333
(**
3434
[function] can only take one argument, that is the reason we did not adopt it
3535
syntax:

jscomp/frontend/bs_builtin_ppx.ml

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,18 @@ let expr_mapper ~async_context ~in_function_def (self : mapper)
115115
| true, pexp_attributes ->
116116
Ast_bs_open.convertBsErrorFunction e.pexp_loc self pexp_attributes
117117
cases)
118-
| Pexp_record
119-
( [
120-
( { txt = Ldot (Ldot (Lident "Js", "Fn"), _) },
121-
({ pexp_desc = Pexp_fun _; pexp_attributes } as inner_exp) );
122-
],
123-
None )
124-
when match Ast_attributes.process_attributes_rev pexp_attributes with
118+
| _
119+
when Ast_uncurried.exprIsUncurriedFun e
120+
&&
121+
match
122+
Ast_attributes.process_attributes_rev
123+
(Ast_uncurried.exprExtractUncurriedFun e).pexp_attributes
124+
with
125125
| Meth_callback _, _ -> true
126126
| _ -> false ->
127127
(* Treat @this (. x, y, z) => ... just like @this (x, y, z) => ... *)
128-
self.expr self inner_exp
128+
let fun_expr = Ast_uncurried.exprExtractUncurriedFun e in
129+
self.expr self fun_expr
129130
| Pexp_fun (label, _, pat, body) -> (
130131
let async = Ast_attributes.has_async_payload e.pexp_attributes <> None in
131132
match Ast_attributes.process_attributes_rev e.pexp_attributes with
@@ -136,12 +137,8 @@ let expr_mapper ~async_context ~in_function_def (self : mapper)
136137
Ast_async.make_function_async ~async (default_expr_mapper self e)
137138
| Uncurry _, pexp_attributes ->
138139
async_context := async;
139-
{
140-
e with
141-
pexp_desc =
142-
Ast_uncurry_gen.to_uncurry_fn e.pexp_loc self label pat body async;
143-
pexp_attributes;
144-
}
140+
Ast_uncurry_gen.to_uncurry_fn { e with pexp_attributes } self label
141+
pat body async
145142
| Method _, _ ->
146143
Location.raise_errorf ~loc:e.pexp_loc
147144
"%@meth is not supported in function expression"

jscomp/gentype/TranslateStructure.ml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,9 @@ let rec addAnnotationsToTypes_ ~config ~(expr : Typedtree.expression)
1414
in
1515
let aName = Ident.name param in
1616
{ aName; aType } :: nextTypes1
17-
| ( Texp_record
18-
{ fields = [| ({ lbl_name = "I" }, Overridden (_, exprRecord)) |] },
19-
Tconstr (path, _, _),
20-
_ )
21-
when match path |> TranslateTypeExprFromTypes.pathToList |> List.rev with
22-
| [ "Js"; "Fn"; _arity ] -> true
23-
| _ -> false ->
24-
(* let uncurried1: Js.Fn.arity1(_) = {I: x => x |> string_of_int} *)
25-
addAnnotationsToTypes_ ~config ~expr:exprRecord argTypes
17+
| ( Texp_construct ({txt = Lident "Function$"}, _, [funExpr]), _, _) ->
18+
(* let uncurried1: function$<_, _> = Function$(x => x |> string_of_int, [`Has_arity1]) *)
19+
addAnnotationsToTypes_ ~config ~expr:funExpr argTypes
2620
| ( Texp_apply ({ exp_desc = Texp_ident (path, _, _) }, [ (_, Some expr1) ]),
2721
_,
2822
_ ) -> (

jscomp/gentype/TranslateTypeExprFromTypes.ml

Lines changed: 1 addition & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -205,134 +205,14 @@ let translateConstr ~config ~paramsTranslation ~(path : Path.t) ~typeEnv =
205205
{ paramTranslation with type_ = Nullable paramTranslation.type_ }
206206
| ([ "Js"; "Promise"; "t" ] | ["promise"]), [ paramTranslation ] ->
207207
{ paramTranslation with type_ = Promise paramTranslation.type_ }
208-
| ( [ "Js"; "Internal"; "fn" ],
209-
[ { dependencies = argsDependencies; type_ = Tuple ts }; ret ] ) ->
210-
{
211-
dependencies = argsDependencies @ ret.dependencies;
212-
type_ =
213-
Function
214-
{
215-
argTypes =
216-
ts |> List.map (fun type_ -> { aName = ""; aType = type_ });
217-
componentName = None;
218-
retType = ret.type_;
219-
typeVars = [];
220-
uncurried = true;
221-
};
222-
}
223-
| ( [ "Js"; "Internal"; "fn" ],
224-
[
225-
{
226-
dependencies = argsDependencies;
227-
type_ = Variant { noPayloads = [ { label = "Arity_0" } ] };
228-
};
229-
ret;
230-
] ) ->
231-
{
232-
dependencies = argsDependencies @ ret.dependencies;
233-
type_ =
234-
Function
235-
{
236-
argTypes = [];
237-
componentName = None;
238-
retType = ret.type_;
239-
typeVars = [];
240-
uncurried = true;
241-
};
242-
}
243-
| [ "Js"; "Fn"; "arity0" ], [ ret ] ->
244-
{
245-
dependencies = ret.dependencies;
246-
type_ =
247-
Function
248-
{
249-
argTypes = [];
250-
componentName = None;
251-
retType = ret.type_;
252-
typeVars = [];
253-
uncurried = true;
254-
};
255-
}
256-
| ( [
257-
("Js" | "Js_OO");
258-
("Fn" | "Meth");
259-
( "arity1" | "arity2" | "arity3" | "arity4" | "arity5" | "arity6"
260-
| "arity7" | "arity8" | "arity9" | "arity10" | "arity11" | "arity12"
261-
| "arity13" | "arity14" | "arity15" | "arity16" | "arity17" | "arity18"
262-
| "arity19" | "arity20" | "arity21" | "arity22" );
263-
],
264-
[ arg ] ) ->
208+
| ( [ "function$"], [ arg; _arity ] ) ->
265209
{
266210
dependencies = arg.dependencies;
267211
type_ =
268212
(match arg.type_ with
269213
| Function function_ -> Function { function_ with uncurried = true }
270214
| _ -> arg.type_);
271215
}
272-
| ( [ "Js"; "Internal"; "fn" ],
273-
[ { dependencies = argsDependencies; type_ = singleT }; ret ] ) ->
274-
let argTypes =
275-
(match singleT with
276-
| Variant { payloads = [ { t = Tuple argTypes } ] } -> argTypes
277-
| Variant { payloads = [ { t = type_ } ] } -> [ type_ ]
278-
| _ -> [ singleT ])
279-
|> List.map (fun type_ -> { aName = ""; aType = type_ })
280-
in
281-
{
282-
dependencies = argsDependencies @ ret.dependencies;
283-
type_ =
284-
Function
285-
{
286-
argTypes;
287-
componentName = None;
288-
retType = ret.type_;
289-
typeVars = [];
290-
uncurried = true;
291-
};
292-
}
293-
| ( ([ "Js"; "Internal"; "meth" ] | [ "Js_internalOO"; "meth" ]),
294-
[
295-
{
296-
dependencies = argsDependencies;
297-
type_ =
298-
Variant
299-
{ payloads = [ { case = { label = "Arity_1" }; t = type_ } ] };
300-
};
301-
ret;
302-
] ) ->
303-
{
304-
dependencies = argsDependencies @ ret.dependencies;
305-
type_ =
306-
Function
307-
{
308-
argTypes = [ { aName = ""; aType = type_ } ];
309-
componentName = None;
310-
retType = ret.type_;
311-
typeVars = [];
312-
uncurried = true;
313-
};
314-
}
315-
| ( ([ "Js"; "Internal"; "meth" ] | [ "Js_internalOO"; "meth" ]),
316-
[
317-
{
318-
dependencies = argsDependencies;
319-
type_ = Variant { payloads = [ { t = Tuple ts } ] };
320-
};
321-
ret;
322-
] ) ->
323-
{
324-
dependencies = argsDependencies @ ret.dependencies;
325-
type_ =
326-
Function
327-
{
328-
argTypes =
329-
ts |> List.map (fun type_ -> { aName = ""; aType = type_ });
330-
componentName = None;
331-
retType = ret.type_;
332-
typeVars = [];
333-
uncurried = true;
334-
};
335-
}
336216
| _ -> defaultCase ()
337217

338218
type processVariant = {

jscomp/gentype_tests/typescript-react-example/package-lock.json

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)