Skip to content

Commit 9c027d6

Browse files
gkzfacebook-github-bot
authored andcommitted
[flow][enums] Add Enum type, which can represet any Flow Enum
Summary: Changelog: [feature] Added a new global type `Enum<>`, which represents all Flow Enums (this is a complement to the new `EnumValue` type). You can constrain the enums to ones which have a particular enum value, e.g. `Enum<>` is the same as `Enum<EnumValue<>>`, and the type of all enums with string representation types is `Enum<EnumValue<string>>`. These "abstract enums" have no know members, so you can't access members or exhaustively check them, but you can use methods such as `.members()`, `.cast()`, etc. The `Enum` type uses the `EnumType` type destructor, which uses the `GetEnumT` use_t. Reviewed By: SamChou19815 Differential Revision: D55536911 fbshipit-source-id: bbe5c1498add6a9e5c0c06222c4b4f597de78473
1 parent 28821d6 commit 9c027d6

29 files changed

+459
-83
lines changed

lib/core.js

+9
Original file line numberDiff line numberDiff line change
@@ -2838,3 +2838,12 @@ type $EnumValueProto<TEnumObject, TRepresentationType> = {|
28382838
valueOf(this: TEnumObject): TRepresentationType,
28392839
__proto__: null,
28402840
|};
2841+
2842+
/**
2843+
* Represents a generic Flow Enum - the enum itself rather than its values.
2844+
* You can supply a type argument to restrict to Flow Enums with a certain
2845+
* enum value (take a look at the `EnumValue` type for more).
2846+
* You can use the Flow Enum methods like `.cast` and `.members` on a value
2847+
* of this type, but it does not have any specific members which you can access.
2848+
*/
2849+
type Enum<+TEnumValue: EnumValue<> = EnumValue<>> = $Enum<TEnumValue>;

src/common/ty/ty.ml

+4
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ and utility =
267267
| Diff of t * t
268268
| Rest of t * t
269269
| ElementType of t * t
270+
| Enum of t
270271
| NonMaybeType of t
271272
| ObjMap of t * t
272273
| ObjMapi of t * t
@@ -677,6 +678,7 @@ class ['A] comparator_ty =
677678
| Required _ -> 24
678679
| ObjMapConst _ -> 25
679680
| ReactCheckComponentRef _ -> 26
681+
| Enum _ -> 27
680682

681683
method tag_of_polarity _ =
682684
function
@@ -830,6 +832,7 @@ let string_of_utility_ctor = function
830832
| Partial _ -> "Partial"
831833
| Required _ -> "Required"
832834
| Exact _ -> "$Exact"
835+
| Enum _ -> "Enum"
833836
| Diff _ -> "$Diff"
834837
| Rest _ -> "$Rest"
835838
| ElementType _ -> "$ElementType"
@@ -857,6 +860,7 @@ let types_of_utility = function
857860
| Diff (t1, t2) -> Some [t1; t2]
858861
| Rest (t1, t2) -> Some [t1; t2]
859862
| ElementType (t1, t2) -> Some [t1; t2]
863+
| Enum t -> Some [t]
860864
| NonMaybeType t -> Some [t]
861865
| ObjMap (t1, t2) -> Some [t1; t2]
862866
| ObjMapi (t1, t2) -> Some [t1; t2]

src/parser_utils/type_sig/type_sig.ml

+1
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ type ('loc, 'a) annot =
531531
elem: 'a;
532532
}
533533
| EnumValue of 'loc * 'a
534+
| Enum of 'loc * 'a
534535
| OptionalIndexedAccessNonMaybeType of {
535536
loc: 'loc;
536537
obj: 'a;

src/parser_utils/type_sig/type_sig_parse.ml

+7
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,13 @@ and maybe_special_unqualified_generic opts scope tbls xs loc targs ref_loc =
23302330
Annot (EnumValue (loc, t))
23312331
| _ -> Err (loc, CheckError)
23322332
end
2333+
| "$Enum" -> begin
2334+
match targs with
2335+
| Some (_, { arguments = [t]; _ }) ->
2336+
let t = annot opts scope tbls xs t in
2337+
Annot (Enum (loc, t))
2338+
| _ -> Err (loc, CheckError)
2339+
end
23332340
| "$NonMaybeType" -> begin
23342341
match targs with
23352342
| Some (_, { arguments = [t]; _ }) ->

src/typing/annotation_inference.ml

+10-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ let object_like_op = function
3939
| Annot_ObjKeyMirror _
4040
| Annot_ObjMapConst _
4141
| Annot_GetKeysT _
42+
| Annot_GetEnumT _
4243
| Annot_DeepReadOnlyT _
4344
| Annot_ToStringT _
4445
| Annot__Future_added_value__ _ ->
@@ -500,6 +501,9 @@ module rec ConsGen : S = struct
500501
| (EvalT (t, TypeDestructorT (use_op, reason, ElementType { index_type }), _), _) ->
501502
let t = elab_t cx t (Annot_GetElemT (reason, use_op, index_type)) in
502503
elab_t cx t op
504+
| (EvalT (t, TypeDestructorT (_, reason, EnumType), _), _) ->
505+
let t = elab_t cx t (Annot_GetEnumT reason) in
506+
elab_t cx t op
503507
| (EvalT (_, TypeDestructorT (_, _, TypeMap (ObjectMap _)), _), _) ->
504508
error_unsupported ~suggestion:"$ObjMapConst" cx t op
505509
| (EvalT (_, TypeDestructorT (_, _, TypeMap (ObjectMapi _)), _), _) ->
@@ -575,9 +579,9 @@ module rec ConsGen : S = struct
575579
(* a component syntax value annotation becomes an element of that component *)
576580
get_builtin_typeapp cx reason "React$Element" [l]
577581
| (DefT (_, TypeT (_, l)), Annot_UseT_TypeT _) -> l
578-
| (DefT (lreason, EnumObjectT enum_info), Annot_UseT_TypeT _) ->
579-
(* an enum object value annotation becomes the enum type *)
580-
mk_enum_type lreason enum_info
582+
| (DefT (_, EnumObjectT { enum_value_t; _ }), Annot_UseT_TypeT _) ->
583+
(* an enum object value annotation becomes the enum value type *)
584+
enum_value_t
581585
| (DefT (enum_reason, EnumValueT _), Annot_UseT_TypeT (reason, _)) ->
582586
Flow_js_utils.add_output cx Error_message.(EEnumMemberUsedAsType { reason; enum_reason });
583587
AnyT.error reason
@@ -1101,6 +1105,8 @@ module rec ConsGen : S = struct
11011105
Flow_js_utils.obj_key_mirror cx o reason_op
11021106
| (DefT (_, ObjT o), Annot_ObjMapConst (reason_op, target)) ->
11031107
Flow_js_utils.obj_map_const cx o reason_op target
1108+
| (DefT (_, EnumValueT enum_info), Annot_GetEnumT reason) ->
1109+
DefT (reason, EnumObjectT { enum_value_t = t; enum_info })
11041110
(***********************)
11051111
(* Opaque types (pt 2) *)
11061112
(***********************)
@@ -1138,11 +1144,10 @@ module rec ConsGen : S = struct
11381144
(*********)
11391145
(* Enums *)
11401146
(*********)
1141-
| ( DefT (enum_reason, EnumObjectT enum_info),
1147+
| ( DefT (enum_reason, EnumObjectT { enum_value_t; enum_info = ConcreteEnum enum_info }),
11421148
Annot_GetPropT (access_reason, use_op, Named { reason = prop_reason; name; _ })
11431149
) ->
11441150
let access = (use_op, access_reason, None, (prop_reason, name)) in
1145-
let enum_value_t = mk_enum_type enum_reason enum_info in
11461151
GetPropTKit.on_EnumObjectT
11471152
cx
11481153
dummy_trace

src/typing/debug_js.ml

+18-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ let string_of_destructor = function
3636
| NonMaybeType -> "NonMaybeType"
3737
| PropertyType { name; _ } -> spf "PropertyType %s" (display_string_of_name name)
3838
| ElementType _ -> "ElementType"
39+
| EnumType -> "EnumType"
3940
| OptionalIndexedAccessNonMaybeType _ -> "OptionalIndexedAccessNonMaybeType"
4041
| OptionalIndexedAccessResultType _ -> "OptionalIndexedAccessResultType"
4142
| ReadOnlyType -> "ReadOnly"
@@ -270,7 +271,8 @@ let rec dump_t_ (depth, tvars) cx t =
270271
| DefT (_, InstanceT inst_t) -> p ~extra:(instance_t inst_t) t
271272
| DefT (_, TypeT (kind, arg)) ->
272273
p ~extra:(spf "%s, %s" (string_of_type_t_kind kind) (kid arg)) t
273-
| DefT (_, EnumValueT (AbstractEnum { representation_t })) ->
274+
| DefT (_, EnumValueT (AbstractEnum { representation_t }))
275+
| DefT (_, EnumObjectT { enum_info = AbstractEnum { representation_t }; _ }) ->
274276
p ~extra:(spf "abstract: %s" (kid representation_t)) t
275277
| DefT
276278
( _,
@@ -282,7 +284,12 @@ let rec dump_t_ (depth, tvars) cx t =
282284
| DefT
283285
( _,
284286
EnumObjectT
285-
{ enum_name; enum_id; members = _; representation_t = _; has_unknown_members = _ }
287+
{
288+
enum_info =
289+
ConcreteEnum
290+
{ enum_name; enum_id; members = _; representation_t = _; has_unknown_members = _ };
291+
_;
292+
}
286293
) ->
287294
p ~extra:(spf "enum concrete: %s #%s" enum_name (ALoc.debug_to_string (enum_id :> ALoc.t))) t
288295
| AnnotT (_, arg, use_desc) -> p ~extra:(spf "use_desc=%b, %s" use_desc (kid arg)) t
@@ -975,6 +982,13 @@ and dump_use_t_ (depth, tvars) cx t =
975982
| EnumExhaustiveCheckInvalid _ -> "EnumExhaustiveCheckInvalid"
976983
in
977984
p ~extra:check_str t
985+
| GetEnumT { kind; _ } ->
986+
p
987+
~extra:
988+
(match kind with
989+
| `GetEnumObject -> "get enum object"
990+
| `GetEnumValue -> "get enum value")
991+
t
978992
| FilterOptionalT (_, arg) -> p ~reason:false ~extra:(kid arg) t
979993
| FilterMaybeT (_, arg) -> p ~reason:false ~extra:(kid arg) t
980994
| DeepReadOnlyT ((_, tv), _, _) -> p ~extra:(tvar tv) t
@@ -1862,6 +1876,8 @@ let dump_error_message =
18621876
| `ConcreteEnum -> "concrete"
18631877
| `AbstractEnum -> "abstract")
18641878
(Base.Option.value ~default:"<None>" representation_type)
1879+
| EEnumInvalidAbstractUse { reason; enum_reason } ->
1880+
spf "EEnumInvalidAbstractUse (%s) (%s)" (dump_reason cx reason) (dump_reason cx enum_reason)
18651881
| EAssignConstLikeBinding { loc; definition; binding_kind } ->
18661882
spf
18671883
"EAssignConstLikeBinding (%s) (%s) (%s)"

src/typing/default_resolve.ml

+1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ let rec default_resolve_touts ~flow ?resolve_callee cx loc u =
202202
| DestructuringT (_, _, _, tvar, _) -> resolve_tvar tvar
203203
| ResolveUnionT { upper; _ } -> default_resolve_touts ~flow cx loc upper
204204
| TypeCastT (_, t) -> resolve t
205+
| GetEnumT { tout; _ } -> resolve tout
205206
| EnumCastT _
206207
| EnumExhaustiveCheckT _ ->
207208
()

src/typing/env_resolution.ml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1052,8 +1052,9 @@ let resolve_declare_namespace cx loc ns =
10521052

10531053
let resolve_enum cx id_loc enum_reason enum_loc name enum =
10541054
if Context.enable_enums cx then
1055-
let enum_info = Statement.mk_enum cx ~enum_reason id_loc name enum in
1056-
(DefT (enum_reason, EnumObjectT enum_info), unknown_use)
1055+
let enum_info = ConcreteEnum (Statement.mk_enum cx ~enum_reason id_loc name enum) in
1056+
let t = mk_enum_object_type enum_reason enum_info in
1057+
(t, unknown_use)
10571058
else (
10581059
Flow_js_utils.add_output cx (Error_message.EEnumsNotEnabled enum_loc);
10591060
(AnyT.error enum_reason, unknown_use)

src/typing/errors/error_message.ml

+20
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,10 @@ and 'loc t' =
570570
representation_type: string option;
571571
casting_syntax: Options.CastingSyntax.t;
572572
}
573+
| EEnumInvalidAbstractUse of {
574+
reason: 'loc virtual_reason;
575+
enum_reason: 'loc virtual_reason;
576+
}
573577
(* end enum error messages *)
574578
| EAssignConstLikeBinding of {
575579
loc: 'loc;
@@ -1362,6 +1366,8 @@ let rec map_loc_of_error_message (f : 'a -> 'b) : 'a t' -> 'b t' =
13621366
representation_type;
13631367
casting_syntax;
13641368
}
1369+
| EEnumInvalidAbstractUse { reason; enum_reason } ->
1370+
EEnumInvalidAbstractUse { reason = map_reason reason; enum_reason = map_reason enum_reason }
13651371
| EAssignConstLikeBinding { loc; definition; binding_kind } ->
13661372
EAssignConstLikeBinding { loc = f loc; definition = map_reason definition; binding_kind }
13671373
| ECannotResolveOpenTvar { use_op; reason; blame_reasons } ->
@@ -1740,6 +1746,7 @@ let util_use_op_of_msg nope util = function
17401746
| EEnumAllMembersAlreadyChecked _
17411747
| EEnumNotAllChecked _
17421748
| EEnumUnknownNotChecked _
1749+
| EEnumInvalidAbstractUse _
17431750
| EEnumInvalidCheck _
17441751
| EEnumMemberUsedAsType _
17451752
| EAssignConstLikeBinding _
@@ -1835,6 +1842,7 @@ let loc_of_msg : 'loc t' -> 'loc option = function
18351842
| EEnumAllMembersAlreadyChecked { reason; _ }
18361843
| EEnumNotAllChecked { reason; _ }
18371844
| EEnumUnknownNotChecked { reason; _ }
1845+
| EEnumInvalidAbstractUse { reason; _ }
18381846
| EEnumInvalidCheck { reason; _ }
18391847
| EEnumMemberUsedAsType { reason; _ }
18401848
| EEnumInvalidMemberAccess { reason; _ }
@@ -4699,6 +4707,17 @@ let friendly_message_of_msg loc_of_aloc msg =
46994707
| _ -> None
47004708
in
47014709
IncompatibleEnum { reason_lower; reason_upper; use_op; suggestion }
4710+
| EEnumInvalidAbstractUse { reason; enum_reason } ->
4711+
let features =
4712+
[
4713+
text "Cannot exhaustively check ";
4714+
desc reason;
4715+
text " because ";
4716+
ref enum_reason;
4717+
text " is an abstract enum value, so has no members.";
4718+
]
4719+
in
4720+
Normal { features }
47024721
| EAssignConstLikeBinding { definition; binding_kind; _ } ->
47034722
let features =
47044723
[
@@ -5851,6 +5870,7 @@ let error_code_of_message err : error_code option =
58515870
end
58525871
| EDuplicateModuleProvider _ -> Some DuplicateModule
58535872
| EEnumAllMembersAlreadyChecked _ -> Some InvalidExhaustiveCheck
5873+
| EEnumInvalidAbstractUse _ -> Some InvalidExhaustiveCheck
58545874
| EEnumInvalidCheck _ -> Some InvalidExhaustiveCheck
58555875
| EEnumInvalidMemberAccess _ -> Some InvalidEnumAccess
58565876
| EEnumInvalidObjectUtilType _ -> Some NotAnObject

src/typing/flow_js.ml

+39-8
Original file line numberDiff line numberDiff line change
@@ -5319,7 +5319,7 @@ struct
53195319
(*********)
53205320
(* enums *)
53215321
(*********)
5322-
| ( DefT (enum_reason, EnumObjectT enum_info),
5322+
| ( DefT (enum_reason, EnumObjectT { enum_value_t; enum_info = ConcreteEnum enum_info }),
53235323
GetPropT
53245324
{
53255325
use_op;
@@ -5332,7 +5332,6 @@ struct
53325332
}
53335333
) ->
53345334
let access = (use_op, access_reason, None, (prop_reason, member_name)) in
5335-
let enum_value_t = mk_enum_type enum_reason enum_info in
53365335
GetPropTKit.on_EnumObjectT
53375336
cx
53385337
trace
@@ -5359,13 +5358,17 @@ struct
53595358
hint;
53605359
}
53615360
)
5362-
| ( DefT (enum_reason, EnumObjectT enum_info),
5361+
| ( DefT (_, EnumObjectT { enum_value_t; enum_info }),
53635362
MethodT (use_op, call_reason, lookup_reason, (Named _ as propref), action)
53645363
) ->
53655364
let t =
53665365
Tvar.mk_no_wrap_where cx lookup_reason (fun tout ->
5367-
let enum_value_t = mk_enum_type enum_reason enum_info in
5368-
let { representation_t; _ } = enum_info in
5366+
let representation_t =
5367+
match enum_info with
5368+
| ConcreteEnum { representation_t; _ }
5369+
| AbstractEnum { representation_t } ->
5370+
representation_t
5371+
in
53695372
rec_flow
53705373
cx
53715374
trace
@@ -5560,6 +5563,25 @@ struct
55605563
continue cx trace (GenericT { reason; id; name; bound = l; no_infer }) cont
55615564
| (GenericT { reason; bound; _ }, _) ->
55625565
rec_flow cx trace (reposition_reason cx reason bound, u)
5566+
(************)
5567+
(* GetEnumT *)
5568+
(************)
5569+
| ( DefT (enum_reason, EnumValueT enum_info),
5570+
GetEnumT { use_op; orig_t; kind = `GetEnumObject; tout; _ }
5571+
) ->
5572+
let enum_value_t = Base.Option.value ~default:l orig_t in
5573+
rec_flow
5574+
cx
5575+
trace
5576+
(DefT (enum_reason, EnumObjectT { enum_value_t; enum_info }), UseT (use_op, tout))
5577+
| (_, GetEnumT { use_op; kind = `GetEnumObject; tout; _ }) ->
5578+
rec_flow cx trace (l, UseT (use_op, tout))
5579+
| ( DefT (_, EnumObjectT { enum_value_t; _ }),
5580+
GetEnumT { use_op; kind = `GetEnumValue; tout; _ }
5581+
) ->
5582+
rec_flow cx trace (enum_value_t, UseT (use_op, tout))
5583+
| (_, GetEnumT { use_op; kind = `GetEnumValue; tout; _ }) ->
5584+
rec_flow cx trace (l, UseT (use_op, tout))
55635585
(**********************************)
55645586
(* Flow Enums exhaustive checking *)
55655587
(**********************************)
@@ -5586,6 +5608,8 @@ struct
55865608
~default_case
55875609
~incomplete_out
55885610
~discriminant_after_check
5611+
| (DefT (enum_reason, EnumValueT (AbstractEnum _)), EnumExhaustiveCheckT { reason; _ }) ->
5612+
add_output cx (Error_message.EEnumInvalidAbstractUse { reason; enum_reason })
55895613
(* Resolving the case tests. *)
55905614
| ( _,
55915615
EnumExhaustiveCheckT
@@ -5607,7 +5631,7 @@ struct
56075631
let { enum_id = enum_id_discriminant; members; _ } = discriminant_enum in
56085632
let checks =
56095633
match l with
5610-
| DefT (_, EnumObjectT { enum_id = enum_id_check; _ })
5634+
| DefT (_, EnumObjectT { enum_info = ConcreteEnum { enum_id = enum_id_check; _ }; _ })
56115635
when ALoc.equal_id enum_id_discriminant enum_id_check && SMap.mem member_name members
56125636
->
56135637
check :: checks
@@ -5626,7 +5650,7 @@ struct
56265650
~default_case
56275651
~incomplete_out
56285652
~discriminant_after_check
5629-
| ( DefT (enum_reason, EnumValueT (ConcreteEnum { members; _ })),
5653+
| ( DefT (enum_reason, EnumValueT enum_info),
56305654
EnumExhaustiveCheckT
56315655
{
56325656
reason;
@@ -5635,7 +5659,11 @@ struct
56355659
discriminant_after_check = _;
56365660
}
56375661
) ->
5638-
let example_member = SMap.choose_opt members |> Base.Option.map ~f:fst in
5662+
let example_member =
5663+
match enum_info with
5664+
| ConcreteEnum { members; _ } -> SMap.choose_opt members |> Base.Option.map ~f:fst
5665+
| AbstractEnum _ -> None
5666+
in
56395667
List.iter
56405668
(fun reason ->
56415669
add_output cx (Error_message.EEnumInvalidCheck { reason; enum_reason; example_member }))
@@ -6812,6 +6840,7 @@ struct
68126840
| AssertExportIsTypeT _
68136841
| ImplicitVoidReturnT _
68146842
| GetElemT _
6843+
| GetEnumT _
68156844
| GetKeysT _
68166845
| GetPrivatePropT _
68176846
| GetPropT _
@@ -7883,6 +7912,8 @@ struct
78837912
OpenT tout
78847913
)
78857914
)
7915+
| EnumType ->
7916+
GetEnumT { use_op; reason; orig_t = Some t; kind = `GetEnumObject; tout = OpenT tout }
78867917
))
78877918

78887919
and eagerly_eval_destructor_if_resolved cx ~trace use_op reason t d tvar =

0 commit comments

Comments
 (0)