Skip to content
Open
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
53 changes: 40 additions & 13 deletions analysis/src/completion_decorators.ml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,46 @@ You will need this decorator whenever you want to use a JSX component in ReScrip
[
{|The `@editor.completeFrom` decorator instructs the editor where it can draw additional completions from for this type.|};
] );
( "instanceOf",
Some "instanceOf(\"$0\")",
[
{|The `@instanceOf` decorator states that the values of an abstract type are instances of a global JavaScript class. Such a type can be the payload of an untagged variant case, which is then recognized with `instanceof`, and is never `undefined`, like with `@notUndefined`.

A private abbreviation of another annotated type declares a subclass. A case of the superclass then excludes the instances of a subclass that is a case too, whatever the order of the cases.

Example usage:
```rescript
@instanceOf("Blob")
type blob

@instanceOf("File")
type file = private blob
```|};
] );
( "typeOf",
Some "typeOf(\"$0\")",
[
{|The `@typeOf` decorator states the JavaScript `typeof` of the values of an abstract type: one of `"string"`, `"number"`, `"bigint"`, `"boolean"`, `"symbol"`, `"function"` or `"object"`. Such a type can be the payload of an untagged variant case, and is never `undefined`, like with `@notUndefined`.

Example usage:
```rescript
@typeOf("symbol")
type key
```|};
] );
( "notUndefined",
None,
[
{|The `@notUndefined` decorator marks an abstract type as one that can never be `undefined` in JavaScript. This allows the compiler to generate more efficient code when the type is used inside an `option`.

Example usage:
```rescript
@notUndefined
type t
```

[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#notundefined-decorator).|};
] );
]

let toplevel =
Expand Down Expand Up @@ -331,17 +371,4 @@ let toplevel =

[Read more and see examples in the documentation](https://rescript-lang.org/docs/manual/latest/jsx#file-level-configuration).|};
] );
( "notUndefined",
None,
[
{|The `@notUndefined` decorator marks an abstract type as one that can never be `undefined` in JavaScript. This allows the compiler to generate more efficient code when the type is used inside an `option`.

Example usage:
```rescript
@notUndefined
type t
```

[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#notundefined-decorator).|};
] );
]
1 change: 1 addition & 0 deletions changes/instanceof-typeof-annotations.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `@instanceOf("Class")` and `@typeOf("…")` for abstract types and type abbreviations, so that values of bound JavaScript classes and opaque primitive types can be untagged variant cases. A private abbreviation of another annotated type declares a subclass.
1 change: 1 addition & 0 deletions changes/stdlib-instanceof-annotations.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- An untagged variant with both `Blob` and `File` cases matches files as `File`, whatever the order of its cases. `File.t` is now a private abbreviation of `Blob.t`, and `JsError.t`, `Symbol.t` and the `Intl` classes can be untagged variant cases.
1 change: 1 addition & 0 deletions changes/untagged-no-hardcoded-classes.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Untagged variants recognize stdlib classes only through their `@instanceOf` annotations; the compiler's list of stdlib type names is gone.
16 changes: 13 additions & 3 deletions compiler/core/js_exp_make.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1385,14 +1385,25 @@ let literal_tag = function
| Null -> nil
| Undefined -> undefined

(* The constructor of a class at a global path such as "Intl.Locale": a
global with properties, so that a binding that shadows the global's name
is noticed. *)
let class_constructor (instance : Variant_runtime.Instance.t) =
match
String.split_on_char '.' (Variant_runtime.Instance.to_string instance)
with
| [] -> assert false
| global :: properties -> List.fold_left dot (js_global global) properties

(* The [typeof] string an untagged payload answers to. *)
let block_type_name = function
| Variant_runtime.IntType | FloatType -> str "number"
| BigintType -> str "bigint"
| BooleanType -> str "boolean"
| SymbolType -> str "symbol"
| FunctionType -> str "function"
| StringType -> str "string"
| InstanceType i -> js_global (Variant_runtime.Instance.to_string i)
| InstanceType i -> class_constructor i
| ObjectType -> str "object"
| UnknownType ->
(* TODO: this should not happen *)
Expand All @@ -1417,8 +1428,7 @@ let rec emit_check (check : t Ast_untagged_variants.Dynamic_checks.t) =
| TypeOf x -> typeof (emit_check x)
| IsInstanceOf (Array, x) -> is_array (emit_check x)
| IsInstanceOf (instance, x) ->
let instance_name = Variant_runtime.Instance.to_string instance in
instanceof (emit_check x) (js_global instance_name)
instanceof (emit_check x) (class_constructor instance)
| Not x -> not (emit_check x)
| Expr x -> x

Expand Down
9 changes: 8 additions & 1 deletion compiler/core/lam_compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -885,13 +885,20 @@ let compile output_prefix =
let needs_array_guard =
has_object_typeof && type_has_array_case && not clauses_have_array_case
in
let instances =
Ext_list.filter_map block_cases (function
| Variant_runtime.InstanceType i -> Some i
| _ -> None)
in
let rec build_if_chain remaining_clauses =
match remaining_clauses with
| ( Variant_runtime.Payload_shape (InstanceType instance_type),
{J.switch_body} )
:: rest ->
S.if_
(E.emit_check (IsInstanceOf (instance_type, Expr e)))
(E.emit_check
(Ast_untagged_variants.Dynamic_checks.is_case_instance ~instances
instance_type (Expr e)))
switch_body
~else_:[build_if_chain rest]
| _ -> (
Expand Down
4 changes: 2 additions & 2 deletions compiler/ext/config.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let cmi_magic_number = "Caml1999I040"
let cmi_magic_number = "Caml1999I041"

(* Magic numbers for marshaled values of the *current* parsetree, whose layout
changes across compiler versions. *)
Expand All @@ -13,7 +13,7 @@ and ast0_impl_magic_number = "Caml1999M022"

and ast0_intf_magic_number = "Caml1999N022"

and cmt_magic_number = "Caml1999T048"
and cmt_magic_number = "Caml1999T049"

let get_load_path () = (Compiler_request_state.current ()).load_path
let set_load_path path = (Compiler_request_state.current ()).load_path <- path
Loading
Loading