Skip to content

Commit

Permalink
Fix type-level access
Browse files Browse the repository at this point in the history
  • Loading branch information
SamChou19815 authored Dec 11, 2024
1 parent 6db254a commit 974ebea
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 100 deletions.
63 changes: 34 additions & 29 deletions src/typing/annotation_inference.ml
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ module rec ConsGen : S = struct
ConsGen.elab_t
cx
t
(Annot_GetPropT (access_reason, use_op, mk_named_prop ~reason:prop_reason name))
(Annot_GetPropT {reason=access_reason; use_op;from_annot=false;prop_ref= mk_named_prop ~reason:prop_reason name})

let mk_react_dro cx _use_op (props_loc, dro_t) t =
ConsGen.elab_t cx t (Annot_DeepReadOnlyT (reason_of_t t, props_loc, dro_t))
Expand Down Expand Up @@ -516,7 +516,7 @@ module rec ConsGen : S = struct
elab_t
cx
t
(Annot_GetPropT (reason_op, use_op, Named { reason; name; from_indexed_access = true }))
( Annot_GetPropT {reason=reason_op; use_op; from_annot=true;prop_ref = Named { reason; name; from_indexed_access = true }})
in
elab_t cx t op
| (EvalT (t, TypeDestructorT (use_op, reason, ElementType { index_type }), _), _) ->
Expand Down Expand Up @@ -945,7 +945,7 @@ module rec ConsGen : S = struct
| DefT (_, ObjT o) -> o.flags.react_dro
| _ -> None
in
(match GetPropTKit.get_obj_prop cx dummy_trace unknown_use o propref reason_op with
(match GetPropTKit.get_obj_prop cx dummy_trace ~union_void_on_computed_prop_access:false unknown_use o propref reason_op with
| Some (p, _) ->
GetPropTKit.perform_read_prop_action cx dummy_trace use_op propref p reason_op react_dro
| None -> Get_prop_helper.cg_lookup_ cx use_op o.proto_t reason_op propref objt)
Expand Down Expand Up @@ -1009,10 +1009,11 @@ module rec ConsGen : S = struct
~seen
values_type
(Annot_GetPropT
( reason_op,
use_op,
Named { reason = prop_ref_reason; name = prop_name; from_indexed_access = false }
)
{reason=reason_op;
use_op;
from_annot=false;
prop_ref= Named { reason = prop_ref_reason; name = prop_name; from_indexed_access = false }
}
))
| (NamespaceT { namespace_symbol = _; values_type; types_tmap = _ }, _) ->
elab_t cx ~seen values_type op
Expand All @@ -1025,16 +1026,17 @@ module rec ConsGen : S = struct
~seen
t
(Annot_GetPropT
( reason_op,
use_op,
Named { reason = prop_ref_reason; name = prop_name; from_indexed_access = false }
)
{reason= reason_op;
use_op;
from_annot = false;
prop_ref = Named { reason = prop_ref_reason; name = prop_name; from_indexed_access = false }
}
)
(************)
(* GetPropT *)
(************)
| ( DefT (reason_instance, InstanceT { super; inst; _ }),
Annot_GetPropT (reason_op, use_op, (Named _ as propref))
Annot_GetPropT {reason=reason_op; use_op;from_annot=_;prop_ref= (Named _ as propref)}
) ->
GetPropTKit.read_instance_prop
cx
Expand All @@ -1049,17 +1051,17 @@ module rec ConsGen : S = struct
inst
propref
reason_op
| (DefT (reason, InstanceT _), Annot_GetPropT (_, _, Computed _)) ->
| (DefT (reason, InstanceT _), Annot_GetPropT {prop_ref=Computed _; _}) ->
error_unsupported cx reason op
| ( DefT (_, ObjT _),
Annot_GetPropT (reason_op, _, Named { name = OrdinaryName "constructor"; _ })
Annot_GetPropT {reason=reason_op; from_annot=_;use_op = _; prop_ref=Named { name = OrdinaryName "constructor"; _ }}
) ->
Unsoundness.why Constructor reason_op
| (DefT (reason_obj, ObjT o), Annot_GetPropT (reason_op, use_op, propref)) ->
GetPropTKit.read_obj_prop cx dummy_trace ~use_op o propref reason_obj reason_op None
| (AnyT _, Annot_GetPropT (reason_op, _, _)) -> AnyT (reason_op, Untyped)
| (DefT (reason_obj, ObjT o), Annot_GetPropT {reason=reason_op; use_op;from_annot;prop_ref}) ->
GetPropTKit.read_obj_prop cx dummy_trace ~use_op ~from_annot o prop_ref reason_obj reason_op None
| (AnyT _, Annot_GetPropT{reason;_}) -> AnyT (reason, Untyped)
| ( DefT (reason, ClassT instance),
Annot_GetPropT (_, _, Named { name = OrdinaryName "prototype"; _ })
Annot_GetPropT {prop_ref=Named { name = OrdinaryName "prototype"; _ };_}
) ->
reposition cx (loc_of_reason reason) instance
(**************)
Expand All @@ -1075,21 +1077,24 @@ module rec ConsGen : S = struct
StrModuleT.why reason_op
| ((DefT (_, (ObjT _ | ArrT _ | InstanceT _)) | AnyT _), Annot_GetElemT (reason_op, use_op, key))
->
elab_t cx key (Annot_ElemT (reason_op, use_op, t))
| (_, Annot_ElemT (reason_op, use_op, (DefT (_, (ObjT _ | InstanceT _)) as obj))) ->
let propref = Flow_js_utils.propref_for_elem_t t in
elab_t cx obj (Annot_GetPropT (reason_op, use_op, propref))
| (_, Annot_ElemT (reason_op, _use_op, (AnyT _ as _obj))) ->
elab_t cx key (Annot_ElemT {reason=reason_op; use_op;from_annot=false; source=t})
| (_, Annot_ElemT {reason=reason_op; use_op; from_annot;source=(DefT (_, (ObjT _ | InstanceT _)) as obj)}) ->
let prop_ref = Flow_js_utils.propref_for_elem_t t in
elab_t cx obj (Annot_GetPropT {reason=reason_op; from_annot; use_op; prop_ref})
| (_, Annot_ElemT {reason=reason_op; use_op=_use_op;from_annot=_; source=(AnyT _ as _obj)}) ->
let value = AnyT.untyped reason_op in
reposition cx (loc_of_reason reason_op) value
| (AnyT _, Annot_ElemT (reason_op, _, DefT (_, ArrT arrtype))) ->
| (AnyT _, Annot_ElemT {reason=reason_op; source= DefT (_, ArrT arrtype); _ }) ->
let value = elemt_of_arrtype arrtype in
reposition cx (loc_of_reason reason_op) value
| ( DefT (_, (NumGeneralT _ | NumT_UNSOUND _)),
Annot_ElemT (reason_op, use_op, DefT (reason_tup, ArrT arrtype))
Annot_ElemT {reason=reason_op; use_op; from_annot; source= DefT (reason_tup, ArrT arrtype)}
) ->
let (value, _, _, _) =
Flow_js_utils.array_elem_check ~write_action:false cx t use_op reason_op reason_tup arrtype
Flow_js_utils.array_elem_check
~write_action:false
~union_void_on_computed_prop_access:(Context.no_unchecked_indexed_access cx && not from_annot)
cx t use_op reason_op reason_tup arrtype
in
reposition cx (loc_of_reason reason_op) value
| (DefT (_, ObjT o), Annot_ObjKeyMirror reason_op) ->
Expand Down Expand Up @@ -1145,7 +1150,7 @@ module rec ConsGen : S = struct
(* Enums *)
(*********)
| ( DefT (enum_reason, EnumObjectT { enum_value_t; enum_info = ConcreteEnum enum_info }),
Annot_GetPropT (access_reason, use_op, Named { reason = prop_reason; name; _ })
Annot_GetPropT {reason=access_reason; use_op; from_annot=_;prop_ref= Named { reason = prop_reason; name; _ }}
) ->
let access = (use_op, access_reason, None, (prop_reason, name)) in
GetPropTKit.on_EnumObjectT
Expand Down Expand Up @@ -1205,7 +1210,7 @@ module rec ConsGen : S = struct
let arr = get_builtin_typeapp cx reason "Array" [elem_t] in
elab_t cx arr op
| ( DefT (reason, ArrT (TupleAT { arity; inexact; _ })),
Annot_GetPropT (reason_op, _, Named { name = OrdinaryName "length"; _ })
Annot_GetPropT {reason=reason_op; prop_ref= Named { name = OrdinaryName "length"; _ }; _}
) ->
GetPropTKit.on_array_length cx dummy_trace reason ~inexact arity reason_op
| ( DefT (reason, ArrT ((TupleAT _ | ROArrayAT _) as arrtype)),
Expand Down Expand Up @@ -1276,7 +1281,7 @@ module rec ConsGen : S = struct
and get_statics cx reason t = elab_t cx t (Annot_GetStaticsT reason)

and get_prop cx use_op reason ?(op_reason = reason) name t =
elab_t cx t (Annot_GetPropT (op_reason, use_op, mk_named_prop ~reason name))
elab_t cx t (Annot_GetPropT {reason=op_reason; use_op; from_annot=false;prop_ref= mk_named_prop ~reason name})

and get_elem cx use_op reason ~key t = elab_t cx t (Annot_GetElemT (reason, use_op, key))

Expand Down
19 changes: 10 additions & 9 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3602,7 +3602,7 @@ struct
ignore_dicts;
}
) ->
(match GetPropTKit.get_obj_prop cx trace unknown_use o propref reason_op with
(match GetPropTKit.get_obj_prop cx trace unknown_use ~union_void_on_computed_prop_access:false o propref reason_op with
| Some (p, target_kind) ->
(match lookup_kind with
| NonstrictReturning (_, Some (id, _)) -> Context.test_prop_hit cx id
Expand Down Expand Up @@ -3684,7 +3684,7 @@ struct
) ->
rec_flow_t cx trace ~use_op:unknown_use (Unsoundness.why Constructor reason, OpenT tout)
| ( DefT (reason_obj, ObjT o),
GetPropT { use_op; reason = reason_op; id; from_annot = _; propref; tout; hint = _ }
GetPropT { use_op; reason = reason_op; id; from_annot; propref; tout; hint = _ }
) ->
let lookup_info =
Base.Option.map id ~f:(fun id ->
Expand All @@ -3696,7 +3696,7 @@ struct
(id, lookup_default_tout)
)
in
GetPropTKit.read_obj_prop cx trace ~use_op o propref reason_obj reason_op lookup_info tout
GetPropTKit.read_obj_prop cx trace ~use_op ~from_annot o propref reason_obj reason_op lookup_info tout
| ( AnyT (_, src),
GetPropT { use_op = _; reason; id; from_annot = _; propref = _; tout; hint = _ }
) ->
Expand All @@ -3721,6 +3721,7 @@ struct
cx
trace
~use_op
~from_annot:false
o
propref
reason_obj
Expand Down Expand Up @@ -3829,14 +3830,14 @@ struct
| ( DefT (_, (NumGeneralT _ | NumT_UNSOUND _)),
ElemT (use_op, reason, (DefT (reason_tup, ArrT arrtype) as arr), action)
) ->
let (write_action, read_action) =
let (write_action, read_action, union_void_on_computed_prop_access) =
match action with
| ReadElem _ -> (false, true)
| CallElem _ -> (false, false)
| WriteElem _ -> (true, false)
| ReadElem {from_annot; _} -> (false, true, Context.no_unchecked_indexed_access cx && not from_annot)
| CallElem _ -> (false, false, Context.no_unchecked_indexed_access cx)
| WriteElem _ -> (true, false, false)
in
let (value, is_tuple, use_op, react_dro) =
array_elem_check ~write_action cx l use_op reason reason_tup arrtype
array_elem_check ~write_action ~union_void_on_computed_prop_access cx l use_op reason reason_tup arrtype
in
let value =
match react_dro with
Expand Down Expand Up @@ -7239,7 +7240,7 @@ struct
and write_obj_prop cx trace ~use_op ~mode o propref reason_obj reason_op tin prop_tout =
let obj_t = DefT (reason_obj, ObjT o) in
let action = WriteProp { use_op; obj_t; prop_tout; tin; write_ctx = Normal; mode } in
match GetPropTKit.get_obj_prop cx trace use_op o propref reason_op with
match GetPropTKit.get_obj_prop cx trace ~union_void_on_computed_prop_access:false use_op o propref reason_op with
| Some (p, target_kind) ->
perform_lookup_action cx trace propref p target_kind reason_obj reason_op action
| None ->
Expand Down
44 changes: 20 additions & 24 deletions src/typing/flow_js_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2304,12 +2304,19 @@ module GetPropT_kit (F : Get_prop_helper_sig) = struct
let t = tuple_length reason ~inexact arity in
F.return cx trace ~use_op:unknown_use (F.reposition cx ~trace loc t)

let get_obj_prop cx trace use_op o propref reason_op =
let get_obj_prop cx trace use_op ~union_void_on_computed_prop_access o propref reason_op =
let named_prop =
match propref with
| Named { name; _ } -> Context.get_prop cx o.props_tmap name
| Computed _ -> None
in
let union_void_if_instructed t =
if union_void_on_computed_prop_access then
let r = reason_of_t t in
UnionT (r, UnionRep.make t (VoidT.why r) [])
else
t
in
let dict_t = Obj_type.get_dict_opt o.flags.obj_kind in
match (propref, named_prop, dict_t) with
| (_, Some prop, _) ->
Expand All @@ -2319,29 +2326,18 @@ module GetPropT_kit (F : Get_prop_helper_sig) = struct
when not (is_dictionary_exempt name) ->
(* Dictionaries match all property reads *)
F.dict_read_check cx trace ~use_op (type_of_key_name cx name reason_op, key);
let type_ =
if Context.no_unchecked_indexed_access cx then
let r = reason_of_t value in
UnionT (r, UnionRep.make value (VoidT.why r) [])
else
value
in
let type_ = union_void_if_instructed value in
Some (OrdinaryField { type_; polarity = dict_polarity }, IndexerProperty)
| (Computed k, None, Some { key; value; dict_polarity; _ }) ->
F.dict_read_check cx trace ~use_op (k, key);
let type_ =
if Context.no_unchecked_indexed_access cx then
let r = reason_of_t value in
UnionT (r, UnionRep.make value (VoidT.why r) [])
else
value
in
let type_ = union_void_if_instructed value in
Some (OrdinaryField { type_; polarity = dict_polarity }, IndexerProperty)
| _ -> None

let read_obj_prop cx trace ~use_op o propref reason_obj reason_op lookup_info =
let read_obj_prop cx trace ~use_op ~from_annot o propref reason_obj reason_op lookup_info =
let l = DefT (reason_obj, ObjT o) in
match get_obj_prop cx trace use_op o propref reason_op with
let union_void_on_computed_prop_access = Context.no_unchecked_indexed_access cx && not from_annot in
match get_obj_prop cx trace use_op ~union_void_on_computed_prop_access o propref reason_op with
| Some (p, _target_kind) ->
Base.Option.iter ~f:(fun (id, _) -> Context.test_prop_hit cx id) lookup_info;
perform_read_prop_action cx trace use_op propref p reason_op o.flags.react_dro
Expand Down Expand Up @@ -2398,14 +2394,14 @@ end
(* ElemT utils *)
(***************)

let array_elem_check ~write_action cx l use_op reason reason_tup arrtype =
let union_void_under_no_unchecked_indexed_access cx elem_t =
if Context.no_unchecked_indexed_access cx then
let array_elem_check ~write_action ~union_void_on_computed_prop_access cx l use_op reason reason_tup arrtype =
let union_void_if_instructed elem_t =
if union_void_on_computed_prop_access then
let r = reason_of_t elem_t in
UnionT (r, UnionRep.make elem_t (VoidT.why r) [])
else
elem_t
in
in
let (elem_t, elements, is_index_restricted, is_tuple, tuple_is_inexact, react_dro) =
match arrtype with
| ArrayAT { elem_t; tuple_view; react_dro } ->
Expand All @@ -2414,13 +2410,13 @@ let array_elem_check ~write_action cx l use_op reason reason_tup arrtype =
~f:(fun (TupleView { elements; arity = _; inexact = _ }) -> elements)
tuple_view
in
let elem_t = union_void_under_no_unchecked_indexed_access cx elem_t in
let elem_t = union_void_if_instructed elem_t in
(elem_t, elements, false, false, false, react_dro)
| TupleAT { elem_t; elements; arity = _; inexact; react_dro } ->
let elem_t = union_void_under_no_unchecked_indexed_access cx elem_t in
let elem_t = union_void_if_instructed elem_t in
(elem_t, Some elements, true, true, inexact, react_dro)
| ROArrayAT (elem_t, react_dro) ->
let elem_t = union_void_under_no_unchecked_indexed_access cx elem_t in
let elem_t = union_void_if_instructed elem_t in
(elem_t, None, true, false, false, react_dro)
in
let (can_write_tuple, value, use_op) =
Expand Down
14 changes: 7 additions & 7 deletions src/typing/type.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3385,9 +3385,9 @@ module AConstraint = struct
prop_ref: Reason.t * name;
}
| Annot_GetEnumT of Reason.t
| Annot_GetPropT of Reason.t * TypeTerm.use_op * TypeTerm.propref
| Annot_GetPropT of {reason: Reason.t; use_op: TypeTerm.use_op; from_annot: bool; prop_ref:TypeTerm.propref}
| Annot_GetElemT of Reason.t * TypeTerm.use_op * TypeTerm.t (* key *)
| Annot_ElemT of Reason.t * TypeTerm.use_op * TypeTerm.t (* read action only *)
| Annot_ElemT of {reason: Reason.t; use_op: TypeTerm.use_op; from_annot: bool; source: TypeTerm.t } (* read action only *)
| Annot_GetStaticsT of Reason.t
| Annot_LookupT of Reason.t * TypeTerm.use_op * TypeTerm.propref * TypeTerm.t
| Annot_ObjKitT of Reason.t * TypeTerm.use_op * Object.resolve_tool * Object.tool
Expand Down Expand Up @@ -3519,10 +3519,10 @@ module AConstraint = struct
| Annot_CopyNamedExportsT (r, _)
| Annot_CopyTypeExportsT (r, _)
| Annot_GetTypeFromNamespaceT { reason = r; _ }
| Annot_GetPropT (r, _, _)
| Annot_GetPropT {reason=r;_}
| Annot_GetElemT (r, _, _)
| Annot_GetEnumT r
| Annot_ElemT (r, _, _)
| Annot_ElemT {reason=r; _}
| Annot_GetStaticsT r
| Annot_LookupT (r, _, _, _)
| Annot_ObjKitT (r, _, _, _)
Expand All @@ -3542,9 +3542,9 @@ module AConstraint = struct
let use_op_of_operation = function
| Annot_SpecializeT (use_op, _, _, _)
| Annot_GetTypeFromNamespaceT { use_op; _ }
| Annot_GetPropT (_, use_op, _)
| Annot_GetPropT {use_op;_}
| Annot_GetElemT (_, use_op, _)
| Annot_ElemT (_, use_op, _)
| Annot_ElemT { use_op; _ }
| Annot_LookupT (_, use_op, _, _)
| Annot_ObjKitT (_, use_op, _, _) ->
Some use_op
Expand Down Expand Up @@ -3602,7 +3602,7 @@ module AConstraint = struct
| Annot_MixinT r -> replace_desc_reason (RCustom "mixins") r
| Annot_UnaryArithT (r, _) -> replace_desc_reason (RCustom "unary minus") r
| Annot_NotT r -> replace_desc_reason (RCustom "unary not") r
| Annot_GetPropT (r, _, propref) -> replace_desc_reason (RProperty (name_of_propref propref)) r
| Annot_GetPropT {reason;prop_ref;_} -> replace_desc_reason (RProperty (name_of_propref prop_ref)) reason
| Annot_ObjRestT (r, _) -> replace_desc_reason (RCustom "rest") r
| r -> reason_of_op r

Expand Down
4 changes: 2 additions & 2 deletions tests/no_unchecked_indexed_access/arr_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ tuple[2] as 2;
tuple[3] as number; // error: out of bound
tuple[key] as number; // error: void ~> number

declare const typeTest: (typeof roArray)[number];
typeTest as string; // todo: the flag should not affect type-level acccess
declare export const typeTest: (typeof roArray)[number];
typeTest as string; // ok: the flag should not affect type-level acccess
4 changes: 4 additions & 0 deletions tests/no_unchecked_indexed_access/exported_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require('./arr_test').typeTest as string; // ok
require('./obj_test').typeTest as string; // ok
require('./arr_test').typeTest as empty; // error: sanity check
require('./obj_test').typeTest as empty; // error: sanity check
Loading

0 comments on commit 974ebea

Please sign in to comment.