Skip to content

Commit 34a542c

Browse files
gkzmeta-codesync[bot]
authored andcommitted
[flow][records] Customize record declaration type-checking - treat them as exact
Summary: This diff adds a new instance_kind: `RecordKind`, and hooks everything up so it is correctly set for records. Using this, we begin customizing the behavior of records. First of all, they are treated as exact (in `match` and when turning into plain objects). Changelog: [internal] Reviewed By: SamChou19815 Differential Revision: D86821062 fbshipit-source-id: ab9c1054dfbca0ce14be38dae1a397b3ae69c496
1 parent 685f9f1 commit 34a542c

20 files changed

+121
-51
lines changed

src/analysis/env_builder/__tests__/name_def_test.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ x = 10;
504504
|};
505505
[%expect {|
506506
[
507-
(3, 7) to (3, 8) => class R;
507+
(3, 7) to (3, 8) => record R;
508508
(4, 10) to (4, 11) => val (4, 14) to (4, 16);
509509
(6, 0) to (6, 1) => val (6, 4) to (6, 6)
510510
] |}]

src/analysis/env_builder/name_def.ml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,7 +1890,9 @@ class def_finder ~autocomplete_hooks ~react_jsx env_info toplevel_scope =
18901890
scope_kind
18911891
()
18921892

1893-
method! class_ loc expr =
1893+
method! class_ loc expr = this#class_internal ~kind:ClassKind.Class loc expr
1894+
1895+
method private class_internal ~kind loc expr =
18941896
let open Ast.Class in
18951897
let { id; body; tparams = class_tparams; extends; implements; class_decorators; comments = _ }
18961898
=
@@ -1954,13 +1956,13 @@ class def_finder ~autocomplete_hooks ~react_jsx env_info toplevel_scope =
19541956
this#add_ordinary_binding
19551957
id_loc
19561958
reason
1957-
(Class { class_loc = loc; class_ = expr; this_super_write_locs })
1959+
(Class { class_loc = loc; class_ = expr; this_super_write_locs; kind })
19581960
| None ->
19591961
let reason = mk_reason (RType (OrdinaryName "<<anonymous class>>")) loc in
19601962
this#add_ordinary_binding
19611963
loc
19621964
reason
1963-
(Class { class_loc = loc; class_ = expr; this_super_write_locs })
1965+
(Class { class_loc = loc; class_ = expr; this_super_write_locs; kind })
19641966
end;
19651967
expr
19661968
)
@@ -2018,7 +2020,8 @@ class def_finder ~autocomplete_hooks ~react_jsx env_info toplevel_scope =
20182020
meth
20192021

20202022
method! record_declaration loc record =
2021-
ignore @@ this#class_ loc (Flow_ast_utils.class_of_record record);
2023+
ignore
2024+
@@ this#class_internal ~kind:ClassKind.Record loc (Flow_ast_utils.class_of_record record);
20222025
record
20232026

20242027
method! declare_function loc (decl : ('loc, 'loc) Ast.Statement.DeclareFunction.t) =

src/analysis/env_builder/name_def_ordering.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,8 @@ struct
12181218
EnvMap.empty
12191219
| Component { tparams_map; component; component_loc = _ } ->
12201220
depends_of_component tparams_map component EnvMap.empty
1221-
| Class { class_; class_loc = _; this_super_write_locs = _ } -> depends_of_class class_
1221+
| Class { class_; class_loc = _; this_super_write_locs = _; kind = _ } ->
1222+
depends_of_class class_
12221223
| DeclaredClass (_, decl) -> depends_of_declared_class decl
12231224
| DeclaredComponent (loc, decl) -> depends_of_declared_component loc decl
12241225
| TypeAlias (_, alias) -> depends_of_alias alias

src/analysis/env_builder/name_def_types.ml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ type expression_def = {
166166
hints: ast_hints;
167167
}
168168

169+
module ClassKind = struct
170+
type t =
171+
| Class
172+
| Record
173+
end
174+
169175
type def =
170176
| Binding of binding
171177
| MatchCasePattern of {
@@ -210,6 +216,7 @@ type def =
210216
class_loc: ALoc.t;
211217
(* A set of this and super write locations that can be resolved by resolving the class. *)
212218
this_super_write_locs: Env_api.EnvSet.t;
219+
kind: ClassKind.t;
213220
}
214221
| DeclaredClass of ALoc.t * (ALoc.t, ALoc.t) Ast.Statement.DeclareClass.t
215222
| DeclaredComponent of ALoc.t * (ALoc.t, ALoc.t) Ast.Statement.DeclareComponent.t
@@ -327,9 +334,15 @@ module Print = struct
327334
| DeclaredComponent
328335
(_, { Ast.Statement.DeclareComponent.id = (_, { Ast.Identifier.name; _ }); _ }) ->
329336
spf "declared component %s" name
330-
| Class { class_ = { Ast.Class.id; _ }; class_loc = _; this_super_write_locs = _ } ->
337+
| Class { class_ = { Ast.Class.id; _ }; kind; class_loc = _; this_super_write_locs = _ } ->
338+
let kind =
339+
match kind with
340+
| ClassKind.Class -> "class"
341+
| ClassKind.Record -> "record"
342+
in
331343
spf
332-
"class %s"
344+
"%s %s"
345+
kind
333346
(Base.Option.value_map
334347
~f:(fun (_, { Ast.Identifier.name; _ }) -> name)
335348
~default:"<anonymous>"

src/common/ty/ty.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ and gen_kind =
136136
| TypeAliasKind
137137
| EnumKind
138138
| ComponentKind
139+
| RecordKind
139140

140141
and fun_effect =
141142
| Hook
@@ -616,6 +617,7 @@ class ['A] comparator_ty =
616617
| TypeAliasKind -> 2
617618
| EnumKind -> 3
618619
| ComponentKind -> 4
620+
| RecordKind -> 5
619621

620622
method tag_of_obj_kind _ =
621623
function
@@ -832,6 +834,7 @@ let debug_string_of_generic_kind = function
832834
| TypeAliasKind -> "type alias"
833835
| EnumKind -> "enum"
834836
| ComponentKind -> "component"
837+
| RecordKind -> "record"
835838

836839
let string_of_utility_ctor = function
837840
| Keys _ -> "$Keys"

src/typing/class_sig.ml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ module Make
6161
| Interface _ -> true
6262
| Class _ -> false
6363

64-
let inst_kind x =
64+
let get_inst_kind x =
6565
match x.super with
6666
| Interface { inline; _ } -> Type.InterfaceKind { inline }
6767
| Class _ -> Type.ClassKind
@@ -481,7 +481,7 @@ module Make
481481
| _ -> failwith "statics must be an ObjT"
482482
)
483483

484-
let insttype cx ~initialized_static_fields s =
484+
let insttype cx ~initialized_static_fields ?inst_kind s =
485485
let constructor =
486486
(* Constructors do not bind `this` *)
487487
let ts =
@@ -526,7 +526,7 @@ module Make
526526
inst_call_t = Base.Option.map call ~f:(Context.make_call_prop cx);
527527
initialized_fields;
528528
initialized_static_fields;
529-
inst_kind = inst_kind s;
529+
inst_kind = Base.Option.value inst_kind ~default:(get_inst_kind s);
530530
inst_dict = s.instance.dict;
531531
class_private_fields = fields_to_prop_map cx s.instance.private_fields;
532532
class_private_static_fields = fields_to_prop_map cx s.static.private_fields;
@@ -642,7 +642,7 @@ module Make
642642
in
643643
(super, static_proto)
644644

645-
let this_instance_type cx x =
645+
let this_instance_type cx ?inst_kind x =
646646
let { static = { reason = sreason; _ }; instance = { reason; _ }; _ } = x in
647647
let (super, static_proto) = supertype cx x in
648648
let implements =
@@ -662,7 +662,7 @@ module Make
662662
implements
663663
in
664664
let (initialized_static_fields, static_objtype) = statictype cx static_proto x in
665-
let inst = insttype cx ~initialized_static_fields x in
665+
let inst = insttype cx ~initialized_static_fields ?inst_kind x in
666666
let static = Type.DefT (sreason, Type.ObjT static_objtype) in
667667
(reason, { Type.static; super; implements; inst })
668668

@@ -795,8 +795,8 @@ module Make
795795

796796
(* TODO: Ideally we should check polarity for all class types, but this flag is
797797
flipped off for interface/declare class currently. *)
798-
let classtype cx ?(check_polarity = true) x =
799-
let (this_reason, this_instance_t) = this_instance_type cx x in
798+
let classtype cx ?(check_polarity = true) ~inst_kind x =
799+
let (this_reason, this_instance_t) = this_instance_type cx ~inst_kind x in
800800
let this = Type.(DefT (this_reason, InstanceT this_instance_t)) in
801801
let this_tparam = this_tparam x in
802802
let tparams_with_this =

src/typing/class_sig_intf.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,6 @@ module type S = sig
182182
(* Create a (polymorphic) class type. In the return tuple, the first type is the internal view of the
183183
class and the second type is the external view--which differ because the internal view can be
184184
comparible with `this`, while the external view shouldn't be. *)
185-
val classtype : Context.t -> ?check_polarity:bool -> t -> Type.t * Type.t
185+
val classtype :
186+
Context.t -> ?check_polarity:bool -> inst_kind:Type.instance_kind -> t -> Type.t * Type.t
186187
end

src/typing/env_resolution.ml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -912,10 +912,15 @@ let resolve_inferred_function
912912
else
913913
fun_type
914914

915-
let resolve_class cx id_loc reason class_loc class_ =
915+
let resolve_class cx id_loc reason ~kind class_loc class_ =
916916
let cache = Context.node_cache cx in
917+
let inst_kind =
918+
match kind with
919+
| ClassKind.Class -> ClassKind
920+
| ClassKind.Record -> RecordKind
921+
in
917922
let ((class_t, class_t_internal, _, _) as sig_info) =
918-
Statement.mk_class_sig cx ~name_loc:id_loc ~class_loc reason class_
923+
Statement.mk_class_sig cx ~name_loc:id_loc ~class_loc ~inst_kind reason class_
919924
in
920925
Node_cache.set_class_sig cache class_loc sig_info;
921926
Type_env.bind_class_self_type cx class_t_internal class_loc;
@@ -1191,8 +1196,8 @@ let resolve cx (def_kind, id_loc) (def, def_scope_kind, class_stack, def_reason)
11911196
def_reason
11921197
function_loc
11931198
function_
1194-
| Class { class_; class_loc; this_super_write_locs = _ } ->
1195-
resolve_class cx id_loc def_reason class_loc class_
1199+
| Class { class_; class_loc; kind; this_super_write_locs = _ } ->
1200+
resolve_class cx id_loc def_reason ~kind class_loc class_
11961201
| MemberAssign { member_loc = _; member = _; rhs } -> expression cx rhs
11971202
| OpAssign { exp_loc; lhs; op; rhs; assertion } ->
11981203
resolve_op_assign cx ~exp_loc lhs assertion op rhs

src/typing/exhaustive.ml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ let get_class_info cx (t : Type.t) : (ALoc.id * string option) option =
4444
| Type.DefT
4545
( _,
4646
Type.InstanceT
47-
{ Type.inst = { Type.inst_kind = Type.ClassKind; class_id; class_name; _ }; _ }
47+
{
48+
Type.inst =
49+
{ Type.inst_kind = Type.ClassKind | Type.RecordKind; class_id; class_name; _ };
50+
_;
51+
}
4852
) ->
4953
Some (class_id, class_name)
5054
| _ -> None
@@ -676,8 +680,16 @@ end = struct
676680
in
677681
{ value_union with objects = obj :: objects }
678682
| Type.DefT
679-
(reason, Type.InstanceT { Type.inst = { Type.class_id; class_name; _ }; super; _ }) ->
680-
let rest = Some reason in
683+
( reason,
684+
Type.InstanceT { Type.inst = { Type.class_id; class_name; inst_kind; _ }; super; _ }
685+
) ->
686+
let rest =
687+
match inst_kind with
688+
| Type.RecordKind -> None
689+
| Type.ClassKind
690+
| Type.InterfaceKind _ ->
691+
Some reason
692+
in
681693
let class_info =
682694
if Context.enable_pattern_matching_instance_patterns cx then
683695
let rec get_super_ids acc t =
@@ -687,7 +699,11 @@ end = struct
687699
Type.InstanceT
688700
{
689701
Type.inst =
690-
{ Type.inst_kind = Type.ClassKind; class_id = super_class_id; _ };
702+
{
703+
Type.inst_kind = Type.ClassKind | Type.RecordKind;
704+
class_id = super_class_id;
705+
_;
706+
};
691707
super;
692708
_;
693709
}

src/typing/predicate_kit.ml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,16 @@ and intersect =
564564
(* C<T> has no overlap with C<S> iff T and S have no overlap *)
565565
and instance_tags_differ cx ~depth t1 t2 =
566566
match (C.unwrap t1, C.unwrap t2) with
567-
| ( DefT (_, InstanceT { inst = { inst_kind = ClassKind; type_args = ts1; _ } as inst1; _ }),
568-
DefT (_, InstanceT { inst = { inst_kind = ClassKind; type_args = ts2; _ } as inst2; _ })
567+
| ( DefT
568+
( _,
569+
InstanceT
570+
{ inst = { inst_kind = ClassKind | RecordKind; type_args = ts1; _ } as inst1; _ }
571+
),
572+
DefT
573+
( _,
574+
InstanceT
575+
{ inst = { inst_kind = ClassKind | RecordKind; type_args = ts2; _ } as inst2; _ }
576+
)
569577
)
570578
when Flow_js_utils.is_same_instance_type inst1 inst2 ->
571579
let ts1 = Base.List.map ts1 ~f:(fun (_, _, t, _) -> t) in

0 commit comments

Comments
 (0)