Skip to content

Commit

Permalink
[flow] Directly create exact objects without going through ExactT
Browse files Browse the repository at this point in the history
Summary:
I want to rewrite `$Exact<...>` using the more modern `EvalT` infra. However, it turns out that there are a few places that actually rely on having the `ExactT` wrapper. This diff does the preparation to remove these assumptions and test it by directly creating exact objects in object annotations, instead of going through `ExactT`.

Currently, the `inexact ~> exact` check is only fired during a flow to `ExactT`. This is quite concerning, since if we forget to wrap any exact objects of `ExactT`, we would miss the check (as shown by the changelog example). In this diff, I make the same check in `ObjT = ObjT` unification and `ObjT ~> ObjT` flow.

Changelog: [error] Flow might catch more inexact incompatible with exact errors. [example](https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+h46fNRLuKxJIGWh8MeT0ZfhYlCStpHzNsFBAMIQkIEQwJODAQfiEyfBE4eWw2fDgofDBMsAALfAA3KjgsXGxxZC4eAw0G-GhcWn9aY3wWZldu-g1mbGqJUoBaCRHEzrcDEgBrbAk62kXhXFxJ923d-cPRHEpTgyEoMDaqZdW7vKgoOfaSKgOKpqmDA+d4gB5fMA-P6LCCMLLQbiLOoYCqgh6-GDYRYIXYLSgkRZkCR4jpddwPfJLZjpOBkO4AX34kA0SQ0Tyo2AABLZ7JyKgposhOQAKYBWcX0gCUnIAvAA+TkNCBwNgAbgAOlBNWzcBzudBeQLlABGIXC42chQLKBsEici0Afk5AAUqFluAAeYD0hVC43S+WK5Vq7VzXUsfV2CSWxTKABMZu9AYVSpVGq+sYkFowdpWxggMD5Geiqs5AHoy5yoBBLdQIJROTgYPXZLXKPWqxAAO6ao0SOOcnOcvPYAtFvuliuRrLCLlpNv1zW5EANEwkODQJIZewmED0oA)

Reviewed By: panagosg7

Differential Revision: D56071682

fbshipit-source-id: 5860d09cd2eb9e6c6fb1b8ec9658d023dffe8244
  • Loading branch information
SamChou19815 authored and facebook-github-bot committed Apr 16, 2024
1 parent 51c5573 commit b313063
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 14 deletions.
12 changes: 12 additions & 0 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9415,6 +9415,18 @@ struct
| ( DefT (lreason, ObjT { props_tmap = lflds; flags = lflags; _ }),
DefT (ureason, ObjT { props_tmap = uflds; flags = uflags; _ })
) ->
if
(not (Obj_type.is_exact lflags.obj_kind))
&& (not (is_literal_object_reason ureason))
&& Obj_type.is_exact uflags.obj_kind
then
exact_obj_error cx trace lflags.obj_kind ~use_op ~exact_reason:ureason t1;
if
(not (Obj_type.is_exact uflags.obj_kind))
&& (not (is_literal_object_reason lreason))
&& Obj_type.is_exact lflags.obj_kind
then
exact_obj_error cx trace uflags.obj_kind ~use_op ~exact_reason:lreason t2;
(* ensure the keys and values are compatible with each other. *)
let ldict = Obj_type.get_dict_opt lflags.obj_kind in
let udict = Obj_type.get_dict_opt uflags.obj_kind in
Expand Down
15 changes: 14 additions & 1 deletion src/typing/subtyping_kit.ml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ module Make (Flow : INPUT) : OUTPUT = struct
| _ -> ());

if rflags.obj_kind = Exact && not (is_literal_object_reason ureason) then (
if not (Obj_type.is_exact lflags.obj_kind) then
exact_obj_error
cx
trace
lflags.obj_kind
~use_op
~exact_reason:ureason
(DefT (lreason, ObjT l_obj));
Context.iter_real_props cx lflds (fun name _ ->
if not (Context.has_prop cx uflds name) then
let use_op =
Expand Down Expand Up @@ -1036,11 +1044,16 @@ module Make (Flow : INPUT) : OUTPUT = struct
* any particular member of the intersection doing so completely.
* Here we trap object UBs with more than one property, and
* decompose them into singletons.
*
* This trap is skipped for exact objects, since intersections of inexact objects
* can never satisfy exact objects, but it might cause spurious errors.
*
* Note: should be able to do this with LookupT rather than
* slices, but that approach behaves in nonobvious ways. TODO why?
*)
| (IntersectionT _, DefT (r, ObjT { flags; props_tmap; proto_t; call_t; reachable_targs = _ }))
when NameUtils.Map.cardinal (Context.find_props cx props_tmap) > 1 ->
when NameUtils.Map.cardinal (Context.find_props cx props_tmap) > 1 && flags.obj_kind <> Exact
->
Context.iter_real_props cx props_tmap (fun x p ->
let pmap = NameUtils.Map.singleton x p in
let id = Context.generate_property_map cx pmap in
Expand Down
12 changes: 7 additions & 5 deletions src/typing/type_annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,6 @@ module Make (ConsGen : Type_annotation_sig.ConsGen) (Statement : Statement_sig.S
(t_opt, tast_opt)

and convert_temporary_object = function
| ExactT (_, DefT (r, ObjT o))
| DefT (r, ObjT o) ->
let r = replace_desc_reason RObjectLit r in
let obj_kind =
Expand Down Expand Up @@ -1986,15 +1985,18 @@ module Make (ConsGen : Type_annotation_sig.ConsGen) (Statement : Statement_sig.S
Inexact
in
let flags = { obj_kind; frozen = false; react_dro = None } in
DefT (mk_annot_reason RObjectType loc, ObjT (mk_objecttype ~flags ~call pmap proto))
(mk_annot_reason RObjectType loc, mk_objecttype ~flags ~call pmap proto)
in
let mk_object_annot cx loc ~exact call dict pmap proto =
let exact = exact && dict = None in
let t = mk_object cx loc ~src_loc:true ~exact call dict pmap proto in
let (reason_obj, obj_t) = mk_object cx loc ~src_loc:true ~exact call dict pmap proto in
if exact then
ExactT (mk_annot_reason (RExactType RObjectType) loc, t)
TypeUtil.make_exact_object
~reason_op:(mk_annot_reason (RExactType RObjectType) loc)
~reason_obj
obj_t
else
t
DefT (reason_obj, ObjT obj_t)
in
let open Ast.Type in
let named_property env loc acc prop =
Expand Down
14 changes: 9 additions & 5 deletions src/typing/type_sig_merge.ml
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,6 @@ and merge_annot env file = function
let t = merge env file t in
let open Type in
(match t with
| ExactT (_, DefT (r, ObjT o))
| DefT (r, ObjT o) ->
let r = Reason.(replace_desc_reason RObjectLit r) in
let obj_kind =
Expand Down Expand Up @@ -1008,12 +1007,17 @@ and merge_annot env file = function
let props = SMap.map (merge_obj_annot_prop env file) props |> NameUtils.namemap_of_smap in
let mk_object call proto =
let id = Type.Properties.id_of_aloc_id ~type_sig:true (Context.make_aloc_id file.cx loc) in
let t = Obj_type.mk_with_proto file.cx reason proto ?call ~props ~obj_kind ~id in
let obj_type =
let flags = { Type.obj_kind; frozen = false; react_dro = None } in
let call = Base.Option.map call ~f:(Context.make_call_prop file.cx) in
Context.add_property_map file.cx id props;
Type.mk_objecttype ~flags ~call id proto
in
if obj_kind = Type.Exact then
let exact_reason = Reason.(mk_annot_reason (RExactType RObjectType) loc) in
Type.ExactT (exact_reason, t)
let reason_op = Reason.(mk_annot_reason (RExactType RObjectType) loc) in
TypeUtil.make_exact_object ~reason_op ~reason_obj:reason obj_type
else
t
Type.DefT (reason, Type.ObjT obj_type)
in
begin
match proto with
Expand Down
5 changes: 2 additions & 3 deletions tests/generic_escape/generic_escape.exp
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,8 @@ References:

Error ---------------------------------------------------------------------------------------------------- misc.js:120:9

Cannot assign `x` to `aaa` because object type [1] is incompatible with number [2]. All writes to `aaa` must be
compatible with the type of its initializer [3]. Add an annotation to `aaa` [3] if a different type is desired.
[incompatible-type]
Cannot assign `x` to `aaa` because `T` [1] is incompatible with number [2]. All writes to `aaa` must be compatible with
the type of its initializer [3]. Add an annotation to `aaa` [3] if a different type is desired. [incompatible-type]

misc.js:120:9
120| aaa = x;
Expand Down

0 comments on commit b313063

Please sign in to comment.