Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#### :boom: Breaking Change

- Remove the deprecated `Js` namespace and its runtime modules. https://github.com/rescript-lang/rescript/pull/8531
- Correct the structured function details produced by `rescript-tools doc` and exposed by `RescriptTools.Docgen`: parameters now retain labels and optionality, nested functions, tuples, variables, and generic arguments retain their type structure, return types are identified correctly, and non-function values no longer receive fake function details. This changes the published docgen detail schema.

#### :eyeglasses: Spec Compliance

Expand All @@ -26,6 +27,12 @@
#### :bug: Bug fix

- Preserve parentheses around multiplication, division, and modulo expressions used as exponents. https://github.com/rescript-lang/rescript/pull/8550
- Make a function's locally abstract types (`(type t, x) => ...`) part of the function AST node instead of a chain of wrapper nodes. Fixes the formatter dropping the association of attributes with their `type` group (`(@attr type t, x, @attr2 type s, y)` used to print as `@attr @attr2` on the function) and comments written next to a type parameter migrating onto the following value parameter.
- Preserve trailing comments between the type and `=` in locally abstract value constraints (`let f: type a. t /* comment */ = value`).
- 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.
- 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.
- 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.
- 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.
- Preserve multibyte characters when wrapping long source lines in compiler code frames. https://github.com/rescript-lang/rescript/pull/8520
- Fix reanalyze optional-argument diagnostics for functions passed or returned as first-class values. https://github.com/rescript-lang/rescript/pull/8321

Expand All @@ -38,6 +45,14 @@
#### :house: Internal

- 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
- Make locally abstract value constraints (`let f: type a. t = value`) structural in the parsetree, remove the obsolete `Pexp_newtype` and `Texp_newtype` wrapper metadata, and keep the old encoding confined to the frozen external-PPX bridge. The CMT magic number is bumped to `Caml1999T024`.
- Eliminate the `Pjs_fn_make`/`Pjs_fn_make_unit` arity-adjustment primitives and the `unsafe_adjust_to_arity` machinery: with structural arity, functions are constructed at their final arity, so the enforcement layer (and the active-pattern currying split it compensated for) is deleted. Generated code improves: no adapter closures for patterns on mutable fields, better constant propagation and name preservation, and recursive modules whose members are plain functions compile statically without the runtime bootstrap.
- Cleanups enabled by structural arity: remove the unreachable `Too_many_arguments` error and the `?in_function` threading through the type checker that existed only to decorate it; remove the dead `function$`-vs-arrow unification bridge, `Ctype.arity`, and the unused parsetree arity helpers; deduplicate the analysis arrow-flattening helpers.

- 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.
- 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, generated JavaScript, printed output, and the external-PPX wire format are unchanged.
- 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.
- Make parsetree arrow arity honest for externals with phantom `@as(...) _` arguments: the parser no longer pre-decrements the head arity (external processing recounts after erasing phantoms), and the mirrored printer compensation is removed. The arity annotation now always equals the number of written parameters.
- Add the `-check-lam` compiler option, enable Lambda invariant checking in compiler tests, and remove build-profile-dependent checking. https://github.com/rescript-lang/rescript/pull/8534
- Replace `-bs-diagnose` with `-debug-ir` and make IR diagnostic artifacts deterministic, compilation-local, and easy to clean. https://github.com/rescript-lang/rescript/pull/8535
- Replace CPPO-based browser conditionals with Dune-selected native and playground compiler implementations. https://github.com/rescript-lang/rescript/pull/8541
Expand Down
17 changes: 7 additions & 10 deletions analysis/reanalyze/src/arnold.ml
Original file line number Diff line number Diff line change
Expand Up @@ -544,15 +544,12 @@ module Find_functions_called = struct
{super with Tast_mapper.expr}

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

module Extend_function_table = struct
Expand Down Expand Up @@ -937,7 +934,7 @@ module Compile = struct
let open Command in
c +++ ConstrOption Rnone
| _ -> c)
| Texp_function {case = case_} -> case ~ctx case_
| Texp_function {body} -> body |> expression ~ctx
| Texp_match (e, cases_ok, cases_exn, _partial)
when not
(cases_exn
Expand Down
37 changes: 14 additions & 23 deletions analysis/reanalyze/src/dead_optional_args.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ let active () = true
let rec has_optional_args (texpr : Types.type_expr) =
match texpr.desc with
| _ when not (active ()) -> false
| Tarrow ({lbl = Optional _}, _tTo, _) -> true
| Tarrow (_, t_to, _) -> has_optional_args t_to
| Tarrow (params, _) ->
params
|> List.exists (fun ({lbl} : Types.arg) ->
match lbl with
| Optional _ -> true
| _ -> false)
| Tlink t -> has_optional_args t
| Tsubst t -> has_optional_args t
| _ -> false
Expand All @@ -25,34 +29,21 @@ let add_function_reference ~config ~cross_file ~(loc_from : Location.t)
(pos_to |> Pos.to_string);
Cross_file_items.add_function_reference cross_file ~pos_from ~pos_to)

(* The function boundary is structural: a function's optional arguments are
exactly the optional parameters of its (one) arrow node. *)
let rec from_type_expr (texpr : Types.type_expr) =
match texpr.desc with
| _ when not (active ()) -> []
| Tarrow ({lbl = Optional {txt = s}}, t_to, _) -> s :: from_type_expr t_to
| Tarrow (_, t_to, _) -> from_type_expr t_to
| Tarrow (params, _) ->
params
|> List.filter_map (fun ({lbl} : Types.arg) ->
match lbl with
| Optional {txt = s} -> Some s
| _ -> None)
| Tlink t -> from_type_expr t
| Tsubst t -> from_type_expr t
| _ -> []

let rec from_type_expr_with_arity (texpr : Types.type_expr) arity =
if arity <= 0 then []
else
match texpr.desc with
| _ when not (active ()) -> []
| Tarrow ({lbl = Optional {txt = s}}, t_to, _) ->
s :: from_type_expr_with_arity t_to (arity - 1)
| Tarrow (_, t_to, _) -> from_type_expr_with_arity t_to (arity - 1)
| Tlink t -> from_type_expr_with_arity t arity
| Tsubst t -> from_type_expr_with_arity t arity
| _ -> []

let rec from_type_expr_with_declared_arity (texpr : Types.type_expr) =
match texpr.desc with
| Tarrow (_, _, Some arity) -> from_type_expr_with_arity texpr arity
| Tlink t -> from_type_expr_with_declared_arity t
| Tsubst t -> from_type_expr_with_declared_arity t
| _ -> from_type_expr texpr

let add_references ~config ~cross_file ~(loc_from : Location.t)
~(loc_to : Location.t) ~(binding : Location.t) ~path
(arg_names, arg_names_maybe) =
Expand Down
24 changes: 6 additions & 18 deletions analysis/reanalyze/src/dead_value.ml
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ let collect_value_binding ~config ~decls ~file ~(current_binding : Location.t)
let name = Ident.name id |> Name.create ~is_interface:false in
let optional_args, reports_optional_args =
match vb.vb_expr.exp_desc with
| Texp_function {arity = Some arity; _} ->
( vb.vb_expr.exp_type
|> (fun texpr ->
Dead_optional_args.from_type_expr_with_arity texpr arity)
|> Optional_args.from_list,
true )
| Texp_function _ ->
( vb.vb_expr.exp_type |> Dead_optional_args.from_type_expr
|> Optional_args.from_list,
Expand Down Expand Up @@ -210,18 +204,12 @@ let rec collect_expr ~config ~refs ~file_deps ~cross_file ~direct_callees
exp_desc =
Texp_function
{
case =
params = [{fp_pat = {pat_desc = Tpat_var (eta_arg, _)}}];
body =
{
c_lhs = {pat_desc = Tpat_var (eta_arg, _)};
c_rhs =
{
exp_desc =
Texp_apply
{
funct = {exp_desc = Texp_ident (id_arg2, _, _)};
args;
};
};
exp_desc =
Texp_apply
{funct = {exp_desc = Texp_ident (id_arg2, _, _)}; args};
};
};
} )
Expand Down Expand Up @@ -397,7 +385,7 @@ let rec process_signature_item ~config ~decls ~file ~do_types ~do_values
in
if (not is_primitive) || !Config.analyze_externals then
let optional_args =
val_type |> Dead_optional_args.from_type_expr_with_declared_arity
val_type |> Dead_optional_args.from_type_expr
|> Optional_args.from_list
in
let reports_optional_args =
Expand Down
15 changes: 11 additions & 4 deletions analysis/src/completion_back_end.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1093,12 +1093,19 @@ and get_completions_for_context_path ~state ~debug ~full ~opens ~raw_opens ~pos
~pos
with
| Some ((TypeExpr typ | ExtractedType (Tfunction {typ})), env) -> (
let rec reconstruct_function_type args t_ret =
let reconstruct_function_type args t_ret =
match args with
| [] -> t_ret
| (label, t_arg) :: rest ->
let rest_type = reconstruct_function_type rest t_ret in
{typ with desc = Tarrow ({lbl = label; typ = t_arg}, rest_type, None)}
| args ->
{
typ with
desc =
Tarrow
( List.map
(fun (label, t_arg) -> {Types.lbl = label; typ = t_arg})
args,
t_ret );
}
in
let rec process_apply args labels =
match (args, labels) with
Expand Down
84 changes: 48 additions & 36 deletions analysis/src/completion_front_end.ml
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,8 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
let old_in_jsx_context = !in_jsx_context in
if Utils.is_jsx_component value_binding then in_jsx_context := true;
(match value_binding with
| {pvb_pat = {ppat_desc = Ppat_constraint (_pat, core_type)}; pvb_expr}
| {pvb_pat = {ppat_desc = Ppat_constraint (_, core_type)}; pvb_expr}
| {pvb_constraint = Some {pvc_type = core_type}; pvb_expr}
when loc_has_cursor pvb_expr.pexp_loc -> (
(* Expression with derivable type annotation.
E.g: let x: someRecord = {<com>} *)
Expand Down Expand Up @@ -806,9 +807,14 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
{context_path = CTypeAtPos loc; prefix; nested = List.rev nested})
| _ -> ())
| {
pvb_pat = {ppat_desc = Ppat_constraint (_pat, core_type); ppat_loc};
pvb_expr;
}
pvb_pat = {ppat_desc = Ppat_constraint (_, core_type); ppat_loc};
pvb_expr;
}
| {
pvb_pat = {ppat_loc};
pvb_expr;
pvb_constraint = Some {pvc_type = core_type};
}
when loc_has_cursor value_binding.pvb_loc
&& loc_has_cursor ppat_loc = false
&& loc_has_cursor pvb_expr.pexp_loc = false
Expand Down Expand Up @@ -1601,42 +1607,48 @@ let completion_with_parser1 ~debug ~offset ~pos_cursor ~kind_file
| Some context_path ->
set_result (Cpath (CPObj (context_path, label)))
| None -> ())
| Pexp_fun
{arg_label = lbl; default = default_exp_opt; lhs = pat; rhs = e} ->
| Pexp_fun {params; body = e} ->
let old_scope = !scope in
(match (!processing_fun, !current_ctx_path) with
| None, Some ctx_path -> processing_fun := Some (ctx_path, 0)
| _ -> ());
let arg_context_path =
match !processing_fun with
| None -> None
| Some (ctx_path, current_unlabelled_count) ->
(processing_fun :=
match lbl with
| Nolabel -> Some (ctx_path, current_unlabelled_count + 1)
| _ -> Some (ctx_path, current_unlabelled_count));
if Debug.verbose () then
print_endline "[expr_iter] Completing for argument value";
Some
(Completable.CArgument
{
function_context_path = ctx_path;
argument_label =
(match lbl with
| Nolabel ->
Unlabelled
{argument_position = current_unlabelled_count}
| Optional {txt = name} -> Optional name
| Labelled {txt = name} -> Labelled name);
})
in
(match default_exp_opt with
| None -> ()
| Some default_exp -> iterator.expr iterator default_exp);
if loc_has_cursor e.pexp_loc = false then
complete_pattern ?context_path:arg_context_path pat;
scope_pattern ?context_path:arg_context_path pat;
iterator.pat iterator pat;
params
|> List.iter
(fun
({p_lbl = lbl; p_default = default_exp_opt; p_pat = pat} :
Parsetree.fun_param)
->
let arg_context_path =
match !processing_fun with
| None -> None
| Some (ctx_path, current_unlabelled_count) ->
(processing_fun :=
match lbl with
| Nolabel ->
Some (ctx_path, current_unlabelled_count + 1)
| _ -> Some (ctx_path, current_unlabelled_count));
if Debug.verbose () then
print_endline "[expr_iter] Completing for argument value";
Some
(Completable.CArgument
{
function_context_path = ctx_path;
argument_label =
(match lbl with
| Nolabel ->
Unlabelled
{argument_position = current_unlabelled_count}
| Optional {txt = name} -> Optional name
| Labelled {txt = name} -> Labelled name);
})
in
(match default_exp_opt with
| None -> ()
| Some default_exp -> iterator.expr iterator default_exp);
if loc_has_cursor e.pexp_loc = false then
complete_pattern ?context_path:arg_context_path pat;
scope_pattern ?context_path:arg_context_path pat;
iterator.pat iterator pat);
iterator.expr iterator e;
scope := old_scope;
processed := true
Expand Down
4 changes: 2 additions & 2 deletions analysis/src/completion_jsx.ml
Original file line number Diff line number Diff line change
Expand Up @@ -246,15 +246,15 @@ let get_jsx_labels ~component_path ~find_type_of_value ~package ~state =
| Some (path, type_args) -> get_fields ~path ~type_args
| None -> [])
| Tarrow
({lbl = Nolabel; typ = {desc = Tconstr (path, type_args, _)}}, _, _)
({lbl = Nolabel; typ = {desc = Tconstr (path, type_args, _)}} :: _, _)
when Path.last path = "props" ->
get_fields ~path ~type_args
| Tconstr (cl_path, [{desc = Tconstr (path, type_args, _)}; _], _)
when Path.name cl_path = "React.componentLike"
&& Path.last path = "props" ->
(* JSX V4 external or interface *)
get_fields ~path ~type_args
| Tarrow ({lbl = Nolabel; typ}, _, _) -> (
| Tarrow ({lbl = Nolabel; typ} :: _, _) -> (
(* Component without the JSX PPX, like a make fn taking a hand-written
type props. *)
let rec dig_to_constr typ =
Expand Down
Loading
Loading