Skip to content

Support optional named arguments without a final unit in uncurried functions. #5907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 14, 2023
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 @@ -24,6 +24,7 @@ subset of the arguments, and return a curried type with the remaining ones https
- Add support for default arguments in uncurried functions https://github.com/rescript-lang/rescript-compiler/pull/5835
- Inline uncurried application when it is safe https://github.com/rescript-lang/rescript-compiler/pull/5847
- Add support for toplevel `await` https://github.com/rescript-lang/rescript-compiler/pull/5940
- Support optional named arguments without a final unit in uncurried functions https://github.com/rescript-lang/rescript-compiler/pull/5907

#### :boom: Breaking Change

Expand Down
49 changes: 34 additions & 15 deletions jscomp/ml/typecore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2103,13 +2103,18 @@ and type_expect_ ?in_function ?(recarg=Rejected) env sexp ty_expected =
exp_type = newty (Ttuple (List.map (fun e -> e.exp_type) expl));
exp_attributes = sexp.pexp_attributes;
exp_env = env }
| Pexp_construct({txt = Lident "Function$"} as lid, sarg) ->
let state = Warnings.backup () in
let arity = Ast_uncurried.attributes_to_arity sexp.pexp_attributes in
let uncurried_typ = Ast_uncurried.make_uncurried_type ~env ~arity (newvar()) in
unify_exp_types loc env ty_expected uncurried_typ;
(* Disable Unerasable_optional_argument for uncurried functions *)
let unerasable_optional_argument = Warnings.number Unerasable_optional_argument in
Warnings.parse_options false ("-" ^ string_of_int unerasable_optional_argument);
let exp = type_construct env loc lid sarg ty_expected sexp.pexp_attributes in
Warnings.restore state;
exp
| Pexp_construct(lid, sarg) ->
(match lid.txt with
| Lident "Function$" ->
let arity = Ast_uncurried.attributes_to_arity sexp.pexp_attributes in
let uncurried_typ = Ast_uncurried.make_uncurried_type ~env ~arity (newvar()) in
unify_exp_types loc env ty_expected uncurried_typ
| _ -> ());
type_construct env loc lid sarg ty_expected sexp.pexp_attributes
| Pexp_variant(l, sarg) ->
(* Keep sharing *)
Expand Down Expand Up @@ -3014,7 +3019,7 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
match has_uncurried_type t with
| Some (arity, _) ->
let newarity = arity - nargs in
let fully_applied = newarity = 0 in
let fully_applied = newarity <= 0 in
if uncurried && not fully_applied then
raise(Error(funct.exp_loc, env,
Uncurried_arity_mismatch (t, arity, List.length sargs)));
Expand All @@ -3024,13 +3029,27 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
in
let rec type_unknown_args max_arity (args : lazy_args) omitted ty_fun (syntax_args : sargs)
: targs * _ =
match syntax_args with
| [] ->
(List.map
(function l, None -> l, None
| l, Some f -> l, Some (f ()))
(List.rev args),
instance env (result_type omitted ty_fun))
match syntax_args with
| [] ->
let collect_args () =
(List.map
(function l, None -> l, None
| l, Some f -> l, Some (f ()))
(List.rev args),
instance env (result_type omitted ty_fun)) in
if List.length args < max_arity && uncurried then
(match (expand_head env ty_fun).desc with
| Tarrow (Optional l,t1,t2,_) ->
ignored := (Optional l,t1,ty_fun.level) :: !ignored;
let arg = Optional l, Some (fun () -> option_none (instance env t1) Location.none) in
type_unknown_args max_arity (arg::args) omitted t2 []
| _ -> collect_args ())
else
collect_args ()
| [(Nolabel, {pexp_desc = Pexp_construct ({txt = Lident "()"}, None)})]
when uncurried && omitted = [] && List.length args = List.length !ignored ->
(* foo(. ) treated as empty application if all args are optional (hence ignored) *)
type_unknown_args max_arity args omitted ty_fun []
| (l1, sarg1) :: sargl ->
let (ty1, ty2) =
let ty_fun = expand_head env ty_fun in
Expand Down Expand Up @@ -3082,7 +3101,7 @@ and type_application uncurried env funct (sargs : sargs) : targs * Types.type_ex
let sargs, omitted, arg =
match extract_label name sargs with
| None ->
if optional && label_assoc Nolabel sargs
if optional && (uncurried || label_assoc Nolabel sargs)
then begin
ignored := (l,ty,lv) :: !ignored;
sargs, omitted , Some (fun () -> option_none (instance env ty) Location.none)
Expand Down
64 changes: 63 additions & 1 deletion jscomp/test/uncurried_default.args.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,42 @@ var partial = Curry._1((function (param) {

var total = withOpt(10, 3)(4, 11);

function foo1(xOpt, y) {
var x = xOpt !== undefined ? xOpt : 3;
return x + y | 0;
}

var x = 3;

var r1 = x + 11 | 0;

function foo2(y, xOpt, zOpt) {
var x = xOpt !== undefined ? xOpt : 3;
var z = zOpt !== undefined ? zOpt : 4;
return (x + y | 0) + z | 0;
}

var r2 = foo2(11, undefined, undefined);

function foo3(xOpt, yOpt) {
var x = xOpt !== undefined ? xOpt : 3;
var y = yOpt !== undefined ? yOpt : 4;
return x + y | 0;
}

var r3 = foo3(undefined, undefined);

var StandardNotation = {
withOpt: withOpt,
testWithOpt: testWithOpt,
partial: partial,
total: total
total: total,
foo1: foo1,
r1: r1,
foo2: foo2,
r2: r2,
foo3: foo3,
r3: r3
};

function withOpt$1(xOpt, y) {
Expand All @@ -45,9 +76,40 @@ var partial$1 = Curry._1((function (param) {

var total$1 = withOpt$1(10, 3)(4, 11);

function foo1$1(xOpt, y) {
var x = xOpt !== undefined ? xOpt : 3;
return x + y | 0;
}

var x$1 = 3;

var r1$1 = x$1 + 11 | 0;

function foo2$1(y, xOpt, zOpt) {
var x = xOpt !== undefined ? xOpt : 3;
var z = zOpt !== undefined ? zOpt : 4;
return (x + y | 0) + z | 0;
}

var r2$1 = foo2$1(11, undefined, undefined);

function foo3$1(xOpt, yOpt) {
var x = xOpt !== undefined ? xOpt : 3;
var y = yOpt !== undefined ? yOpt : 4;
return x + y | 0;
}

var r3$1 = foo3$1(undefined, undefined);

exports.StandardNotation = StandardNotation;
exports.withOpt = withOpt$1;
exports.testWithOpt = testWithOpt$1;
exports.partial = partial$1;
exports.total = total$1;
exports.foo1 = foo1$1;
exports.r1 = r1$1;
exports.foo2 = foo2$1;
exports.r2 = r2$1;
exports.foo3 = foo3$1;
exports.r3 = r3$1;
/* testWithOpt Not a pure module */
18 changes: 18 additions & 0 deletions jscomp/test/uncurried_default.args.res
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ module StandardNotation = {
let testWithOpt = withOpt(. 3)(. 4)
let partial = withOpt(~x=10)(3)(~z=4)(11)
let total = withOpt(. ~x=10, 3)(. ~z=4, 11)

let foo1 = (. ~x=3, ~y) => x+y
let r1 = foo1(. ~y=11)

let foo2 = (. ~y, ~x=3, ~z=4) => x+y+z
let r2 = foo2(. ~y=11)

let foo3 = (. ~x=3, ~y=4) => x+y
let r3 = foo3(. )
}

@@uncurried
Expand All @@ -13,3 +22,12 @@ let withOpt = (~x=1, y) => (~z=1, w) => x+y+z+w
let testWithOpt = withOpt(3)(4)
let partial = withOpt(. ~x=10)(. 3)(. ~z=4)(. 11)
let total = withOpt(~x=10, 3)(~z=4, 11)

let foo1 = (~x=3, ~y) => x+y
let r1 = foo1(~y=11)

let foo2 = (~y, ~x=3, ~z=4) => x+y+z
let r2 = foo2(~y=11)

let foo3 = (~x=3, ~y=4) => x+y
let r3 = foo3()