Skip to content

Commit 8ee328b

Browse files
cristianoczth
andauthored
Untagged variants powered by instanceof (#6383)
* Skeleton adapt untagged variants to several instanceof cases. * handle instanceof for untagged variants in code generator * extend examples * add more instanceof backed type * add blob and file to untagged variants instanceof support * refactor * centralize emission logic for instanceof checks * changelog --------- Co-authored-by: Gabriel Nordeborn <gabbe.nord@gmail.com>
1 parent 6390057 commit 8ee328b

18 files changed

+319
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#### :rocket: New Feature
1616

1717
- Support renaming fields in inline records with `@as` attribute. [#6391](https://github.com/rescript-lang/rescript-compiler/pull/6391)
18+
- Add builtin abstract types for File and Blob APIs. https://github.com/rescript-lang/rescript-compiler/pull/6383
19+
- Untagged variants: Support `promise`, RegExes, Dates, File and Blob. https://github.com/rescript-lang/rescript-compiler/pull/6383
1820

1921
# 11.0.0-rc.3
2022

jscomp/core/js_exp_make.ml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ let tag_type = function
807807
| Untagged FloatType -> str "number"
808808
| Untagged FunctionType -> str "function"
809809
| Untagged StringType -> str "string"
810-
| Untagged ArrayType -> str "Array" ~delim:DNoQuotes
810+
| Untagged (InstanceType i) -> str (Ast_untagged_variants.Instance.to_string i) ~delim:DNoQuotes
811811
| Untagged ObjectType -> str "object"
812812
| Untagged UnknownType ->
813813
(* TODO: this should not happen *)
@@ -824,7 +824,10 @@ let rec emit_check (check : t Ast_untagged_variants.DynamicChecks.t) = match che
824824
in
825825
bin op (emit_check x) (emit_check y)
826826
| TypeOf x -> typeof (emit_check x)
827-
| IsArray x -> is_array (emit_check x)
827+
| IsInstanceOf (Array, x) -> is_array (emit_check x)
828+
| IsInstanceOf (instance, x) ->
829+
let instance_name = Ast_untagged_variants.Instance.to_string instance in
830+
instanceof (emit_check x) (str instance_name ~delim:DNoQuotes)
828831
| Not x -> not (emit_check x)
829832
| Expr x -> x
830833

jscomp/core/lam_compile.ml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -762,18 +762,18 @@ and compile_untagged_cases ~cxt ~switch_exp ~default ~block_cases cases =
762762
in
763763
E.emit_check check
764764
in
765-
let is_array (l, _) = l = Ast_untagged_variants.Untagged ArrayType in
765+
let is_not_typeof (l, _) = match l with
766+
| Ast_untagged_variants.Untagged (InstanceType _) -> true
767+
| _ -> false in
766768
let switch ?default ?declaration e clauses =
767-
let array_clauses = Ext_list.filter clauses is_array in
768-
match array_clauses with
769-
| [(l, {J.switch_body})] when List.length clauses > 1 ->
770-
let rest = Ext_list.filter clauses (fun c -> not (is_array c)) in
771-
S.if_ (E.is_array e)
769+
let (not_typeof_clauses, typeof_clauses) = List.partition is_not_typeof clauses in
770+
let rec build_if_chain remaining_clauses = (match remaining_clauses with
771+
| (Ast_untagged_variants.Untagged (InstanceType instanceType), {J.switch_body}) :: rest ->
772+
S.if_ (E.emit_check (IsInstanceOf (instanceType, Expr e)))
772773
(switch_body)
773-
~else_:([S.string_switch ?default ?declaration (E.typeof e) rest])
774-
| _ :: _ :: _ -> assert false (* at most 1 array case *)
775-
| _ ->
776-
S.string_switch ?default ?declaration (E.typeof e) clauses in
774+
~else_:([build_if_chain rest])
775+
| _ -> S.string_switch ?default ?declaration (E.typeof e) typeof_clauses) in
776+
build_if_chain not_typeof_clauses in
777777
cases |> compile_general_cases
778778
~make_exp: E.tag_type
779779
~eq_exp: mk_eq

jscomp/ml/ast_untagged_variants.ml

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1+
module Instance = struct
2+
type t =
3+
| Array
4+
| Blob
5+
| Date
6+
| File
7+
| Promise
8+
| RegExp
9+
let to_string = function
10+
Array -> "Array"
11+
| Blob -> "Blob"
12+
| Date -> "Date"
13+
| File -> "File"
14+
| Promise -> "Promise"
15+
| RegExp -> "RegExp"
16+
end
17+
118
type untaggedError =
219
| OnlyOneUnknown of string
320
| AtMostOneObject
4-
| AtMostOneArray
21+
| AtMostOneInstance of Instance.t
522
| AtMostOneFunction
623
| AtMostOneString
724
| AtMostOneNumber
@@ -29,7 +46,7 @@ let report_error ppf =
2946
(match untaggedVariant with
3047
| OnlyOneUnknown name -> "Case " ^ name ^ " has a payload that is not of one of the recognized shapes (object, array, etc). Then it must be the only case with payloads."
3148
| AtMostOneObject -> "At most one case can be an object type."
32-
| AtMostOneArray -> "At most one case can be an array type."
49+
| AtMostOneInstance i -> "At most one case can be a " ^ (Instance.to_string i) ^ " type."
3350
| AtMostOneFunction -> "At most one case can be a function type."
3451
| AtMostOneString -> "At most one case can be a string type."
3552
| AtMostOneNumber ->
@@ -42,7 +59,7 @@ type block_type =
4259
| IntType
4360
| StringType
4461
| FloatType
45-
| ArrayType
62+
| InstanceType of Instance.t
4663
| FunctionType
4764
| ObjectType
4865
| UnknownType
@@ -121,9 +138,26 @@ let type_is_builtin_object (t : Types.type_expr) =
121138
match t.desc with
122139
| Tconstr (path, _, _) ->
123140
let name = Path.name path in
124-
name = "Js.Dict.t" || name = "Js_dict.t" || name = "Js.Re.t" || name = "RescriptCore.Re.t"
141+
name = "Js.Dict.t" || name = "Js_dict.t"
125142
| _ -> false
126143

144+
let type_to_instanceof_backed_obj (t : Types.type_expr) =
145+
match t.desc with
146+
| Tconstr (path, _, _) when Path.same path Predef.path_promise ->
147+
Some Instance.Promise
148+
| Tconstr (path, _, _) when Path.same path Predef.path_array ->
149+
Some Array
150+
| Tconstr (path, _, _) -> (
151+
match Path.name path with
152+
| "Js.Date.t" | "Js_date.t" -> Some(Date)
153+
| "Js.Re.t" | "Js_re.t" | "RescriptCore.Re.t" ->
154+
(* TODO: Get rid of explicit Core by digging through aliases *)
155+
Some(RegExp)
156+
| "Js.File.t" | "Js_file.t" -> Some(File)
157+
| "Js.Blob.t" | "Js_blob.t" -> Some(Blob)
158+
| _ -> None)
159+
| _ -> None
160+
127161
let get_block_type ~env (cstr : Types.constructor_declaration) :
128162
block_type option =
129163
match (process_untagged cstr.cd_attributes, cstr.cd_args) with
@@ -137,9 +171,6 @@ let get_block_type ~env (cstr : Types.constructor_declaration) :
137171
| true, Cstr_tuple [{desc = Tconstr (path, _, _)}]
138172
when Path.same path Predef.path_float ->
139173
Some FloatType
140-
| true, Cstr_tuple [{desc = Tconstr (path, _, _)}]
141-
when Path.same path Predef.path_array ->
142-
Some ArrayType
143174
| true, Cstr_tuple [({desc = Tconstr _} as t)]
144175
when Ast_uncurried_utils.typeIsUncurriedFun t ->
145176
Some FunctionType
@@ -150,6 +181,11 @@ let get_block_type ~env (cstr : Types.constructor_declaration) :
150181
| true, Cstr_tuple [({desc = Tconstr _} as t)] when type_is_builtin_object t
151182
->
152183
Some ObjectType
184+
| true, Cstr_tuple [({desc = Tconstr _} as t)] when type_to_instanceof_backed_obj t |> Option.is_some
185+
->
186+
(match type_to_instanceof_backed_obj t with
187+
| None -> None
188+
| Some instanceType -> Some (InstanceType instanceType))
153189
| true, Cstr_tuple [ty] -> (
154190
let default = Some UnknownType in
155191
match !extract_concrete_typedecl env ty with
@@ -192,7 +228,7 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list)
192228
let module StringSet = Set.Make (String) in
193229
let string_literals = ref StringSet.empty in
194230
let nonstring_literals = ref StringSet.empty in
195-
let arrayTypes = ref 0 in
231+
let instanceTypes = Hashtbl.create 1 in
196232
let functionTypes = ref 0 in
197233
let objectTypes = ref 0 in
198234
let stringTypes = ref 0 in
@@ -213,8 +249,10 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list)
213249
raise (Error (loc, InvalidUntaggedVariantDefinition (OnlyOneUnknown name)));
214250
if !objectTypes > 1 then
215251
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneObject));
216-
if !arrayTypes > 1 then
217-
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneArray));
252+
Hashtbl.iter (fun i count ->
253+
if count > 1 then
254+
raise (Error (loc, InvalidUntaggedVariantDefinition (AtMostOneInstance i))))
255+
instanceTypes;
218256
if !functionTypes > 1 then
219257
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneFunction));
220258
if !stringTypes > 1 then
@@ -244,8 +282,9 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list)
244282
| Some ObjectType ->
245283
incr objectTypes;
246284
invariant loc name
247-
| Some ArrayType ->
248-
incr arrayTypes;
285+
| Some (InstanceType i) ->
286+
let count = Hashtbl.find_opt instanceTypes i |> Option.value ~default:0 in
287+
Hashtbl.replace instanceTypes i (count + 1);
249288
invariant loc name
250289
| Some FunctionType ->
251290
incr functionTypes;
@@ -298,15 +337,15 @@ module DynamicChecks = struct
298337
| BinOp of op * 'a t * 'a t
299338
| TagType of tag_type
300339
| TypeOf of 'a t
301-
| IsArray of 'a t
340+
| IsInstanceOf of Instance.t * 'a t
302341
| Not of 'a t
303342
| Expr of 'a
304343

305344
let bin op x y = BinOp (op, x, y)
306345
let tag_type t = TagType t
307346
let typeof x = TypeOf x
308347
let str s = String s |> tag_type
309-
let is_array x = IsArray x
348+
let is_instance i x = IsInstanceOf (i, x)
310349
let not x = Not x
311350
let nil = Null |> tag_type
312351
let undefined = Undefined |> tag_type
@@ -348,7 +387,7 @@ module DynamicChecks = struct
348387
typeof e != number
349388
| FloatType when literals_overlaps_with_number () = false ->
350389
typeof e != number
351-
| ArrayType -> not (is_array e)
390+
| InstanceType i -> not (is_instance i e)
352391
| FunctionType -> typeof e != function_
353392
| ObjectType when literals_overlaps_with_object () = false ->
354393
typeof e != object_
@@ -394,13 +433,18 @@ module DynamicChecks = struct
394433
typeof e != object_
395434

396435
let add_runtime_type_check ~tag_type ~(block_cases : block_type list) x y =
397-
let has_array () = Ext_list.exists block_cases (fun t -> t = ArrayType) in
436+
let instances = Ext_list.filter_map block_cases (function InstanceType i -> Some i | _ -> None) in
398437
match tag_type with
399438
| Untagged (IntType | StringType | FloatType | FunctionType) ->
400439
typeof y == x
401440
| Untagged ObjectType ->
402-
if has_array () then typeof y == x &&& not (is_array y) else typeof y == x
403-
| Untagged ArrayType -> is_array y
441+
if instances <> [] then
442+
let not_one_of_the_instances =
443+
Ext_list.fold_right instances (typeof y == x) (fun i x -> x &&& not (is_instance i y)) in
444+
not_one_of_the_instances
445+
else
446+
typeof y == x
447+
| Untagged (InstanceType i) -> is_instance i y
404448
| Untagged UnknownType ->
405449
(* This should not happen because unknown must be the only non-literal case *)
406450
assert false

jscomp/others/js.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ module Int = Js_int
261261
module Bigint = Js_bigint
262262
(** Provide utilities for bigint *)
263263

264+
module File = Js_file
265+
(** Provide utilities for File *)
266+
267+
module Blob = Js_blob
268+
(** Provide utilities for Blob *)
269+
264270
module Option = Js_option
265271
(** Provide utilities for option *)
266272

jscomp/others/js_blob.res

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/*** JavaScript Blob API */
2+
3+
type t

jscomp/others/js_file.res

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/*** JavaScript File API */
2+
3+
type t

jscomp/others/release.ninja

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ o others/js_OO.cmi others/js_OO.cmj : cc others/js_OO.res | others/belt_internal
1919
o others/js_array.cmi others/js_array.cmj : cc others/js_array.res | others/belt_internals.cmi others/js.cmi others/js_array2.cmj $bsc
2020
o others/js_array2.cmi others/js_array2.cmj : cc others/js_array2.res | others/belt_internals.cmi others/js.cmi $bsc
2121
o others/js_bigint.cmi others/js_bigint.cmj : cc others/js_bigint.res | others/belt_internals.cmi others/js.cmi $bsc
22+
o others/js_blob.cmi others/js_blob.cmj : cc others/js_blob.res | others/belt_internals.cmi others/js.cmi $bsc
2223
o others/js_cast.cmj : cc_cmi others/js_cast.res | others/belt_internals.cmi others/js.cmi others/js_cast.cmi $bsc
2324
o others/js_cast.cmi : cc others/js_cast.resi | others/belt_internals.cmi others/js.cmi $bsc
2425
o others/js_console.cmi others/js_console.cmj : cc others/js_console.res | others/belt_internals.cmi others/js.cmi $bsc
@@ -27,6 +28,7 @@ o others/js_dict.cmj : cc_cmi others/js_dict.res | others/belt_internals.cmi oth
2728
o others/js_dict.cmi : cc others/js_dict.resi | others/belt_internals.cmi others/js.cmi $bsc
2829
o others/js_exn.cmj : cc_cmi others/js_exn.res | others/belt_internals.cmi others/js.cmi others/js_exn.cmi $bsc
2930
o others/js_exn.cmi : cc others/js_exn.resi | others/belt_internals.cmi others/js.cmi $bsc
31+
o others/js_file.cmi others/js_file.cmj : cc others/js_file.res | others/belt_internals.cmi others/js.cmi $bsc
3032
o others/js_float.cmi others/js_float.cmj : cc others/js_float.res | others/belt_internals.cmi others/js.cmi $bsc
3133
o others/js_global.cmi others/js_global.cmj : cc others/js_global.res | others/belt_internals.cmi others/js.cmi $bsc
3234
o others/js_int.cmi others/js_int.cmj : cc others/js_int.res | others/belt_internals.cmi others/js.cmi $bsc
@@ -72,7 +74,7 @@ o others/jsxEventU.cmi others/jsxEventU.cmj : cc others/jsxEventU.res | others/b
7274
o others/jsxPPXReactSupportC.cmi others/jsxPPXReactSupportC.cmj : cc others/jsxPPXReactSupportC.res | others/belt_internals.cmi others/js.cmi others/jsxC.cmj $bsc
7375
o others/jsxPPXReactSupportU.cmi others/jsxPPXReactSupportU.cmj : cc others/jsxPPXReactSupportU.res | others/belt_internals.cmi others/js.cmi others/jsxU.cmj $bsc
7476
o others/jsxU.cmi others/jsxU.cmj : cc others/jsxU.res | others/belt_internals.cmi others/js.cmi $bsc
75-
o js_pkg : phony others/js_OO.cmi others/js_OO.cmj others/js_array.cmi others/js_array.cmj others/js_array2.cmi others/js_array2.cmj others/js_bigint.cmi others/js_bigint.cmj others/js_cast.cmi others/js_cast.cmj others/js_console.cmi others/js_console.cmj others/js_date.cmi others/js_date.cmj others/js_dict.cmi others/js_dict.cmj others/js_exn.cmi others/js_exn.cmj others/js_float.cmi others/js_float.cmj others/js_global.cmi others/js_global.cmj others/js_int.cmi others/js_int.cmj others/js_json.cmi others/js_json.cmj others/js_list.cmi others/js_list.cmj others/js_map.cmi others/js_map.cmj others/js_mapperRt.cmi others/js_mapperRt.cmj others/js_math.cmi others/js_math.cmj others/js_null.cmi others/js_null.cmj others/js_null_undefined.cmi others/js_null_undefined.cmj others/js_obj.cmi others/js_obj.cmj others/js_option.cmi others/js_option.cmj others/js_promise.cmi others/js_promise.cmj others/js_promise2.cmi others/js_promise2.cmj others/js_re.cmi others/js_re.cmj others/js_result.cmi others/js_result.cmj others/js_set.cmi others/js_set.cmj others/js_string.cmi others/js_string.cmj others/js_string2.cmi others/js_string2.cmj others/js_typed_array.cmi others/js_typed_array.cmj others/js_typed_array2.cmi others/js_typed_array2.cmj others/js_types.cmi others/js_types.cmj others/js_undefined.cmi others/js_undefined.cmj others/js_vector.cmi others/js_vector.cmj others/js_weakmap.cmi others/js_weakmap.cmj others/js_weakset.cmi others/js_weakset.cmj others/jsxC.cmi others/jsxC.cmj others/jsxDOMC.cmi others/jsxDOMC.cmj others/jsxDOMStyle.cmi others/jsxDOMStyle.cmj others/jsxDOMU.cmi others/jsxDOMU.cmj others/jsxEventC.cmi others/jsxEventC.cmj others/jsxEventU.cmi others/jsxEventU.cmj others/jsxPPXReactSupportC.cmi others/jsxPPXReactSupportC.cmj others/jsxPPXReactSupportU.cmi others/jsxPPXReactSupportU.cmj others/jsxU.cmi others/jsxU.cmj
77+
o js_pkg : phony others/js_OO.cmi others/js_OO.cmj others/js_array.cmi others/js_array.cmj others/js_array2.cmi others/js_array2.cmj others/js_bigint.cmi others/js_bigint.cmj others/js_blob.cmi others/js_blob.cmj others/js_cast.cmi others/js_cast.cmj others/js_console.cmi others/js_console.cmj others/js_date.cmi others/js_date.cmj others/js_dict.cmi others/js_dict.cmj others/js_exn.cmi others/js_exn.cmj others/js_file.cmi others/js_file.cmj others/js_float.cmi others/js_float.cmj others/js_global.cmi others/js_global.cmj others/js_int.cmi others/js_int.cmj others/js_json.cmi others/js_json.cmj others/js_list.cmi others/js_list.cmj others/js_map.cmi others/js_map.cmj others/js_mapperRt.cmi others/js_mapperRt.cmj others/js_math.cmi others/js_math.cmj others/js_null.cmi others/js_null.cmj others/js_null_undefined.cmi others/js_null_undefined.cmj others/js_obj.cmi others/js_obj.cmj others/js_option.cmi others/js_option.cmj others/js_promise.cmi others/js_promise.cmj others/js_promise2.cmi others/js_promise2.cmj others/js_re.cmi others/js_re.cmj others/js_result.cmi others/js_result.cmj others/js_set.cmi others/js_set.cmj others/js_string.cmi others/js_string.cmj others/js_string2.cmi others/js_string2.cmj others/js_typed_array.cmi others/js_typed_array.cmj others/js_typed_array2.cmi others/js_typed_array2.cmj others/js_types.cmi others/js_types.cmj others/js_undefined.cmi others/js_undefined.cmj others/js_vector.cmi others/js_vector.cmj others/js_weakmap.cmi others/js_weakmap.cmj others/js_weakset.cmi others/js_weakset.cmj others/jsxC.cmi others/jsxC.cmj others/jsxDOMC.cmi others/jsxDOMC.cmj others/jsxDOMStyle.cmi others/jsxDOMStyle.cmj others/jsxDOMU.cmi others/jsxDOMU.cmj others/jsxEventC.cmi others/jsxEventC.cmj others/jsxEventU.cmi others/jsxEventU.cmj others/jsxPPXReactSupportC.cmi others/jsxPPXReactSupportC.cmj others/jsxPPXReactSupportU.cmi others/jsxPPXReactSupportU.cmj others/jsxU.cmi others/jsxU.cmj
7678
o others/belt_Array.cmj : cc_cmi others/belt_Array.res | others/belt.cmi others/belt_Array.cmi others/belt_internals.cmi others/js.cmi others/js.cmj others/js_math.cmj $bsc js_pkg
7779
o others/belt_Array.cmi : cc others/belt_Array.resi | others/belt.cmi others/belt_internals.cmi others/js.cmi others/js.cmj $bsc js_pkg
7880
o others/belt_Float.cmj : cc_cmi others/belt_Float.res | others/belt.cmi others/belt_Float.cmi others/belt_internals.cmi others/js.cmi $bsc js_pkg

jscomp/runtime/js.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,12 @@ module Int = Js_int
228228
module Bigint = Js_bigint
229229
(** Provide utilities for bigint *)
230230

231+
module File = Js_file
232+
(** Provide utilities for File *)
233+
234+
module Blob = Js_blob
235+
(** Provide utilities for Blob *)
236+
231237
module Option = Js_option
232238
(** Provide utilities for option *)
233239

0 commit comments

Comments
 (0)