Skip to content

Commit 4c9e95f

Browse files
cristianocclaude
andcommitted
Make the typed layers n-ary: Tarrow params and Texp_function params
Types.Tarrow carries a parameter list (Tarrow of arg list * type_expr); Texp_function carries typed parameters {fp_lbl; fp_param; fp_pat; fp_partial} and a body; Ttyp_arrow and Otyp_arrow follow. The arity annotation and its int-option phantom state are gone from the compiler. Type relations compare parameters pairwise; a length mismatch is structural incompatibility (which also makes mcomp's arrow verdict sound: arrows of different lengths can never unify). filter_arrow becomes filter_arrow_n. type_function types all parameters against one arrow, checking expected labels up front to preserve the dedicated Abstract_wrong_label diagnostics; optional-parameter defaults desugar to uniquified *opt_<label>* bindings stacked at the head of the body. type_application is a single parameters-to-arguments matching loop preserving the legacy commutation, optional auto-fill, eta-expansion placeholder, and error-selection behavior. translcore's push_defaults is deleted (defaults now sit in the body by construction) and transl_function walks the parameter list, keeping the active-pattern split. Downstream, the gather-until-arity walkers in gentype and the outcome printer, reanalyze's two arity-corrective helpers, and typedecl's structural arity fallback are all deleted. The cmi and cmt magic numbers are bumped to Caml1999I023/Caml1999T023. Generated JavaScript is byte-identical across the test suite except: - a bug fix: defaults of optional parameters in curried functions are now computed when their own parameter group is applied ((~x=d, y) => (~z=d, w) => ... no longer defers x's default to the inner application); pinned by the uncurried_default.args snapshot; - optional-parameter internals are named *opt_<label>* instead of *opt* in the one unprettified case (mario_game). Error-message improvements: method arity mismatches report unlabelled argument counts precisely, and missing-argument lists print in source order. Reanalyze no longer emits spurious empty optional-argument references; genType recovers real parameter names after defaulted parameters. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Cristiano Calcagno <cristianoc@users.noreply.github.com>
1 parent 5e23370 commit 4c9e95f

48 files changed

Lines changed: 1063 additions & 981 deletions

Some content is hidden

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

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
- Preserve parentheses around multiplication, division, and modulo expressions used as exponents. https://github.com/rescript-lang/rescript/pull/8550
3131
- Enforce function arity in interface/module inclusion and type coercion. Previously a curried implementation (e.g. `int => int => int`) could satisfy an uncurried interface (`(int, int) => int`) or be coerced to it, which could miscompile calls made through the interface type. Such mismatches are now compile errors with an explanatory hint. https://github.com/rescript-lang/rescript/pull/8559
32+
- Fix default values of optional parameters being computed at the wrong time for curried functions: in `(~x=default, y) => (~z=default, w) => ...`, `x`'s default was only computed when the *inner* function was applied. Each default is now computed when its own parameter group is applied. https://github.com/rescript-lang/rescript/pull/8568
3233
- Fix bare labeled arrow types (`~x: int => string`) getting no arity: they printed identically to their parenthesized form (`(~x: int) => string`) but did not unify with it. https://github.com/rescript-lang/rescript/pull/8563
3334
- Fix losses of fidelity when code passes through an external PPX: the internal `@res.async` marker no longer leaks into the program, attributes on an arrow type or on an `await` expression are no longer dropped or relocated (previously this could crash the formatter), JSX elements keep their closing tag, and PPX-emitted OCaml-style `function` is desugared instead of crashing the compiler. https://github.com/rescript-lang/rescript/pull/8561
3435
- Preserve multibyte characters when wrapping long source lines in compiler code frames. https://github.com/rescript-lang/rescript/pull/8520
@@ -45,6 +46,7 @@
4546

4647
- Sync the platform npm package's compiler binaries (`packages/@rescript/<platform>/bin`) via dune promotion on every `dune build`, instead of Makefile/CI copy steps that only ran when make did: a plain `dune build` can no longer leave `cli/*.js` and the test harnesses running a stale compiler. https://github.com/rescript-lang/rescript/pull/8560
4748
- Remove unused compiler IR definitions, modules, helpers, error variants, and Typedtree fields. https://github.com/rescript-lang/rescript/pull/8551 https://github.com/rescript-lang/rescript/pull/8555
49+
- Make the typed layers n-ary as well: `Types.Tarrow` carries a parameter list, `Texp_function` carries typed parameters (label, ident, pattern, per-parameter exhaustiveness) and a body, and `Ttyp_arrow`/`Otyp_arrow` follow. The `arity` annotation and its `int option` phantom state are gone from the compiler entirely; `push_defaults` in translcore and the hand-rolled gather-until-arity walks in gentype, reanalyze, and the outcome printer are deleted. The cmi and cmt magic numbers are bumped (`Caml1999I023`/`Caml1999T023`). Generated JavaScript is byte-identical across the test suite (optional-parameter internals are named `*opt_<label>*` instead of `*opt*`, visible only in the rare unprettified case); reanalyze no longer emits spurious empty optional-argument references, and genType recovers real parameter names after defaulted parameters. https://github.com/rescript-lang/rescript/pull/8568
4850
- Make functions and arrow types n-ary in the parsetree: `Pexp_fun` carries a parameter list and `Ptyp_arrow` a parameter list, replacing the curried one-parameter-per-node chains with an `arity` annotation on the head. Arity is now structural (`List.length params`) and `ast_uncurried.ml` is deleted. The typed layers, cmt format, printed output, and the external-PPX wire format are unchanged. Generated JavaScript is unchanged with one deliberate exception: `@this this => async arg => ...` now means what it says (a method returning an async function) instead of absorbing the nested parameter into the method; write `@this async (this, arg) => ...` for the old meaning. https://github.com/rescript-lang/rescript/pull/8566
4951
- Give marshaled current-parsetree streams (`-as-pp`, `res_parser -print binary`) their own magic numbers, distinct from the frozen Parsetree0 wire format used for external PPXes. https://github.com/rescript-lang/rescript/pull/8561
5052
- Record the written parameter count in parsed arrow arity for externals with phantom `@as(...) _` arguments. External processing recounts after erasing phantoms, so the parser no longer needs to pre-decrement the arity or the printer to compensate for it. https://github.com/rescript-lang/rescript/pull/8563

analysis/reanalyze/src/arnold.ml

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -544,15 +544,12 @@ module Find_functions_called = struct
544544
{super with Tast_mapper.expr}
545545

546546
let find_callees (expression : Typedtree.expression) =
547-
let is_function =
548-
match expression.exp_desc with
549-
| Texp_function {arity = None} -> true
550-
| _ -> false
551-
in
552-
let callees = ref String_set.empty in
553-
let traverse_expr = traverse_expr ~callees in
554-
if is_function then expression |> traverse_expr.expr traverse_expr |> ignore;
555-
!callees
547+
(* Legacy behavior: callees were only collected for arity-less (curried)
548+
function nodes, which compiler-produced bindings never were once
549+
functions became uncurried by default, and which no longer exist at
550+
all with the n-ary representation. *)
551+
ignore expression;
552+
String_set.empty
556553
end
557554

558555
module Extend_function_table = struct
@@ -937,7 +934,7 @@ module Compile = struct
937934
let open Command in
938935
c +++ ConstrOption Rnone
939936
| _ -> c)
940-
| Texp_function {case = case_} -> case ~ctx case_
937+
| Texp_function {body} -> body |> expression ~ctx
941938
| Texp_match (e, cases_ok, cases_exn, _partial)
942939
when not
943940
(cases_exn

analysis/reanalyze/src/dead_optional_args.ml

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ let active () = true
55
let rec has_optional_args (texpr : Types.type_expr) =
66
match texpr.desc with
77
| _ when not (active ()) -> false
8-
| Tarrow ({lbl = Optional _}, _tTo, _) -> true
9-
| Tarrow (_, t_to, _) -> has_optional_args t_to
8+
| Tarrow (params, _) ->
9+
params
10+
|> List.exists (fun ({lbl} : Types.arg) ->
11+
match lbl with
12+
| Optional _ -> true
13+
| _ -> false)
1014
| Tlink t -> has_optional_args t
1115
| Tsubst t -> has_optional_args t
1216
| _ -> false
@@ -25,34 +29,21 @@ let add_function_reference ~config ~cross_file ~(loc_from : Location.t)
2529
(pos_to |> Pos.to_string);
2630
Cross_file_items.add_function_reference cross_file ~pos_from ~pos_to)
2731

32+
(* The function boundary is structural: a function's optional arguments are
33+
exactly the optional parameters of its (one) arrow node. *)
2834
let rec from_type_expr (texpr : Types.type_expr) =
2935
match texpr.desc with
3036
| _ when not (active ()) -> []
31-
| Tarrow ({lbl = Optional {txt = s}}, t_to, _) -> s :: from_type_expr t_to
32-
| Tarrow (_, t_to, _) -> from_type_expr t_to
37+
| Tarrow (params, _) ->
38+
params
39+
|> List.filter_map (fun ({lbl} : Types.arg) ->
40+
match lbl with
41+
| Optional {txt = s} -> Some s
42+
| _ -> None)
3343
| Tlink t -> from_type_expr t
3444
| Tsubst t -> from_type_expr t
3545
| _ -> []
3646

37-
let rec from_type_expr_with_arity (texpr : Types.type_expr) arity =
38-
if arity <= 0 then []
39-
else
40-
match texpr.desc with
41-
| _ when not (active ()) -> []
42-
| Tarrow ({lbl = Optional {txt = s}}, t_to, _) ->
43-
s :: from_type_expr_with_arity t_to (arity - 1)
44-
| Tarrow (_, t_to, _) -> from_type_expr_with_arity t_to (arity - 1)
45-
| Tlink t -> from_type_expr_with_arity t arity
46-
| Tsubst t -> from_type_expr_with_arity t arity
47-
| _ -> []
48-
49-
let rec from_type_expr_with_declared_arity (texpr : Types.type_expr) =
50-
match texpr.desc with
51-
| Tarrow (_, _, Some arity) -> from_type_expr_with_arity texpr arity
52-
| Tlink t -> from_type_expr_with_declared_arity t
53-
| Tsubst t -> from_type_expr_with_declared_arity t
54-
| _ -> from_type_expr texpr
55-
5647
let add_references ~config ~cross_file ~(loc_from : Location.t)
5748
~(loc_to : Location.t) ~(binding : Location.t) ~path
5849
(arg_names, arg_names_maybe) =

analysis/reanalyze/src/dead_value.ml

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ let collect_value_binding ~config ~decls ~file ~(current_binding : Location.t)
4444
let name = Ident.name id |> Name.create ~is_interface:false in
4545
let optional_args, reports_optional_args =
4646
match vb.vb_expr.exp_desc with
47-
| Texp_function {arity = Some arity; _} ->
48-
( vb.vb_expr.exp_type
49-
|> (fun texpr ->
50-
Dead_optional_args.from_type_expr_with_arity texpr arity)
51-
|> Optional_args.from_list,
52-
true )
5347
| Texp_function _ ->
5448
( vb.vb_expr.exp_type |> Dead_optional_args.from_type_expr
5549
|> Optional_args.from_list,
@@ -210,18 +204,12 @@ let rec collect_expr ~config ~refs ~file_deps ~cross_file ~direct_callees
210204
exp_desc =
211205
Texp_function
212206
{
213-
case =
207+
params = [{fp_pat = {pat_desc = Tpat_var (eta_arg, _)}}];
208+
body =
214209
{
215-
c_lhs = {pat_desc = Tpat_var (eta_arg, _)};
216-
c_rhs =
217-
{
218-
exp_desc =
219-
Texp_apply
220-
{
221-
funct = {exp_desc = Texp_ident (id_arg2, _, _)};
222-
args;
223-
};
224-
};
210+
exp_desc =
211+
Texp_apply
212+
{funct = {exp_desc = Texp_ident (id_arg2, _, _)}; args};
225213
};
226214
};
227215
} )
@@ -397,7 +385,7 @@ let rec process_signature_item ~config ~decls ~file ~do_types ~do_values
397385
in
398386
if (not is_primitive) || !Config.analyze_externals then
399387
let optional_args =
400-
val_type |> Dead_optional_args.from_type_expr_with_declared_arity
388+
val_type |> Dead_optional_args.from_type_expr
401389
|> Optional_args.from_list
402390
in
403391
let reports_optional_args =

analysis/src/completion_back_end.ml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,12 +1093,19 @@ and get_completions_for_context_path ~state ~debug ~full ~opens ~raw_opens ~pos
10931093
~pos
10941094
with
10951095
| Some ((TypeExpr typ | ExtractedType (Tfunction {typ})), env) -> (
1096-
let rec reconstruct_function_type args t_ret =
1096+
let reconstruct_function_type args t_ret =
10971097
match args with
10981098
| [] -> t_ret
1099-
| (label, t_arg) :: rest ->
1100-
let rest_type = reconstruct_function_type rest t_ret in
1101-
{typ with desc = Tarrow ({lbl = label; typ = t_arg}, rest_type, None)}
1099+
| args ->
1100+
{
1101+
typ with
1102+
desc =
1103+
Tarrow
1104+
( List.map
1105+
(fun (label, t_arg) -> {Types.lbl = label; typ = t_arg})
1106+
args,
1107+
t_ret );
1108+
}
11021109
in
11031110
let rec process_apply args labels =
11041111
match (args, labels) with

analysis/src/completion_jsx.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,15 @@ let get_jsx_labels ~component_path ~find_type_of_value ~package ~state =
246246
| Some (path, type_args) -> get_fields ~path ~type_args
247247
| None -> [])
248248
| Tarrow
249-
({lbl = Nolabel; typ = {desc = Tconstr (path, type_args, _)}}, _, _)
249+
({lbl = Nolabel; typ = {desc = Tconstr (path, type_args, _)}} :: _, _)
250250
when Path.last path = "props" ->
251251
get_fields ~path ~type_args
252252
| Tconstr (cl_path, [{desc = Tconstr (path, type_args, _)}; _], _)
253253
when Path.name cl_path = "React.componentLike"
254254
&& Path.last path = "props" ->
255255
(* JSX V4 external or interface *)
256256
get_fields ~path ~type_args
257-
| Tarrow ({lbl = Nolabel; typ}, _, _) -> (
257+
| Tarrow ({lbl = Nolabel; typ} :: _, _) -> (
258258
(* Component without the JSX PPX, like a make fn taking a hand-written
259259
type props. *)
260260
let rec dig_to_constr typ =

analysis/src/create_interface.ml

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,8 @@ let print_signature ~extractor ~signature =
121121
in
122122
match typ.desc with
123123
| Tarrow
124-
( {typ = {desc = Tconstr (Path.Pident props_id, type_args, _)}},
125-
ret_type,
126-
_ )
124+
( {typ = {desc = Tconstr (Path.Pident props_id, type_args, _)}} :: _,
125+
ret_type )
127126
when Ident.name props_id = "props" ->
128127
Some (type_args, ret_type)
129128
| Tconstr
@@ -159,24 +158,24 @@ let print_signature ~extractor ~signature =
159158
| Some x -> x
160159
| None -> assert false
161160
in
162-
let rec mk_fun_type (label_decls : Types.label_declaration list) =
163-
match label_decls with
164-
| [] -> ret_type
165-
| label_decl :: rest ->
166-
let prop_type =
167-
Type_utils.instantiate_type ~type_params ~type_args
168-
label_decl.ld_type
169-
in
170-
let lbl_name = label_decl.ld_id |> Ident.name in
171-
let lbl =
172-
if label_decl.ld_optional then
173-
Asttypes.Optional {txt = lbl_name; loc = Location.none}
174-
else Asttypes.Labelled {txt = lbl_name; loc = Location.none}
175-
in
176-
{
177-
ret_type with
178-
desc = Tarrow ({lbl; typ = prop_type}, mk_fun_type rest, None);
179-
}
161+
let mk_fun_type (label_decls : Types.label_declaration list) =
162+
let params =
163+
label_decls
164+
|> List.map (fun (label_decl : Types.label_declaration) ->
165+
let prop_type =
166+
Type_utils.instantiate_type ~type_params ~type_args
167+
label_decl.ld_type
168+
in
169+
let lbl_name = label_decl.ld_id |> Ident.name in
170+
let lbl =
171+
if label_decl.ld_optional then
172+
Asttypes.Optional {txt = lbl_name; loc = Location.none}
173+
else
174+
Asttypes.Labelled {txt = lbl_name; loc = Location.none}
175+
in
176+
{Types.lbl; typ = prop_type})
177+
in
178+
{ret_type with desc = Tarrow (params, ret_type)}
180179
in
181180
let fun_type =
182181
if List.length label_decls = 0 (* No props *) then
@@ -185,7 +184,7 @@ let print_signature ~extractor ~signature =
185184
in
186185
{
187186
ret_type with
188-
desc = Tarrow ({lbl = Nolabel; typ = t_unit}, ret_type, None);
187+
desc = Tarrow ([{Types.lbl = Nolabel; typ = t_unit}], ret_type);
189188
}
190189
else mk_fun_type label_decls
191190
in

analysis/src/process_cmt.ml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,12 +723,7 @@ and scan_let_modules ~env (e : Typedtree.expression) =
723723
in
724724
List.iter scan_case cases;
725725
List.iter scan_case exn_cases
726-
| Texp_function {case; _} ->
727-
let {Typedtree.c_lhs = _; c_guard; c_rhs} = case in
728-
(match c_guard with
729-
| Some g -> scan_let_modules ~env g
730-
| None -> ());
731-
scan_let_modules ~env c_rhs
726+
| Texp_function {body; _} -> scan_let_modules ~env body
732727
| Texp_try (e, cases) ->
733728
scan_let_modules ~env e;
734729
cases

analysis/src/shared.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ let find_type_constructors (tel : Types.type_expr list) =
4848
| Tconstr (path, args, _) ->
4949
add_path path;
5050
args |> List.iter loop
51-
| Tarrow (arg, ret, _) ->
52-
loop arg.typ;
51+
| Tarrow (params, ret) ->
52+
List.iter (fun ({typ} : Types.arg) -> loop typ) params;
5353
loop ret
5454
| Ttuple tel -> tel |> List.iter loop
5555
| Tnil | Tvar _ | Tobject _ | Tfield _ | Tvariant _ | Tunivar _ | Tpackage _

0 commit comments

Comments
 (0)