Skip to content

Commit 0196f60

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 124bc4b commit 0196f60

51 files changed

Lines changed: 1144 additions & 1043 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
@@ -30,6 +30,7 @@
3030
- Fix argument evaluation order when a function call is inlined: the beta reducer stacked argument bindings in reverse parameter order, so the last argument was evaluated first when arguments could not be substituted directly. https://github.com/rescript-lang/rescript/pull/8572
3131
- Preserve parentheses around multiplication, division, and modulo expressions used as exponents. https://github.com/rescript-lang/rescript/pull/8550
3232
- 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
33+
- 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
3334
- 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
3435
- 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
3536
- Preserve multibyte characters when wrapping long source lines in compiler code frames. https://github.com/rescript-lang/rescript/pull/8520
@@ -46,6 +47,7 @@
4647

4748
- 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
4849
- 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
50+
- 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
4951
- 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
5052
- 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
5153
- 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: 9 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -530,31 +530,6 @@ module Function_table = struct
530530
| exception Not_found -> None
531531
end
532532

533-
module Find_functions_called = struct
534-
let traverse_expr ~callees =
535-
let super = Tast_mapper.default in
536-
let expr (self : Tast_mapper.mapper) (e : Typedtree.expression) =
537-
(match e.exp_desc with
538-
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)}} ->
539-
let function_name = Path.name callee in
540-
callees := !callees |> String_set.add function_name
541-
| _ -> ());
542-
super.expr self e
543-
in
544-
{super with Tast_mapper.expr}
545-
546-
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
556-
end
557-
558533
module Extend_function_table = struct
559534
(* Add functions passed a recursive function via a labeled argument,
560535
and functions calling progress functions, to the function table. *)
@@ -592,39 +567,10 @@ module Extend_function_table = struct
592567
if args |> List.for_all check_arg then Some (path, loc) else None
593568
| _ -> None
594569

595-
let traverse_expr ~config ~function_table ~progress_functions
596-
~value_bindings_table =
570+
let traverse_expr ~config ~function_table =
597571
let super = Tast_mapper.default in
598572
let expr (self : Tast_mapper.mapper) (e : Typedtree.expression) =
599573
(match e.exp_desc with
600-
| Texp_ident (callee, _, _) -> (
601-
let loc = e.exp_loc in
602-
match Hashtbl.find_opt value_bindings_table (Path.name callee) with
603-
| None -> ()
604-
| Some (id_pos, _, callees) ->
605-
if
606-
not
607-
(String_set.is_empty
608-
(String_set.inter (Lazy.force callees) progress_functions))
609-
then
610-
let function_name = Path.name callee in
611-
if
612-
not
613-
(callee
614-
|> Function_table.is_in_function_in_table ~function_table)
615-
then (
616-
function_table |> Function_table.add_function ~function_name;
617-
if config.Dce_config.cli.debug then
618-
Log_.warning ~for_stats:false ~loc
619-
(Termination
620-
{
621-
termination = TerminationAnalysisInternal;
622-
message =
623-
Format.asprintf
624-
"Extend Function Table with @{<info>%s@} (%a) as it \
625-
calls a progress function"
626-
function_name print_pos id_pos;
627-
})))
628574
| Texp_apply {funct = {exp_desc = Texp_ident (callee, _, _)}; args}
629575
when callee |> Function_table.is_in_function_in_table ~function_table ->
630576
let function_name = Path.name callee in
@@ -654,12 +600,8 @@ module Extend_function_table = struct
654600
in
655601
{super with Tast_mapper.expr}
656602

657-
let run ~config ~function_table ~progress_functions ~value_bindings_table
658-
(expression : Typedtree.expression) =
659-
let traverse_expr =
660-
traverse_expr ~config ~function_table ~progress_functions
661-
~value_bindings_table
662-
in
603+
let run ~config ~function_table (expression : Typedtree.expression) =
604+
let traverse_expr = traverse_expr ~config ~function_table in
663605
expression |> traverse_expr.expr traverse_expr |> ignore
664606
end
665607

@@ -696,7 +638,7 @@ module Check_expression_well_formed = struct
696638
match
697639
Hashtbl.find_opt value_bindings_table function_name
698640
with
699-
| Some (_pos, (body : Typedtree.expression), _)
641+
| Some (_pos, (body : Typedtree.expression))
700642
when path
701643
|> Function_table.is_in_function_in_table
702644
~function_table ->
@@ -937,7 +879,7 @@ module Compile = struct
937879
let open Command in
938880
c +++ ConstrOption Rnone
939881
| _ -> c)
940-
| Texp_function {case = case_} -> case ~ctx case_
882+
| Texp_function {body} -> body |> expression ~ctx
941883
| Texp_match (e, cases_ok, cases_exn, _partial)
942884
when not
943885
(cases_exn
@@ -1327,11 +1269,8 @@ let traverse_ast ~config ~value_bindings_table =
13271269
|> List.iter (fun (vb : Typedtree.value_binding) ->
13281270
match vb.vb_pat.pat_desc with
13291271
| Tpat_var (id, {loc = {loc_start = pos}}) ->
1330-
let callees =
1331-
lazy (Find_functions_called.find_callees vb.vb_expr)
1332-
in
13331272
Hashtbl.replace value_bindings_table (Ident.name id)
1334-
(pos, vb.vb_expr, callees)
1273+
(pos, vb.vb_expr)
13351274
| _ -> ());
13361275
let progress_functions, functions_to_analyze =
13371276
if rec_flag = Asttypes.Nonrecursive then (String_set.empty, [])
@@ -1378,7 +1317,7 @@ let traverse_ast ~config ~value_bindings_table =
13781317
|> List.fold_left
13791318
(fun acc function_name ->
13801319
match Hashtbl.find_opt value_bindings_table function_name with
1381-
| Some (_pos, e, _set) -> (function_name, e) :: acc
1320+
| Some (_pos, e) -> (function_name, e) :: acc
13821321
| None -> acc)
13831322
[]
13841323
|> List.rev
@@ -1388,9 +1327,7 @@ let traverse_ast ~config ~value_bindings_table =
13881327
function_table |> Function_table.add_function ~function_name);
13891328
recursive_definitions
13901329
|> List.iter (fun (_, body) ->
1391-
body
1392-
|> Extend_function_table.run ~config ~function_table
1393-
~progress_functions ~value_bindings_table);
1330+
body |> Extend_function_table.run ~config ~function_table);
13941331
recursive_definitions
13951332
|> List.iter (fun (_, body) ->
13961333
body
@@ -1405,7 +1342,7 @@ let traverse_ast ~config ~value_bindings_table =
14051342
if function_definition.body = None then
14061343
match Hashtbl.find_opt value_bindings_table function_name with
14071344
| None -> ()
1408-
| Some (_pos, body, _) ->
1345+
| Some (_pos, body) ->
14091346
function_table
14101347
|> Function_table.add_body
14111348
~body:

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

0 commit comments

Comments
 (0)