Skip to content

Commit f41f93a

Browse files
committed
New mechanism to determine arity of externals.
The arity of external declarations is currently determined just based on the untyped parse tree. This is probably because the "real" arity is ambiguous in curried mode, and the convention is that an explicit type of the form `t1 => t2 => t3` determines arity 2, but type aliases are ignored. Here's an example: https://rescript-lang.org/try?version=v11.1.2&code=C4TwDgpgBArlC8UAUBLAdsANFdwCUCAfDhgFAACAbgIYA2UEAHsBAE5p1SUCMAXLAigAiSkKgB6cVGqsUoKADNWAewC2AlAGccqsLRQBjORRr0mLdp0oAmfqgzZcBeMVyCRYydNnytDRnqGxqS0EMCK3IKMRFzcSIyYjHghYYrWUTE28YnJQA In the example ```res type u = (int, int) => int @Val external v1: u = "v" // arity from u is implicit @Val external v2: (int, int) => int = "v" // arity is explicit let f1 = x => v1(x,x) let f2 = x => v2(x,x) ``` we have that `v2` is determined to have arity 2, but the arity of `v1` is not determined. As a consequence, the code generated for `f1` uses runtime `Curry`, both in curried an uncurried mode. This PR uses the actual type coming from the type checker when in uncurried mode, as that does not suffer from ambiguity. The consequences are that the external behaves the same however the type is defined, and in the example, no `Curry` runtime is generated.
1 parent 18a727a commit f41f93a

16 files changed

+79
-98
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- Allow free vars in types for type coercion `e :> t`. https://github.com/rescript-lang/rescript-compiler/pull/6828
2121
- Allow `private` in with constraints. https://github.com/rescript-lang/rescript-compiler/pull/6843
2222
- Add regex literals as syntax sugar for `@bs.re`. https://github.com/rescript-lang/rescript-compiler/pull/6776
23+
- Improved mechanism to determine arity of externals, which is consistent however the type is written. https://github.com/rescript-lang/rescript-compiler/pull/6874
2324

2425
#### :boom: Breaking Change
2526

jscomp/build_tests/react_ppx/src/gpr_3695_test.bs.js

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

jscomp/frontend/external_ffi_types.ml

+1-8
Original file line numberDiff line numberDiff line change
@@ -240,14 +240,7 @@ let from_string s : t =
240240

241241
let () =
242242
Primitive.coerce :=
243-
fun ({
244-
prim_name;
245-
prim_arity;
246-
prim_native_name;
247-
prim_alloc = _;
248-
prim_native_repr_args = _;
249-
prim_native_repr_res = _;
250-
} :
243+
fun ({prim_name; prim_arity; prim_native_name; prim_alloc = _} :
251244
Primitive.description) (p2 : Primitive.description) ->
252245
let p2_native = p2.prim_native_name in
253246
prim_name = p2.prim_name && prim_arity = p2.prim_arity

jscomp/gentype_tests/typescript-react-example/src/ImportHookDefault.res.js

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

jscomp/gentype_tests/typescript-react-example/src/ImportHooks.res.js

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

jscomp/gentype_tests/typescript-react-example/src/ImportIndex.res.js

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

jscomp/gentype_tests/typescript-react-example/src/JSXV4.res.js

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

jscomp/gentype_tests/typescript-react-example/src/MyInput.res.js

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

jscomp/ml/primitive.ml

+7-26
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,32 @@ open Parsetree
2020

2121
type boxed_integer = Pbigint | Pint32 | Pint64
2222

23-
type native_repr =
24-
| Same_as_ocaml_repr
25-
2623
type description =
2724
{ prim_name: string; (* Name of primitive or C function *)
2825
prim_arity: int; (* Number of arguments *)
2926
prim_alloc: bool; (* Does it allocates or raise? *)
3027
prim_native_name: string; (* Name of C function for the nat. code gen. *)
31-
prim_native_repr_args: native_repr list;
32-
prim_native_repr_res: native_repr }
28+
}
3329

3430
let coerce : (description -> description -> bool) ref =
3531
ref (fun
3632
(p1 : description) (p2 : description) ->
3733
p1 = p2
3834
)
3935

40-
41-
42-
let rec make_native_repr_args arity x =
43-
if arity = 0 then
44-
[]
45-
else
46-
x :: make_native_repr_args (arity - 1) x
47-
4836
let simple ~name ~arity ~alloc =
4937
{prim_name = name;
5038
prim_arity = arity;
5139
prim_alloc = alloc;
52-
prim_native_name = "";
53-
prim_native_repr_args = make_native_repr_args arity Same_as_ocaml_repr;
54-
prim_native_repr_res = Same_as_ocaml_repr}
40+
prim_native_name = "";}
5541

56-
let make ~name ~alloc ~native_name ~native_repr_args ~native_repr_res =
42+
let make ~name ~alloc ~native_name ~arity =
5743
{prim_name = name;
58-
prim_arity = List.length native_repr_args;
44+
prim_arity = arity;
5945
prim_alloc = alloc;
60-
prim_native_name = native_name;
61-
prim_native_repr_args = native_repr_args;
62-
prim_native_repr_res = native_repr_res}
46+
prim_native_name = native_name;}
6347

64-
let parse_declaration valdecl ~native_repr_args ~native_repr_res =
65-
let arity = List.length native_repr_args in
48+
let parse_declaration valdecl ~arity =
6649
let name, native_name =
6750
match valdecl.pval_prim with
6851
| name :: name2 :: _ -> (name, name2)
@@ -73,9 +56,7 @@ let parse_declaration valdecl ~native_repr_args ~native_repr_res =
7356
{prim_name = name;
7457
prim_arity = arity;
7558
prim_alloc = true;
76-
prim_native_name = native_name;
77-
prim_native_repr_args = native_repr_args;
78-
prim_native_repr_res = native_repr_res}
59+
prim_native_name = native_name;}
7960

8061
open Outcometree
8162

jscomp/ml/primitive.mli

+3-11
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,12 @@
1717

1818
type boxed_integer = Pbigint | Pint32 | Pint64
1919

20-
(* Representation of arguments/result for the native code version
21-
of a primitive *)
22-
type native_repr =
23-
| Same_as_ocaml_repr
24-
2520
type description = private
2621
{ prim_name: string; (* Name of primitive or C function *)
2722
prim_arity: int; (* Number of arguments *)
2823
prim_alloc: bool; (* Does it allocates or raise? *)
2924
prim_native_name: string; (* Name of C function for the nat. code gen. *)
30-
prim_native_repr_args: native_repr list;
31-
prim_native_repr_res: native_repr }
25+
}
3226

3327
(* Invariant [List.length d.prim_native_repr_args = d.prim_arity] *)
3428

@@ -42,14 +36,12 @@ val make
4236
: name:string
4337
-> alloc:bool
4438
-> native_name:string
45-
-> native_repr_args: native_repr list
46-
-> native_repr_res: native_repr
39+
-> arity: int
4740
-> description
4841

4942
val parse_declaration
5043
: Parsetree.value_description
51-
-> native_repr_args:native_repr list
52-
-> native_repr_res:native_repr
44+
-> arity: int
5345
-> description
5446

5547
val print

jscomp/ml/translcore.ml

+2-5
Original file line numberDiff line numberDiff line change
@@ -851,9 +851,7 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
851851
)
852852
| _ -> "#fn_mk" in
853853
let prim =
854-
Primitive.make ~name ~alloc:true ~native_name:arity_s
855-
~native_repr_args:[ Same_as_ocaml_repr ]
856-
~native_repr_res:Same_as_ocaml_repr
854+
Primitive.make ~name ~alloc:true ~native_name:arity_s ~arity:1
857855
in
858856
Lprim
859857
( Pccall prim
@@ -1188,8 +1186,7 @@ and transl_record loc env fields repres opt_init_expr =
11881186
let arity_s = String.sub lbl_name 1 (String.length lbl_name - 1) in
11891187
let prim =
11901188
Primitive.make ~name:"#fn_mk" ~alloc:true ~native_name:arity_s
1191-
~native_repr_args:[ Same_as_ocaml_repr ]
1192-
~native_repr_res:Same_as_ocaml_repr
1189+
~arity:1
11931190
in
11941191
Lprim
11951192
( Pccall prim

jscomp/ml/typedecl.ml

+15-31
Original file line numberDiff line numberDiff line change
@@ -1707,28 +1707,19 @@ let transl_exception env sext =
17071707

17081708

17091709

1710-
let rec parse_native_repr_attributes env core_type ty =
1710+
let rec arity_from_arrow_type env core_type ty =
17111711
match core_type.ptyp_desc, (Ctype.repr ty).desc
17121712
with
17131713
| Ptyp_arrow (_, _, ct2), Tarrow (_, _, t2, _) ->
1714-
let repr_arg = Same_as_ocaml_repr in
1715-
let repr_args, repr_res =
1716-
parse_native_repr_attributes env ct2 t2
1717-
in
1718-
(repr_arg :: repr_args, repr_res)
1714+
1 + (arity_from_arrow_type env ct2 t2)
17191715
| Ptyp_arrow _, _ | _, Tarrow _ -> assert false
1720-
| _ -> ([], Same_as_ocaml_repr)
1716+
| _ -> 0
17211717

17221718

1723-
let parse_native_repr_attributes env core_type ty =
1724-
match core_type.ptyp_desc, (Ctype.repr ty).desc
1725-
with
1726-
| Ptyp_constr ({txt = Lident "function$"}, [{ptyp_desc = Ptyp_arrow (_, _, ct2)}; _]),
1727-
Tconstr (Pident {name = "function$"},[{desc = Tarrow (_, _, t2, _)}; _],_) ->
1728-
let repr_args, repr_res = parse_native_repr_attributes env ct2 t2 in
1729-
let native_repr_args = Same_as_ocaml_repr :: repr_args in
1730-
(native_repr_args, repr_res)
1731-
| _ -> parse_native_repr_attributes env core_type ty
1719+
let parse_arity env core_type ty =
1720+
match Ast_uncurried.uncurried_type_get_arity_opt ~env ty with
1721+
| Some arity -> arity
1722+
| None -> arity_from_arrow_type env core_type ty
17321723

17331724
(* Translate a value declaration *)
17341725
let transl_value_decl env loc valdecl =
@@ -1742,30 +1733,23 @@ let transl_value_decl env loc valdecl =
17421733
| [] ->
17431734
raise (Error(valdecl.pval_loc, Val_in_structure))
17441735
| _ ->
1745-
let native_repr_args, native_repr_res =
1746-
let rec scann (attrs : Parsetree.attributes) =
1736+
let arity =
1737+
let rec scan_attributes (attrs : Parsetree.attributes) =
17471738
match attrs with
1748-
| ({txt = "internal.arity";_},
1739+
| ({txt = "internal.arity";_}, (* This is likely not needed in uncurried mode *)
17491740
PStr [ {pstr_desc = Pstr_eval
17501741
(
17511742
({pexp_desc = Pexp_constant (Pconst_integer (i,_))} :
17521743
Parsetree.expression) ,_)}]) :: _ ->
17531744
Some (int_of_string i)
1754-
| _ :: rest -> scann rest
1745+
| _ :: rest -> scan_attributes rest
17551746
| [] -> None
1756-
and make n =
1757-
if n = 0 then []
1758-
else Primitive.Same_as_ocaml_repr :: make (n - 1)
17591747
in
1760-
match scann valdecl.pval_attributes with
1761-
| None -> parse_native_repr_attributes env valdecl.pval_type ty
1762-
| Some x -> make x , Primitive.Same_as_ocaml_repr
1763-
in
1764-
let prim =
1765-
Primitive.parse_declaration valdecl
1766-
~native_repr_args
1767-
~native_repr_res
1748+
match scan_attributes valdecl.pval_attributes with
1749+
| None -> parse_arity env valdecl.pval_type ty
1750+
| Some x -> x
17681751
in
1752+
let prim = Primitive.parse_declaration valdecl ~arity in
17691753
let prim_native_name = prim.prim_native_name in
17701754
if prim.prim_arity = 0 &&
17711755
not ( String.length prim_native_name >= 20 &&

jscomp/test/ExternalArity.js

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

jscomp/test/ExternalArity.res

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@@uncurried
2+
3+
type u = (int, int) => int
4+
@val external v1: u = "v" // arity from u is implicit
5+
@val external v2: (int, int) => int = "v" // arity is explicit
6+
7+
let f1 = x => v1(x,x)
8+
let f2 = x => v2(x,x)

jscomp/test/bs_splice_partial.js

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

jscomp/test/build.ninja

+2-1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)