File tree

14 files changed

+94
-34
lines changed

14 files changed

+94
-34
lines changed
Original file line numberDiff line numberDiff line change
@@ -843,14 +843,6 @@ let is_literal_function_reason r =
843843
| RFunction _ -> true
844844
| _ -> false
845845

846-
let is_literal_union_reason r =
847-
match desc_of_reason r with
848-
| RInferredUnionElemArray _
849-
| RConditional
850-
| RLogical _ ->
851-
true
852-
| _ -> false
853-
854846
let is_lib_reason r =
855847
r.loc |> ALoc.source |> Base.Option.value_map ~default:false ~f:File_key.is_lib_file
856848

Original file line numberDiff line numberDiff line change
@@ -281,8 +281,6 @@ val is_literal_array_reason : 'loc virtual_reason -> bool
281281

282282
val is_literal_function_reason : 'loc virtual_reason -> bool
283283

284-
val is_literal_union_reason : 'loc virtual_reason -> bool
285-
286284
val is_lib_reason : reason -> bool
287285

288286
val is_lib_reason_def : reason -> bool
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,20 @@ let rec dump_t_ (depth, tvars) cx t =
302302
| IntersectionT (_, rep) ->
303303
p ~extra:(spf "[%s]" (String.concat "; " (Base.List.map ~f:kid (InterRep.members rep)))) t
304304
| UnionT (_, rep) ->
305+
let kind_str =
306+
match UnionRep.union_kind rep with
307+
| UnionRep.ProvidersKind -> "ProvidersKind"
308+
| UnionRep.ConditionalKind -> "ConditionalKind"
309+
| UnionRep.ImplicitInstiationKind -> "ImplicitInstiationKind"
310+
| UnionRep.ResolvedKind -> "ResolvedKind"
311+
| UnionRep.LogicalKind -> "LogicalKind"
312+
| UnionRep.UnknownKind -> "UnknownKind"
313+
in
305314
p
306315
~extra:
307316
(spf
308-
"[%s]%s"
317+
"(kind=%s)[%s]%s"
318+
kind_str
309319
(String.concat "; " (Base.List.map ~f:kid (UnionRep.members rep)))
310320
(UnionRep.string_of_specialization rep)
311321
)
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ let resolve_hint cx loc hint : Type_hint.concr_hint =
212212
Type_env.checked_find_loc_env_write cx Env_api.OrdinaryNameLoc loc
213213
)
214214
in
215-
UnionT (mk_reason (RCustom "providers") loc, UnionRep.make ~synthetic:true t1 t2 ts)
215+
UnionT
216+
(mk_reason (RCustom "providers") loc, UnionRep.make ~kind:UnionRep.ProvidersKind t1 t2 ts)
216217
| WriteLocHint (kind, loc) -> Type_env.checked_find_loc_env_write cx kind loc
217218
| StringLiteralType name ->
218219
DefT
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ let rec collect_lowers ~filter_empty cx seen acc = function
5858
(* Everything else becomes part of the merge typed *)
5959
| _ -> collect_lowers ~filter_empty cx seen (t :: acc) ts)
6060

61-
let merge_tvar_opt ?(filter_empty = false) cx r id =
61+
let merge_tvar_opt ?(filter_empty = false) ?(union_kind = UnionRep.ResolvedKind) cx r id =
6262
let lowers =
6363
let seen = ISet.singleton id in
6464
collect_lowers cx seen [] (possible_types cx id) ~filter_empty
6565
in
6666
match lowers with
6767
| [t] -> Some t
68-
| t0 :: t1 :: ts -> Some (UnionT (r, UnionRep.make ~synthetic:true t0 t1 ts))
68+
| t0 :: t1 :: ts -> Some (UnionT (r, UnionRep.make ~kind:union_kind t0 t1 ts))
6969
| [] -> None
7070

7171
let merge_tvar ?(filter_empty = false) ~no_lowers cx r id =
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,8 @@ module Make (Observer : OBSERVER) (Flow : Flow_common.S) : S = struct
602602
match tuple_ts with
603603
| [] -> EmptyT.why reason
604604
| [t] -> t
605-
| t0 :: t1 :: ts -> UnionT (reason, UnionRep.make t0 t1 ts)
605+
| t0 :: t1 :: ts ->
606+
UnionT (reason, UnionRep.make ~kind:UnionRep.ImplicitInstiationKind t0 t1 ts)
606607
in
607608
let len = Base.List.length tuple_ts in
608609
let t =
@@ -775,7 +776,7 @@ module Make (Observer : OBSERVER) (Flow : Flow_common.S) : S = struct
775776
|> union_flatten_list
776777
|> generalize_singletons cx
777778
|> Base.List.filter ~f:(fun t -> not @@ Flow_js_utils.TvarVisitors.has_placeholders cx t)
778-
|> TypeUtil.union_of_ts_opt r)
779+
|> TypeUtil.union_of_ts_opt ~kind:UnionRep.ImplicitInstiationKind r)
779780
| t -> Some t
780781

781782
let on_missing_bounds cx ~use_op tparam ~default_bound ~tparam_binder_reason ~instantiation_reason
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ let is_builtin_promise =
5353
in
5454
(fun cx t -> loop cx ISet.empty t)
5555

56+
let is_literal_union r rep =
57+
let open UnionRep in
58+
match union_kind rep with
59+
| ConditionalKind
60+
| ImplicitInstiationKind
61+
| LogicalKind ->
62+
true
63+
| ProvidersKind
64+
| ResolvedKind
65+
| UnknownKind ->
66+
let open Reason in
67+
(match desc_of_reason r with
68+
| RInferredUnionElemArray _ -> true
69+
| _ -> false)
70+
5671
(**
5772
* [generalize_singletons cx ~force_general t] walks a `t` and replacing instances
5873
* of singleton types that originate from literals with the general version of the
@@ -109,7 +124,7 @@ let generalize_singletons =
109124
| TypeAppT { type_; _ } when is_builtin_promise cx type_ ->
110125
(* async expressions will wrap result in Promise<>, so we need to descend here *)
111126
super#type_ cx force_general t
112-
| UnionT (r, _) when is_literal_union_reason r -> super#type_ cx force_general t
127+
| UnionT (r, rep) when is_literal_union r rep -> super#type_ cx force_general t
113128
| DefT (r, SingletonStrT { from_annot = false; value }) ->
114129
if force_general || Context.natural_inference_local_primitive_literals_full cx then
115130
DefT (replace_desc_reason RString r, StrGeneralT AnyLiteral)
Original file line numberDiff line numberDiff line change
@@ -3296,7 +3296,7 @@ module Make
32963296
| (false, true) -> t1
32973297
(* Both sides threw--see below for where we re-raise *)
32983298
| (true, true) -> EmptyT.at loc
3299-
| (false, false) -> UnionT (reason, UnionRep.make ~synthetic:true t1 t2 [])
3299+
| (false, false) -> UnionT (reason, UnionRep.make ~kind:UnionRep.ConditionalKind t1 t2 [])
33003300
(* NOTE: In general it is dangerous to express the least upper bound of
33013301
some types as a union: it might pin down the least upper bound
33023302
prematurely (before all the types have been inferred), and when the
Original file line numberDiff line numberDiff line change
@@ -2323,13 +2323,21 @@ end
23232323
and UnionRep : sig
23242324
type t
23252325

2326+
type union_kind =
2327+
| ProvidersKind
2328+
| ConditionalKind
2329+
| ImplicitInstiationKind
2330+
| ResolvedKind
2331+
| LogicalKind
2332+
| UnknownKind
2333+
23262334
val same_source : t -> t -> bool
23272335

23282336
val same_structure : t -> t -> bool
23292337

23302338
(** build a rep from list of members *)
23312339
val make :
2332-
?source_aloc:ALoc.id -> ?synthetic:bool -> TypeTerm.t -> TypeTerm.t -> TypeTerm.t list -> t
2340+
?source_aloc:ALoc.id -> ?kind:union_kind -> TypeTerm.t -> TypeTerm.t -> TypeTerm.t list -> t
23332341

23342342
(** members in declaration order *)
23352343
val members : t -> TypeTerm.t list
@@ -2342,6 +2350,8 @@ and UnionRep : sig
23422350

23432351
val is_synthetic : t -> bool
23442352

2353+
val union_kind : t -> union_kind
2354+
23452355
(** map rep r to rep r' along type mapping f. if nothing would be changed,
23462356
returns the physically-identical rep. *)
23472357
val ident_map : ?always_keep_source:bool -> (TypeTerm.t -> TypeTerm.t) -> t -> t
@@ -2475,6 +2485,14 @@ end = struct
24752485
| NoCandidateMembers
24762486
| NoCommonKeys
24772487

2488+
type union_kind =
2489+
| ProvidersKind
2490+
| ConditionalKind
2491+
| ImplicitInstiationKind
2492+
| ResolvedKind
2493+
| LogicalKind
2494+
| UnknownKind
2495+
24782496
type t = {
24792497
t0: TypeTerm.t;
24802498
t1: TypeTerm.t;
@@ -2488,7 +2506,7 @@ end = struct
24882506
(* A union is synthetic roughly when it does not emerge from an annotation,
24892507
* e.g. when it emerges as the collection of lower bounds during implicit
24902508
* instantiation. *)
2491-
synthetic: bool;
2509+
kind: union_kind;
24922510
}
24932511

24942512
let same_source { source_aloc = s1; _ } { source_aloc = s2; _ } =
@@ -2524,7 +2542,12 @@ end = struct
25242542
| DefT (_, NullT) -> Some NullTag
25252543
| _ -> None
25262544

2527-
let is_synthetic { synthetic; _ } = synthetic
2545+
let is_synthetic { kind; _ } =
2546+
match kind with
2547+
| UnknownKind -> false
2548+
| _ -> true
2549+
2550+
let union_kind { kind; _ } = kind
25282551

25292552
(** given a list of members, build a rep.
25302553
specialized reps are used on compatible type lists *)
@@ -2539,14 +2562,14 @@ end = struct
25392562
| _ -> None
25402563
end
25412564
in
2542-
fun ?source_aloc ?(synthetic = false) t0 t1 ts ->
2565+
fun ?source_aloc ?(kind = UnknownKind) t0 t1 ts ->
25432566
let enum =
25442567
Base.Option.(
25452568
mk_enum (UnionEnumSet.empty, tag_of_member t0) (t0 :: t1 :: ts) >>| fun (tset, tag) ->
25462569
EnumUnion (tset, tag)
25472570
)
25482571
in
2549-
{ t0; t1; ts; source_aloc; specialization = ref enum; synthetic }
2572+
{ t0; t1; ts; source_aloc; specialization = ref enum; kind }
25502573

25512574
let members { t0; t1; ts; _ } = t0 :: t1 :: ts
25522575

@@ -2559,7 +2582,8 @@ end = struct
25592582
| t0 :: t1 :: ts -> make t0 t1 ts
25602583
| _ -> failwith "impossible"
25612584

2562-
let ident_map ?(always_keep_source = false) f ({ t0; t1; ts; source_aloc = source; _ } as rep) =
2585+
let ident_map
2586+
?(always_keep_source = false) f ({ t0; t1; ts; source_aloc = source; kind; _ } as rep) =
25632587
let t0_ = f t0 in
25642588
let t1_ = f t1 in
25652589
let ts_ = ListUtils.ident_map f ts in
@@ -2571,7 +2595,7 @@ end = struct
25712595
else
25722596
None
25732597
in
2574-
make ?source_aloc t0_ t1_ ts_
2598+
make ?source_aloc ~kind t0_ t1_ ts_
25752599
else
25762600
rep
25772601

Original file line numberDiff line numberDiff line change
@@ -765,21 +765,21 @@ let map_annotated_or_inferred f = function
765765
(* Creates a union from a list of types. Since unions require a minimum of two
766766
types this function will return an empty type when there are no types in the
767767
list, or the list head when there is one type in the list. *)
768-
let union_of_ts reason ts =
768+
let union_of_ts ?(kind = UnionRep.UnknownKind) reason ts =
769769
match ts with
770770
(* If we have no types then this is an error. *)
771771
| [] -> DefT (reason, EmptyT)
772772
(* If we only have one type then only that should be used. *)
773773
| [t0] -> t0
774774
(* If we have more than one type then we make a union type. *)
775-
| t0 :: t1 :: ts -> UnionT (reason, UnionRep.make t0 t1 ts)
775+
| t0 :: t1 :: ts -> UnionT (reason, UnionRep.make ~kind t0 t1 ts)
776776

777-
let union_of_ts_opt reason ts =
777+
let union_of_ts_opt ?(kind = UnionRep.UnknownKind) reason ts =
778778
match ts with
779779
| [] -> None
780780
| [t0] -> Some t0
781781
(* If we have more than one type then we make a union type. *)
782-
| t0 :: t1 :: ts -> Some (UnionT (reason, UnionRep.make t0 t1 ts))
782+
| t0 :: t1 :: ts -> Some (UnionT (reason, UnionRep.make ~kind t0 t1 ts))
783783

784784
let annotated_or_inferred_of_option ~default = function
785785
| Some t -> Annotated t
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ val type_t_of_annotated_or_inferred : Type.annotated_or_inferred -> Type.t
9191
val map_annotated_or_inferred :
9292
(Type.t -> Type.t) -> Type.annotated_or_inferred -> Type.annotated_or_inferred
9393

94-
val union_of_ts : reason -> Type.t list -> Type.t
94+
val union_of_ts : ?kind:Type.UnionRep.union_kind -> reason -> Type.t list -> Type.t
9595

96-
val union_of_ts_opt : reason -> Type.t list -> Type.t option
96+
val union_of_ts_opt : ?kind:Type.UnionRep.union_kind -> reason -> Type.t list -> Type.t option
9797

9898
val annotated_or_inferred_of_option : default:Type.t -> Type.t option -> Type.annotated_or_inferred
9999

Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ let provider_type_for_def_loc ?(intersect = false) cx env def_loc =
184184
| t1 :: t2 :: ts when intersect ->
185185
IntersectionT (mk_reason (RCustom "providers") def_loc, InterRep.make t1 t2 ts)
186186
| t1 :: t2 :: ts ->
187-
UnionT (mk_reason (RCustom "providers") def_loc, UnionRep.make ~synthetic:true t1 t2 ts)
187+
UnionT
188+
(mk_reason (RCustom "providers") def_loc, UnionRep.make ~kind:UnionRep.ProvidersKind t1 t2 ts)
188189

189190
(*************)
190191
(* Reading *)
@@ -800,7 +801,9 @@ let constraining_type ~default cx name loc =
800801
| Some [t] -> t
801802
| Some (t1 :: t2 :: ts) ->
802803
UnionT
803-
(mk_reason (RIdentifier (OrdinaryName name)) loc, UnionRep.make ~synthetic:true t1 t2 ts))
804+
( mk_reason (RIdentifier (OrdinaryName name)) loc,
805+
UnionRep.make ~kind:UnionRep.ProvidersKind t1 t2 ts
806+
))
804807

805808
(*************)
806809
(* Writing *)
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,9 @@ module Operators = struct
375375
let id = Tvar.mk_no_wrap cx reason in
376376
let tout = (reason, id) in
377377
f tout;
378-
match Flow_js_utils.merge_tvar_opt ~filter_empty:true cx reason id with
378+
match
379+
Flow_js_utils.merge_tvar_opt ~filter_empty:true ~union_kind:UnionRep.LogicalKind cx reason id
380+
with
379381
| Some t -> t
380382
| None -> Tvar_resolver.default_no_lowers reason
381383

Original file line numberDiff line numberDiff line change
@@ -527,3 +527,17 @@ function test_async() {
527527
const x = new AsyncSelector(async () => ({title: ''}));
528528
x as AsyncSelector<Promise<{title: string}>>; // okay '' has generalized to string
529529
}
530+
531+
function test_logical_nullish() {
532+
declare const x: ?('a' | 'b');
533+
declare var arr: Array<void>;
534+
const mappedArr = arr.map(_ => ({
535+
prop: x ?? 'c',
536+
}));
537+
mappedArr[0].prop = 'd'; // okay - 'c' causes generalization to string
538+
}
539+
function test_array_elem_union() {
540+
declare function foo<T>(create: T): T;
541+
const strings = foo(['a', 'b']);
542+
strings.push("c"); // okay
543+
}

0 commit comments

Comments
 (0)