Skip to content

Commit 9fd7821

Browse files
committed
Support bool in untagged variant
1 parent a8c7f15 commit 9fd7821

File tree

9 files changed

+128
-25
lines changed

9 files changed

+128
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "unboxed_bool_with_const",
3+
"version": "0.1.0",
4+
"sources": [
5+
{
6+
"dir": "src",
7+
"subdirs": true
8+
}
9+
],
10+
"package-specs": {
11+
"module": "commonjs",
12+
"in-source": true
13+
},
14+
"suffix": ".bs.js"
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ts-check
2+
3+
var cp = require("child_process");
4+
var assert = require("assert");
5+
var rescript_exe = require("../../../scripts/bin_path").rescript_exe;
6+
7+
var out = cp.spawnSync(rescript_exe, {
8+
cwd: __dirname,
9+
encoding: "utf8",
10+
});
11+
12+
assert.equal(
13+
out.stdout,
14+
`Dependency Finished
15+
rescript: [1/1] src/Main.cmj
16+
FAILED: src/Main.cmj
17+
18+
We've found a bug for you!
19+
/Users/dzakh/code/DZakh-forks/rescript-compiler/jscomp/build_tests/unboxed_bool_with_const/src/Main.res:3:3-14
20+
21+
1 │ @unboxed
22+
2 │ type t<'a> =
23+
3 │ | Bool(bool)
24+
4 │ | @as(false) False
25+
5 │ | @as(true) True
26+
27+
This untagged variant definition is invalid: At most one case can be a boolean type.
28+
29+
FAILED: cannot make progress due to previous errors.
30+
`
31+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@unboxed
2+
type t<'a> =
3+
| Bool(bool)
4+
| @as(false) False
5+
| @as(true) True

jscomp/core/js_exp_make.ml

+1
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ let tag_type = function
805805
| Undefined -> undefined
806806
| Untagged IntType -> str "number"
807807
| Untagged FloatType -> str "number"
808+
| Untagged BooleanType -> str "boolean"
808809
| Untagged FunctionType -> str "function"
809810
| Untagged StringType -> str "string"
810811
| Untagged (InstanceType i) -> str (Ast_untagged_variants.Instance.to_string i) ~delim:DNoQuotes

jscomp/gentype/TranslateStructure.ml

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ let rec addAnnotationsToTypes_ ~config ~(expr : Typedtree.expression)
1111
let aName =
1212
if aName = "*opt*" then
1313
match arg_label with
14-
| Optional l ->
15-
l
14+
| Optional l -> l
1615
| _ -> "" (* should not happen *)
1716
else aName
1817
in

jscomp/ml/ast_untagged_variants.ml

+35-23
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type untaggedError =
2222
| AtMostOneFunction
2323
| AtMostOneString
2424
| AtMostOneNumber
25+
| AtMostOneBoolean
2526
| DuplicateLiteral of string
2627
| ConstructorMoreThanOneArg of string
2728
type error =
@@ -49,6 +50,7 @@ let report_error ppf =
4950
| AtMostOneInstance i -> "At most one case can be a " ^ (Instance.to_string i) ^ " type."
5051
| AtMostOneFunction -> "At most one case can be a function type."
5152
| AtMostOneString -> "At most one case can be a string type."
53+
| AtMostOneBoolean -> "At most one case can be a boolean type."
5254
| AtMostOneNumber ->
5355
"At most one case can be a number type (int or float)."
5456
| DuplicateLiteral s -> "Duplicate literal " ^ s ^ "."
@@ -59,6 +61,7 @@ type block_type =
5961
| IntType
6062
| StringType
6163
| FloatType
64+
| BooleanType
6265
| InstanceType of Instance.t
6366
| FunctionType
6467
| ObjectType
@@ -167,6 +170,8 @@ let get_block_type_from_typ ~env (t: Types.type_expr) : block_type option =
167170
Some IntType
168171
| {desc = Tconstr (path, _, _)} when Path.same path Predef.path_float ->
169172
Some FloatType
173+
| {desc = Tconstr (path, _, _)} when Path.same path Predef.path_bool ->
174+
Some BooleanType
170175
| ({desc = Tconstr _} as t) when Ast_uncurried_utils.typeIsUncurriedFun t ->
171176
Some FunctionType
172177
| {desc = Tarrow _} -> Some FunctionType
@@ -232,6 +237,7 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list)
232237
let objectTypes = ref 0 in
233238
let stringTypes = ref 0 in
234239
let numberTypes = ref 0 in
240+
let booleanTypes = ref 0 in
235241
let unknownTypes = ref 0 in
236242
let addStringLiteral ~loc s =
237243
if StringSet.mem s !string_literals then
@@ -258,6 +264,10 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list)
258264
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneString));
259265
if !numberTypes > 1 then
260266
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneNumber));
267+
if !booleanTypes > 1 then
268+
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneBoolean));
269+
if !booleanTypes > 0 && (StringSet.mem "true" !nonstring_literals || StringSet.mem "false" !nonstring_literals) then
270+
raise (Error (loc, InvalidUntaggedVariantDefinition AtMostOneBoolean));
261271
()
262272
in
263273
Ext_list.rev_iter consts (fun (loc, literal) ->
@@ -267,34 +277,27 @@ let checkInvariant ~isUntaggedDef ~(consts : (Location.t * tag) list)
267277
| Some (Float f) -> addNonstringLiteral ~loc f
268278
| Some Null -> addNonstringLiteral ~loc "null"
269279
| Some Undefined -> addNonstringLiteral ~loc "undefined"
270-
| Some (Bool b) ->
271-
addNonstringLiteral ~loc (if b then "true" else "false")
280+
| Some (Bool b) -> addNonstringLiteral ~loc (if b then "true" else "false")
272281
| Some (Untagged _) -> ()
273282
| None -> addStringLiteral ~loc literal.name);
274283
if isUntaggedDef then
275284
Ext_list.rev_iter blocks (fun (loc, block) ->
276-
let name = block.tag.name in
277-
match block.block_type with
278-
| Some UnknownType ->
279-
incr unknownTypes;
280-
invariant loc name
281-
| Some ObjectType ->
282-
incr objectTypes;
283-
invariant loc name
284-
| Some (InstanceType i) ->
285+
match block.block_type with
286+
| Some block_type ->
287+
(match block_type with
288+
| UnknownType -> incr unknownTypes;
289+
| ObjectType -> incr objectTypes;
290+
| (InstanceType i) ->
285291
let count = Hashtbl.find_opt instanceTypes i |> Option.value ~default:0 in
286292
Hashtbl.replace instanceTypes i (count + 1);
287-
invariant loc name
288-
| Some FunctionType ->
289-
incr functionTypes;
290-
invariant loc name
291-
| Some (IntType | FloatType) ->
292-
incr numberTypes;
293-
invariant loc name
294-
| Some StringType ->
295-
incr stringTypes;
296-
invariant loc name
297-
| None -> ())
293+
| FunctionType -> incr functionTypes;
294+
| (IntType | FloatType) -> incr numberTypes;
295+
| BooleanType -> incr booleanTypes;
296+
| StringType -> incr stringTypes;
297+
);
298+
invariant loc block.tag.name
299+
| None -> ()
300+
)
298301

299302
let names_from_type_variant ?(isUntaggedDef = false) ~env
300303
(cstrs : Types.constructor_declaration list) =
@@ -353,6 +356,7 @@ module DynamicChecks = struct
353356
let function_ = Untagged FunctionType |> tag_type
354357
let string = Untagged StringType |> tag_type
355358
let number = Untagged IntType |> tag_type
359+
let boolean = Untagged BooleanType |> tag_type
356360

357361
let ( == ) x y = bin EqEqEq x y
358362
let ( != ) x y = bin NotEqEq x y
@@ -371,6 +375,11 @@ module DynamicChecks = struct
371375
| Int _ | Float _ -> true
372376
| _ -> false)
373377
in
378+
let literals_overlaps_with_boolean () =
379+
Ext_list.exists literal_cases (function
380+
| Bool _ -> true
381+
| _ -> false)
382+
in
374383
let literals_overlaps_with_object () =
375384
Ext_list.exists literal_cases (function
376385
| Null -> true
@@ -386,6 +395,8 @@ module DynamicChecks = struct
386395
typeof e != number
387396
| FloatType when literals_overlaps_with_number () = false ->
388397
typeof e != number
398+
| BooleanType when literals_overlaps_with_boolean () = false ->
399+
typeof e != boolean
389400
| InstanceType i -> not (is_instance i e)
390401
| FunctionType -> typeof e != function_
391402
| ObjectType when literals_overlaps_with_object () = false ->
@@ -394,6 +405,7 @@ module DynamicChecks = struct
394405
| StringType (* overlap *)
395406
| IntType (* overlap *)
396407
| FloatType (* overlap *)
408+
| BooleanType (* overlap *)
397409
| UnknownType -> (
398410
(* We don't know the type of unknown, so we need to express:
399411
this is not one of the literals *)
@@ -434,7 +446,7 @@ module DynamicChecks = struct
434446
let add_runtime_type_check ~tag_type ~(block_cases : block_type list) x y =
435447
let instances = Ext_list.filter_map block_cases (function InstanceType i -> Some i | _ -> None) in
436448
match tag_type with
437-
| Untagged (IntType | StringType | FloatType | FunctionType) ->
449+
| Untagged (IntType | StringType | FloatType | BooleanType | FunctionType) ->
438450
typeof y == x
439451
| Untagged ObjectType ->
440452
if instances <> [] then

jscomp/test/variantsMatching.gen.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ export type MyNullable_t<a> = null | undefined | a;
1919

2020
// tslint:disable-next-line:interface-over-type-literal
2121
export type MyNullableExtended_t<a> = null | undefined | "WhyNotAnotherOne" | a;
22+
23+
// tslint:disable-next-line:interface-over-type-literal
24+
export type UntaggedWithBool_t<a> = string | number | boolean | string;

jscomp/test/variantsMatching.js

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/variantsMatching.res

+14
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,17 @@ module CustomTagNotInline = {
272272
let a = A(10)
273273
let b = B(20)
274274
}
275+
276+
module UntaggedWithBool = {
277+
@unboxed @genType
278+
type t<'a> = String(string) | Float(float) | Bool(bool) | Object({name: string})
279+
280+
let classify = x =>
281+
switch x {
282+
| String(_) => "string"
283+
| Float(_) => "int"
284+
| Bool(true) => "true"
285+
| Bool(_) => "boolean"
286+
| Object({name}) => "Object" ++ name
287+
}
288+
}

0 commit comments

Comments
 (0)