Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#### :bug: Bug fix

- Fix @directive on function level with async and multiple parameters. https://github.com/rescript-lang/rescript/pull/7977
- Fix fatal error for external with @as. https://github.com/rescript-lang/rescript/pull/7978

#### :memo: Documentation

Expand Down
21 changes: 19 additions & 2 deletions compiler/frontend/ast_external_process.ml
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,24 @@ let handle_attributes (loc : Bs_loc.t) (type_annotation : Parsetree.core_type)
new_arg_types,
if arg_type = Ignore then i else i + 1 ))
in
(* If every original argument was erased (e.g. all `@as(json ...) _`),
keep the external binding callable by threading a final `unit`
parameter through the type and arg specs. *)
let args, arg_type_specs =
match (args, arg_type_specs_length) with
| [], n when n > 0 ->
let unit_type =
Ast_helper.Typ.constr ~loc
(Location.mkloc (Longident.Lident "unit") loc)
[]
in
let unit_arg = {Parsetree.attrs = []; lbl = Nolabel; typ = unit_type} in
( [unit_arg],
arg_type_specs
@ [{External_arg_spec.arg_label = Arg_empty; arg_type = Extern_unit}]
)
| _ -> (args, arg_type_specs)
in
let ffi : External_ffi_types.external_spec =
external_desc_of_non_obj loc external_desc prim_name_with_source
arg_type_specs_length arg_types_ty arg_type_specs
Expand All @@ -1008,8 +1026,7 @@ let handle_attributes (loc : Bs_loc.t) (type_annotation : Parsetree.core_type)
let return_wrapper =
check_return_wrapper loc external_desc.return_wrapper result_type
in
let fn_type = Ast_helper.Typ.arrows ~loc args result_type in
( build_uncurried_type ~arity:(List.length args) fn_type,
( Ast_helper.Typ.arrows ~loc args result_type,
External_ffi_types.ffi_bs arg_type_specs return_wrapper ffi,
unused_attrs,
Comment on lines +1029 to 1031

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve uncurried arity when rebuilding external type

Previously this code rebuilt the return type with build_uncurried_type ~arity:(List.length args) so externals declared with an uncurried arrow retained their arity metadata. After the change it returns Ast_helper.Typ.arrows directly and the build_uncurried_type wrapper is never applied in the non-object path, so externals such as external add: (int, int) => int are now typed as int -> int -> int. Calls written with uncurried syntax (add(1, 2)) will start raising Apply_non_function/arity mismatches during type checking because the arity flag is gone. The new code should still apply build_uncurried_type to the reconstructed arrow to keep uncurried externals working.

Useful? React with 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex But Ast_helper.Typ.arrows does already set the arity for the outer function to List.length args.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not true, external add: (int, int) => int still works correctly.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How comes codex runs now? That changed?
It used to get stuck trying to install ocaml.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it didn't actually run anything, it just reviews the code.

relative )
Expand Down
5 changes: 4 additions & 1 deletion tests/tests/src/AsInUncurriedExternals.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ function shouldNotFail(objectMode, name) {
return 3;
}

let x = somescope.somefn({foo:true});

export {
mo,
options,
shouldNotFail,
x,
}
/* No side effect */
/* x Not a pure module */
5 changes: 5 additions & 0 deletions tests/tests/src/AsInUncurriedExternals.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ let mo = makeOptions
let options = mo(~name="foo", ())

let shouldNotFail: (~objectMode: _, ~name: string) => int = (~objectMode, ~name) => 3

@scope("somescope")
external constantArgOnly: @as(json`{foo:true}`) _ => string = "somefn"

let x = constantArgOnly()
Loading