Skip to content

Add Core standard library support for genType #6019

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 14 commits into from
Mar 2, 2023
Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ subset of the arguments, and return a curried type with the remaining ones https
- 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
- Add experimental suppport for directives. An annotation such as `@@directive("use client;")` emits `use client;` verbatim before imports https://github.com/rescript-lang/rescript-compiler/pull/5998
- `genType`: add `Core` standard library support for the following builtin types: `Null.t`, `Nullable.t`, `Undefined.t`, `Dict.t<_>`, `Promise.t<_>`, `Date.t`, `BigInt.t`, `RegExp.t`, `Map.t<_, _>`, `WeakMap.t<_, _>`, `Set<_>`, `WeakSet<_>` https://github.com/rescript-lang/rescript-compiler/pull/6019

#### :boom: Breaking Change

Expand All @@ -39,6 +40,9 @@ subset of the arguments, and return a curried type with the remaining ones https
Also, `(. int) => string => bool` is not equivalen to `(. int, string) => bool` anymore.
These are only breaking changes for unformatted code.
- Exponentiation operator `**` is now right-associative. `2. ** 3. ** 2.` now compile to `Math.pow(2, Math.pow(3, 2))` and not anymore `Math.pow(Math.pow(2, 3), 2)`. Parentheses can be used to change precedence.
- `genType`: streamline the treatment of optionals as undefined https://github.com/rescript-lang/rescript-compiler/pull/6022
- Represent `option<t>` as `undefined | t` instead of `null | undefined | t`. This is more permissive when importing functions taking optional values (allows to use option types), but stricter when e.g. exporting ReScript functions taking arguments of option type. Fallback: use `Js.undefined<_>` instead.
- Represent `{x:option<string>}` as `{x:(undefined | string)}` instead of `{x?: string}`. This is more in line with TS's behaviour. Fallback: use `{x?:string}`.

#### :bug: Bug Fix

Expand Down
96 changes: 11 additions & 85 deletions jscomp/gentype/Converter.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ type t =
| CircularC of string * t
| FunctionC of functionC
| IdentC
| NullableC of t
| ObjectC of fieldsC
| OptionC of t
| PromiseC of t
| RecordC of fieldsC
| TupleC of t list
| VariantC of variantC

Expand Down Expand Up @@ -68,8 +66,7 @@ let rec toString converter =
|> String.concat ", ")
^ " -> " ^ toString retConverter ^ ")"
| IdentC -> "id"
| NullableC c -> "nullable(" ^ toString c ^ ")"
| ObjectC fieldsC | RecordC fieldsC ->
| ObjectC fieldsC ->
let dot =
match converter with
| ObjectC _ -> ". "
Expand Down Expand Up @@ -120,6 +117,7 @@ let typeGetConverterNormalized ~config ~inline ~lookupId ~typeNameIsInterface
| Array (t, mutable_) ->
let tConverter, tNormalized = t |> visit ~visited in
(ArrayC tConverter, Array (tNormalized, mutable_))
| Dict _ -> (IdentC, normalized_)
| Function
({argTypes; componentName; retType; typeVars; uncurried} as function_)
->
Expand Down Expand Up @@ -189,10 +187,10 @@ let typeGetConverterNormalized ~config ~inline ~lookupId ~typeNameIsInterface
else (IdentC, normalized_))
| Null t ->
let tConverter, tNormalized = t |> visit ~visited in
(NullableC tConverter, Null tNormalized)
(OptionC tConverter, Null tNormalized)
| Nullable t ->
let tConverter, tNormalized = t |> visit ~visited in
(NullableC tConverter, Nullable tNormalized)
(OptionC tConverter, Nullable tNormalized)
| Object (closedFlag, fields) ->
let fieldsConverted =
fields
Expand Down Expand Up @@ -220,26 +218,6 @@ let typeGetConverterNormalized ~config ~inline ~lookupId ~typeNameIsInterface
| Promise t ->
let tConverter, tNormalized = t |> visit ~visited in
(PromiseC tConverter, Promise tNormalized)
| Record fields ->
let fieldsConverted =
fields
|> List.map (fun ({type_} as field) -> (field, type_ |> visit ~visited))
in
( RecordC
(fieldsConverted
|> List.map (fun ({nameJS; nameRE; optional}, (converter, _)) ->
{
lblJS = nameJS;
lblRE = nameRE;
c =
(match optional = Mandatory with
| true -> converter
| false -> OptionC converter);
})),
Record
(fieldsConverted
|> List.map (fun (field, (_, tNormalized)) ->
{field with type_ = tNormalized})) )
| Tuple innerTypes ->
let innerConversions, normalizedList =
innerTypes |> List.map (visit ~visited) |> List.split
Expand Down Expand Up @@ -379,7 +357,6 @@ let rec converterIsIdentity ~config ~toJS converter =
argConverter |> converterIsIdentity ~config ~toJS:(not toJS)
| GroupConverter _ -> false)
| IdentC -> true
| NullableC c -> c |> converterIsIdentity ~config ~toJS
| ObjectC fieldsC ->
fieldsC
|> List.for_all (fun {lblJS; lblRE; c} ->
Expand All @@ -388,9 +365,8 @@ let rec converterIsIdentity ~config ~toJS converter =
match c with
| OptionC c1 -> c1 |> converterIsIdentity ~config ~toJS
| _ -> c |> converterIsIdentity ~config ~toJS)
| OptionC c -> if toJS then c |> converterIsIdentity ~config ~toJS else false
| OptionC c -> c |> converterIsIdentity ~config ~toJS
| PromiseC c -> c |> converterIsIdentity ~config ~toJS
| RecordC _ -> false
| TupleC innerTypesC ->
innerTypesC |> List.for_all (converterIsIdentity ~config ~toJS)
| VariantC {withPayloads; useVariantTables} ->
Expand Down Expand Up @@ -518,13 +494,6 @@ let rec apply ~config ~converter ~indent ~nameGen ~toJS ~variantTables value =
EmitText.funDef ~bodyArgs ~functionName:componentName ~funParams ~indent
~mkBody ~typeVars
| IdentC -> value
| NullableC c ->
EmitText.parens
[
value ^ " == null ? " ^ value ^ " : "
^ (value
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables);
]
| ObjectC fieldsC ->
let simplifyFieldConverted fieldConverter =
match fieldConverter with
Expand Down Expand Up @@ -553,62 +522,19 @@ let rec apply ~config ~converter ~indent ~nameGen ~toJS ~variantTables value =
in
"{" ^ fieldValues ^ "}"
| OptionC c ->
if toJS then
EmitText.parens
[
value ^ " == null ? " ^ value ^ " : "
^ (value
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables
);
]
else
EmitText.parens
[
value ^ " == null ? undefined : "
^ (value
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables
);
]
EmitText.parens
[
value ^ " == null ? " ^ value ^ " : "
^ (value
|> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables);
]
| PromiseC c ->
let x = "$promise" |> EmitText.name ~nameGen in
value ^ ".then(function _element("
^ (x |> EmitType.ofTypeAny ~config)
^ ") { return "
^ (x |> apply ~config ~converter:c ~indent ~nameGen ~toJS ~variantTables)
^ "})"
| RecordC fieldsC ->
let simplifyFieldConverted fieldConverter =
match fieldConverter with
| OptionC converter1 when converter1 |> converterIsIdentity ~config ~toJS
->
IdentC
| _ -> fieldConverter
in
if toJS then
let fieldValues =
fieldsC
|> List.mapi (fun index {lblJS; c = fieldConverter} ->
lblJS ^ ":"
^ (value
|> EmitText.arrayAccess ~index
|> apply ~config
~converter:(fieldConverter |> simplifyFieldConverted)
~indent ~nameGen ~toJS ~variantTables))
|> String.concat ", "
in
"{" ^ fieldValues ^ "}"
else
let fieldValues =
fieldsC
|> List.map (fun {lblJS; c = fieldConverter} ->
value
|> EmitText.fieldAccess ~label:lblJS
|> apply ~config
~converter:(fieldConverter |> simplifyFieldConverted)
~indent ~nameGen ~toJS ~variantTables)
|> String.concat ", "
in
"[" ^ fieldValues ^ "]"
| TupleC innerTypesC ->
"["
^ (innerTypesC
Expand Down
6 changes: 3 additions & 3 deletions jscomp/gentype/EmitJs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ let typeNameIsInterface ~(exportTypeMap : CodeItem.exportTypeMap)
~(exportTypeMapFromOtherFiles : CodeItem.exportTypeMap) typeName =
let typeIsInterface type_ =
match type_ with
| Object _ | Record _ -> true
| Object _ -> true
| _ -> false
in
match exportTypeMap |> StringMap.find typeName with
Expand Down Expand Up @@ -620,11 +620,11 @@ let propagateAnnotationToSubTypes ~codeItems (typeMap : CodeItem.exportTypeMap)
type1 |> visit
| exception Not_found ->
annotatedSet := !annotatedSet |> StringSet.add typeName)
| Array (t, _) -> t |> visit
| Array (t, _) | Dict t -> t |> visit
| Function {argTypes; retType} ->
argTypes |> List.iter (fun {aType} -> visit aType);
retType |> visit
| GroupOfLabeledArgs fields | Object (_, fields) | Record fields ->
| GroupOfLabeledArgs fields | Object (_, fields) ->
fields |> List.iter (fun {type_} -> type_ |> visit)
| Option t | Null t | Nullable t | Promise t -> t |> visit
| Tuple innerTypes -> innerTypes |> List.iter visit
Expand Down
18 changes: 16 additions & 2 deletions jscomp/gentype/EmitType.ml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
arrayName ^ "<"
^ (t |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
^ ">"
| Dict type_ ->
"{[id: string]: "
^ (type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
^ "}"
| Function
{argTypes = [{aType = Object (closedFlag, fields)}]; retType; typeVars}
when retType |> isTypeFunctionComponent ~fields ->
Expand All @@ -116,7 +120,7 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
| Function {argTypes; retType; typeVars} ->
renderFunType ~config ~indent ~inFunType ~typeNameIsInterface ~typeVars
argTypes retType
| GroupOfLabeledArgs fields | Object (_, fields) | Record fields ->
| GroupOfLabeledArgs fields | Object (_, fields) ->
let indent1 = fields |> Indent.heuristicFields ~indent in
fields
|> renderFields ~config ~indent:indent1 ~inFunType ~typeNameIsInterface
Expand All @@ -136,7 +140,7 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
"(null | "
^ (type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
^ ")"
| Nullable type_ | Option type_ ->
| Nullable type_ ->
let useParens x =
match type_ with
| Function _ | Variant _ -> EmitText.parens [x]
Expand All @@ -146,6 +150,16 @@ let rec renderType ~(config : Config.t) ?(indent = None) ~typeNameIsInterface
^ useParens
(type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
^ ")"
| Option type_ ->
let useParens x =
match type_ with
| Function _ | Variant _ -> EmitText.parens [x]
| _ -> x
in
"(undefined | "
^ useParens
(type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
^ ")"
| Promise type_ ->
"Promise" ^ "<"
^ (type_ |> renderType ~config ~indent ~typeNameIsInterface ~inFunType)
Expand Down
10 changes: 8 additions & 2 deletions jscomp/gentype/GenTypeCommon.ml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type closedFlag = Open | Closed

type type_ =
| Array of type_ * mutable_
| Dict of type_
| Function of function_
| GroupOfLabeledArgs of fields
| Ident of ident
Expand All @@ -67,7 +68,6 @@ type type_ =
| Object of closedFlag * fields
| Option of type_
| Promise of type_
| Record of fields
| Tuple of type_ list
| TypeVar of string
| Variant of variant
Expand Down Expand Up @@ -108,6 +108,7 @@ and payload = {case: case; inlineRecord: bool; numArgs: int; t: type_}
let typeIsObject type_ =
match type_ with
| Array _ -> true
| Dict _ -> true
| Function _ -> false
| GroupOfLabeledArgs _ -> false
| Ident _ -> false
Expand All @@ -116,7 +117,6 @@ let typeIsObject type_ =
| Object _ -> true
| Option _ -> false
| Promise _ -> true
| Record _ -> true
| Tuple _ -> true
| TypeVar _ -> false
| Variant _ -> false
Expand Down Expand Up @@ -210,11 +210,17 @@ let sanitizeTypeName name =
| '\'' -> '_'
| c -> c)
let unknown = ident "unknown"
let bigintT = ident "BigInt"
let booleanT = ident "boolean"
let dateT = ident "Date"
let mapT (x,y) = ident ~typeArgs:[x;y] "Map"
let numberT = ident "number"
let regexpT = ident "RegExp"
let setT (x) = ident ~typeArgs:[x] "Set"
let stringT = ident "string"
let unitT = ident "void"
let weakmapT (x,y) = ident ~typeArgs:[x;y] "WeakMap"
let weaksetT (x) = ident ~typeArgs:[x] "WeakSet"
let int64T = Tuple [numberT; numberT]

module NodeFilename = struct
Expand Down
5 changes: 3 additions & 2 deletions jscomp/gentype/TranslateSignatureFromTypes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ let translateTypeDeclarationFromTypes ~config ~outputFileRelative ~resolver
Log_.item "Translate Types.type_declaration %s\n" typeName;
let declarationKind =
match type_kind with
| Type_record (labelDeclarations, _) ->
TranslateTypeDeclarations.RecordDeclarationFromTypes labelDeclarations
| Type_record (labelDeclarations, recordRepresentation) ->
TranslateTypeDeclarations.RecordDeclarationFromTypes
(labelDeclarations, recordRepresentation)
| Type_variant constructorDeclarations
when not
(TranslateTypeDeclarations.hasSomeGADTLeaf constructorDeclarations)
Expand Down
27 changes: 19 additions & 8 deletions jscomp/gentype/TranslateTypeDeclarations.ml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
open GenTypeCommon

type declarationKind =
| RecordDeclarationFromTypes of Types.label_declaration list
| RecordDeclarationFromTypes of
Types.label_declaration list * Types.record_representation
| GeneralDeclaration of Typedtree.core_type option
| GeneralDeclarationFromTypes of Types.type_expr option
(** As the above, but from Types not Typedtree *)
Expand Down Expand Up @@ -78,7 +79,12 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
in
{CodeItem.importTypes; exportFromTypeDeclaration}
in
let translateLabelDeclarations labelDeclarations =
let translateLabelDeclarations ~recordRepresentation labelDeclarations =
let isOptional l =
match recordRepresentation with
| Types.Record_optional_labels lbls -> List.mem l lbls
| _ -> false
in
let fieldTranslations =
labelDeclarations
|> List.map (fun {Types.ld_id; ld_mutable; ld_type; ld_attributes} ->
Expand Down Expand Up @@ -111,7 +117,7 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
->
let optional, type1 =
match type_ with
| Option type1 -> (Optional, type1)
| Option type1 when isOptional nameRE -> (Optional, type1)
| _ -> (Mandatory, type_)
in
{mutable_; nameJS; nameRE; optional; type_ = type1})
Expand Down Expand Up @@ -196,9 +202,10 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
in
{translation with type_} |> handleGeneralDeclaration
|> returnTypeDeclaration
| RecordDeclarationFromTypes labelDeclarations, None ->
| RecordDeclarationFromTypes (labelDeclarations, recordRepresentation), None
->
let {TranslateTypeExprFromTypes.dependencies; type_} =
labelDeclarations |> translateLabelDeclarations
labelDeclarations |> translateLabelDeclarations ~recordRepresentation
in
let importTypes =
dependencies
Expand Down Expand Up @@ -227,7 +234,11 @@ let traslateDeclarationKind ~config ~loc ~outputFileRelative ~resolver
|> TranslateTypeExprFromTypes.translateTypeExprsFromTypes
~config ~typeEnv
| Cstr_record labelDeclarations ->
[labelDeclarations |> translateLabelDeclarations]
[
labelDeclarations
|> translateLabelDeclarations
~recordRepresentation:Types.Record_regular;
]
in
let inlineRecord =
match constructorArgs with
Expand Down Expand Up @@ -342,8 +353,8 @@ let translateTypeDeclaration ~config ~outputFileRelative ~recursive ~resolver
in
let declarationKind =
match typ_type.type_kind with
| Type_record (labelDeclarations, _) ->
RecordDeclarationFromTypes labelDeclarations
| Type_record (labelDeclarations, recordRepresentation) ->
RecordDeclarationFromTypes (labelDeclarations, recordRepresentation)
| Type_variant constructorDeclarations ->
VariantDeclarationFromTypes constructorDeclarations
| Type_abstract -> GeneralDeclaration typ_manifest
Expand Down
Loading