Skip to content

Commit 026e3ce

Browse files
committed
Add support for functions in untagged variants.
This was done at speed: need to double check that there are no corner cases missing. Fixes #6278
1 parent bae9420 commit 026e3ce

File tree

10 files changed

+72
-13
lines changed

10 files changed

+72
-13
lines changed

jscomp/core/js_exp_make.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,7 @@ let tag_type = function
769769
| Undefined -> undefined
770770
| Untagged IntType -> str "number"
771771
| Untagged FloatType -> str "number"
772+
| Untagged FunctionType -> str "function"
772773
| Untagged StringType -> str "string"
773774
| Untagged ArrayType -> str "Array" ~delim:DNoQuotes
774775
| Untagged ObjectType -> str "object"

jscomp/frontend/ast_core_type.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ let get_uncurry_arity (ty : t) =
125125
| _ -> None
126126

127127
let get_curry_arity (ty : t) =
128-
if Ast_uncurried.typeIsUncurriedFun ty then
128+
if Ast_uncurried.coreTypeIsUncurriedFun ty then
129129
let arity, _ = Ast_uncurried.typeExtractUncurriedFun ty in
130130
arity
131131
else get_uncurry_arity_aux ty 0

jscomp/frontend/ast_external_process.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ let spec_of_ptyp (nolabel : bool) (ptyp : Parsetree.core_type) :
6868
| _ -> Bs_syntaxerr.err ptyp.ptyp_loc Invalid_bs_unwrap_type)
6969
| `Uncurry opt_arity -> (
7070
let real_arity =
71-
if Ast_uncurried.typeIsUncurriedFun ptyp then
71+
if Ast_uncurried.coreTypeIsUncurriedFun ptyp then
7272
let arity, _ = Ast_uncurried.typeExtractUncurriedFun ptyp in
7373
Some arity
7474
else Ast_core_type.get_uncurry_arity ptyp

jscomp/ml/ast_uncurried.ml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,19 @@ let exprExtractUncurriedFun (expr : Parsetree.expression) =
6363
| Pexp_construct ({ txt = Lident "Function$" }, Some e) -> e
6464
| _ -> assert false
6565

66-
let typeIsUncurriedFun (typ : Parsetree.core_type) =
66+
let coreTypeIsUncurriedFun (typ : Parsetree.core_type) =
6767
match typ.ptyp_desc with
6868
| Ptyp_constr ({txt = Lident "function$"}, [{ptyp_desc = Ptyp_arrow _}; _]) ->
6969
true
7070
| _ -> false
7171

72+
let typeIsUncurriedFun (typ : Types.type_expr) =
73+
match typ.desc with
74+
| Tconstr (Pident {name = "function$"}, [{desc = Tarrow _}; _], _) ->
75+
true
76+
| _ -> false
77+
78+
7279
let typeExtractUncurriedFun (typ : Parsetree.core_type) =
7380
match typ.ptyp_desc with
7481
| Ptyp_constr ({txt = Lident "function$"}, [tArg; tArity]) ->

jscomp/ml/ast_untagged_variants.ml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
type untaggedError = OnlyOneUnknown | AtMostOneObject | AtMostOneArray | AtMostOneString | AtMostOneNumber | DuplicateLiteral of string
1+
type untaggedError = OnlyOneUnknown | AtMostOneObject | AtMostOneArray | AtMostOneFunction | AtMostOneString | AtMostOneNumber | DuplicateLiteral of string
22
type error =
33
| InvalidVariantAsAnnotation
44
| Duplicated_bs_as
@@ -22,14 +22,15 @@ let report_error ppf =
2222
| OnlyOneUnknown -> "An unknown case must be the only case with payloads."
2323
| AtMostOneObject -> "At most one case can be an object type."
2424
| AtMostOneArray -> "At most one case can be an array type."
25+
| AtMostOneFunction -> "At most one case can be a function type."
2526
| AtMostOneString -> "At most one case can be a string type."
2627
| AtMostOneNumber -> "At most one case can be a number type (int or float)."
2728
| DuplicateLiteral s -> "Duplicate literal " ^ s ^ "."
2829
)
2930

3031
(* Type of the runtime representation of an untagged block (case with payoad) *)
3132
type block_type =
32-
| IntType | StringType | FloatType | ArrayType | ObjectType | UnknownType
33+
| IntType | StringType | FloatType | ArrayType | FunctionType | ObjectType | UnknownType
3334

3435
(*
3536
Type of the runtime representation of a tag.
@@ -116,6 +117,10 @@ let get_block_type ~env (cstr: Types.constructor_declaration) : block_type optio
116117
Some FloatType
117118
| true, Cstr_tuple [{desc = Tconstr (path, _, _)}] when Path.same path Predef.path_array ->
118119
Some ArrayType
120+
| true, Cstr_tuple [{desc = Tconstr _} as t] when Ast_uncurried.typeIsUncurriedFun t ->
121+
Some FunctionType
122+
| true, Cstr_tuple [{desc = Tarrow _} ] ->
123+
Some FunctionType
119124
| true, Cstr_tuple [{desc = Tconstr (path, _, _)}] when Path. same path Predef.path_string ->
120125
Some StringType
121126
| true, Cstr_tuple [{desc = Tconstr _} as t] when type_is_builtin_object t ->
@@ -162,6 +167,7 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list) ~(blocks :
162167
let string_literals = ref StringSet.empty in
163168
let nonstring_literals = ref StringSet.empty in
164169
let arrayTypes = ref 0 in
170+
let functionTypes = ref 0 in
165171
let objectTypes = ref 0 in
166172
let stringTypes = ref 0 in
167173
let numberTypes = ref 0 in
@@ -181,6 +187,8 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list) ~(blocks :
181187
then raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneObject));
182188
if !arrayTypes > 1
183189
then raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneArray));
190+
if !functionTypes > 1
191+
then raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneFunction));
184192
if !stringTypes > 1
185193
then raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneString));
186194
if !numberTypes > 1
@@ -214,6 +222,9 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list) ~(blocks :
214222
| Some ArrayType ->
215223
incr arrayTypes;
216224
invariant loc
225+
| Some FunctionType ->
226+
incr functionTypes;
227+
invariant loc
217228
| Some (IntType | FloatType) ->
218229
incr numberTypes;
219230
invariant loc
@@ -266,6 +277,8 @@ module DynamicChecks = struct
266277
let nil = Null |> tag_type
267278
let undefined = Undefined |> tag_type
268279
let object_ = Untagged ObjectType |> tag_type
280+
281+
let function_ = Untagged FunctionType |> tag_type
269282
let string = Untagged StringType |> tag_type
270283
let number = Untagged IntType |> tag_type
271284

@@ -298,6 +311,8 @@ module DynamicChecks = struct
298311
typeof e != number
299312
| ArrayType ->
300313
not (is_array e)
314+
| FunctionType ->
315+
typeof e != function_
301316
| ObjectType when literals_overlaps_with_object () = false ->
302317
typeof e != object_
303318
| ObjectType (* overlap *) ->
@@ -341,9 +356,8 @@ module DynamicChecks = struct
341356
let add_runtime_type_check ~tag_type ~(block_cases: block_type list) x y =
342357
let has_array() = Ext_list.exists block_cases (fun t -> t = ArrayType) in
343358
match tag_type with
344-
| Untagged IntType
345-
| Untagged StringType
346-
| Untagged FloatType -> typeof y == x
359+
| Untagged (IntType | StringType | FloatType | FunctionType) ->
360+
typeof y == x
347361
| Untagged ObjectType ->
348362
if has_array() then
349363
typeof y == x &&& not (is_array y)

jscomp/syntax/src/react_jsx_common.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ let raiseErrorMultipleReactComponent ~loc =
4545
let optionalAttr = ({txt = "res.optional"; loc = Location.none}, PStr [])
4646

4747
let extractUncurried typ =
48-
if Ast_uncurried.typeIsUncurriedFun typ then
48+
if Ast_uncurried.coreTypeIsUncurriedFun typ then
4949
let _arity, t = Ast_uncurried.typeExtractUncurriedFun typ in
5050
t
5151
else typ

jscomp/syntax/src/res_parens.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ let includeModExpr modExpr =
442442
let arrowReturnTypExpr typExpr =
443443
match typExpr.Parsetree.ptyp_desc with
444444
| Parsetree.Ptyp_arrow _ -> true
445-
| _ when Ast_uncurried.typeIsUncurriedFun typExpr -> true
445+
| _ when Ast_uncurried.coreTypeIsUncurriedFun typExpr -> true
446446
| _ -> false
447447

448448
let patternRecordRowRhs (pattern : Parsetree.pattern) =

jscomp/syntax/src/res_printer.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,7 @@ and printTypExpr ~(state : State.t) (typExpr : Parsetree.core_type) cmtTbl =
15911591
let doc = printTypExpr ~state n cmtTbl in
15921592
match n.ptyp_desc with
15931593
| Ptyp_arrow _ | Ptyp_tuple _ | Ptyp_alias _ -> addParens doc
1594-
| _ when Ast_uncurried.typeIsUncurriedFun n -> addParens doc
1594+
| _ when Ast_uncurried.coreTypeIsUncurriedFun n -> addParens doc
15951595
| _ -> doc
15961596
in
15971597
Doc.group
@@ -1652,7 +1652,7 @@ and printTypExpr ~(state : State.t) (typExpr : Parsetree.core_type) cmtTbl =
16521652
let needsParens =
16531653
match typ.ptyp_desc with
16541654
| Ptyp_arrow _ -> true
1655-
| _ when Ast_uncurried.typeIsUncurriedFun typ -> true
1655+
| _ when Ast_uncurried.coreTypeIsUncurriedFun typ -> true
16561656
| _ -> false
16571657
in
16581658
let doc = printTypExpr ~state typ cmtTbl in
@@ -1664,7 +1664,7 @@ and printTypExpr ~(state : State.t) (typExpr : Parsetree.core_type) cmtTbl =
16641664
| Ptyp_object (fields, openFlag) ->
16651665
printObject ~state ~inline:false fields openFlag cmtTbl
16661666
| Ptyp_arrow _ -> printArrow ~uncurried:false typExpr
1667-
| Ptyp_constr _ when Ast_uncurried.typeIsUncurriedFun typExpr ->
1667+
| Ptyp_constr _ when Ast_uncurried.coreTypeIsUncurriedFun typExpr ->
16681668
let arity, tArg = Ast_uncurried.typeExtractUncurriedFun typExpr in
16691669
printArrow ~uncurried:true ~arity tArg
16701670
| Ptyp_constr (longidentLoc, [{ptyp_desc = Ptyp_object (fields, openFlag)}])

jscomp/test/UntaggedVariants.js

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/UntaggedVariants.res

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,17 @@ module OptionUnboxingHeuristic = {
293293
type untaggedInlineMultinaryOption = A | B({x: option<int>, y?: string})
294294
let untaggedInlineMultinaryOption = (x: untaggedInlineMultinaryOption) => Some(x)
295295
}
296+
297+
module TestFunctionCase = {
298+
@unboxed
299+
type t = Array(array<int>) | Record({x:int}) | Function((. int) => int)
300+
301+
let classify = v =>
302+
switch v {
303+
| Record({x}) => x
304+
| Array(a) => a[0]
305+
| Function(f) => f(. 3)
306+
}
307+
308+
let ff = Function((. x) => x+1)
309+
}

0 commit comments

Comments
 (0)