2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
13 module Env
= Typing_env
14 module Reason
= Typing_reason
15 module TySet
= Typing_set
16 module Utils
= Typing_utils
17 module MakeType
= Typing_make_type
19 module Cls
= Decl_provider.Class
21 exception Dont_simplify
24 let log_union r ty1 ty2
(env
, result
) =
26 log_with_level env
"union" ~level
:1 (fun () ->
32 ( "Typing_union.union",
34 Log_type
("ty1", ty1
);
35 Log_type
("ty2", ty2
);
36 Log_type
("result", result
);
41 let log_simplify_union r ty1 ty2
(env
, result
) =
43 log_with_level env
"union" ~level
:2 (fun () ->
49 ( "Typing_union.simplify_union",
51 Log_type
("ty1", ty1
);
52 Log_type
("ty2", ty2
);
54 | None
-> Log_head
("result: None", [])
55 | Some ty
-> Log_type
("result: Some", ty
));
60 let log_union_list r tyl
(env
, result
) =
62 log_with_level env
"union" ~level
:1 (fun () ->
68 ( "Typing_union.union_list",
69 List.map tyl ~f
:(fun ty
-> Log_type
("ty", ty
))
70 @ [Log_type
("result", result
)] );
74 let log_simplify_unions r ty
(env
, result
) =
76 log_with_level env
"union" ~level
:1 (fun () ->
82 ( "Typing_union.simplify_unions",
83 [Log_type
("ty", ty
); Log_type
("result", result
)] );
88 (* Two types are "equivalent" if when expanded they are equal.
89 * If they are equivalent, this function returns a type which is equivalent to
90 * both, otherwise returns None.
92 * We need this instead of simply ty_equal, otherwise a lot of things aren't
93 * unioned properly - e.g. we get things like `array<^(int)> | array<^(int)>` -
94 * which potentially creates huge unresolved types, damaging perf.
96 * Furthermore, when unioning classes with type parameters, we allow
97 * A<Unresolved[]> | A<B> = A<B>
98 * This is because collections are often initialized like
99 * $myvec = get_int_vec() ?? vec[]
100 * If we don't do this simplification, this potentially produces exponentially
101 * growing Unresolved, like in test map_corner_case.php.
103 let ty_equiv env ty1 ty2 ~are_ty_param
=
104 let (env
, ety1
) = Env.expand_type env ty1
in
105 let (env
, ety2
) = Env.expand_type env ty2
in
107 match ((get_node ety1
, ty1
), (get_node ety2
, ty2
)) with
108 | ((Tunion
[], _
), (_
, ty))
109 | ((_
, ty), (Tunion
[], _
))
112 | _
when ty_equal ety1 ety2
->
114 match get_node ty1
with
122 (* Destructure a union into a list of its sub-types, decending into sub-unions,
123 and also pulling out null and dynamic.
125 let dest_union_list env tyl
=
126 let orr r_opt r
= Some
(Option.value r_opt ~default
:r
) in
127 let rec dest_union env
ty tyl tyl_res r_null r_union r_dyn
=
128 let (env
, ty) = Env.expand_type env
ty in
130 | (r
, Tunion tyl'
) ->
131 dest_union_list env
(tyl'
@ tyl
) tyl_res r_null
(orr r_union r
) r_dyn
133 dest_union env
ty tyl tyl_res
(orr r_null r
) r_union r_dyn
134 | (r
, Tprim
Aast.Tnull
) ->
135 dest_union_list env tyl tyl_res
(orr r_null r
) r_union r_dyn
137 dest_union_list env tyl tyl_res r_null r_union
(orr r_dyn r
)
138 | _
-> dest_union_list env tyl
(ty :: tyl_res
) r_null r_union r_dyn
139 and dest_union_list env tyl tyl_res r_null r_union r_dyn
=
141 | [] -> (env
, (tyl_res
, r_null
, r_union
, r_dyn
))
142 | ty :: tyl
-> dest_union env
ty tyl tyl_res r_null r_union r_dyn
144 dest_union_list env tyl
[] None None None
146 (** Constructor for unions and options, taking a list of types and whether the
147 result should be nullable. Simplify things like singleton unions or nullable nothing.
148 This allows the result of any unioning function from this module to be flattened and normalized,
149 with options being "pushed out" of the union (e.g. we return ?(A|B) instead of (A | ?B)
150 or (A | null | B), and similarly with like types). *)
151 let make_union env r tyl reason_nullable_opt reason_dyn_opt
=
155 | tyl
-> MakeType.union r tyl
157 (* Given that unions with null are encoded specially in the type system - as options -
158 we still need to simplify unions of null and nonnull here.
159 For example, we may have to construct ?(nonnull | any) which should result in mixed.
160 For something more complex like ?(A | (B & nonnull)), one can
161 distribute the option to give (?A | (?B & ?nonnull)) which then simplifies to ?(A | B).
162 This function performs those simplifications. *)
163 let simplify_nonnull env
ty =
164 let is_nonnull ty = ty_equal
ty (MakeType.nonnull r
) in
165 let rec simplify env
ty =
166 let (env
, ty) = Env.expand_type env
ty in
169 (* ok to remove the Toption since we'll re-add it at the end. *)
172 let (env
, tyl
) = List.map_env env tyl ~f
:simplify in
173 if List.exists tyl ~f
:is_nonnull then
174 (env
, MakeType.nonnull r
)
176 (env
, MakeType.union r tyl
)
177 | (r
, Tintersection tyl
) ->
178 let (env
, tyl
) = List.map_env env tyl ~f
:simplify in
179 let tyl = List.filter
tyl ~f
:(fun ty -> not
(is_nonnull ty)) in
180 (env
, MakeType.intersection r
tyl)
186 if Option.is_some reason_nullable_opt
then
187 simplify_nonnull env
ty
191 let like_ty dyn_r
ty = MakeType.union dyn_r
[MakeType.dynamic dyn_r
; ty] in
192 (* Some code is sensitive to dynamic appearing first in the union. *)
194 match (reason_nullable_opt
, reason_dyn_opt
, deref
ty) with
195 | (None
, Some dyn_r
, (_
, Tunion
[])) -> MakeType.dynamic dyn_r
196 | (Some null_r
, None
, (_
, Tunion
[])) -> MakeType.null null_r
197 | (Some null_r
, Some dyn_r
, (_
, Tunion
[])) ->
198 like_ty dyn_r
(MakeType.null null_r
)
199 | (None
, None
, _
) -> ty
200 | (None
, Some dyn_r
, (r
, Tunion
tyl)) ->
201 MakeType.union r
(MakeType.dynamic dyn_r
:: tyl)
202 | (None
, Some dyn_r
, _
) -> like_ty dyn_r
ty
203 | (Some null_r
, None
, _
) -> MakeType.nullable_locl null_r
ty
204 | (Some null_r
, Some dyn_r
, _
) ->
205 like_ty dyn_r
(MakeType.nullable_locl null_r
ty)
207 let (env
, ty) = Typing_utils.wrap_union_inter_ty_in_var env r
ty in
210 let exact_least_upper_bound e1 e2
=
212 | (Exact
, Exact
) -> Exact
215 let rec union env ?
(approx_cancel_neg
= false) ty1 ty2
=
216 let r1 = get_reason ty1
in
217 let r2 = get_reason ty2
in
218 Log.log_union r2 ty1 ty2
220 if ty_equal ty1 ty2
then
222 else if Typing_utils.is_sub_type_for_union ~coerce
:None env ty1 ty2
then
224 else if Typing_utils.is_sub_type_for_union ~coerce
:None env ty2 ty1
then
227 let r = union_reason
r1 r2 in
229 Typing_intersection.negate_type env
Reason.none ty2 ~approx
:Utils.ApproxUp
231 if Utils.is_sub_type_for_union env non_ty2 ty1
then
232 (env
, Typing_make_type.mixed
r)
235 Typing_intersection.negate_type
239 ~approx
:Utils.ApproxUp
241 if Utils.is_sub_type_for_union env non_ty1 ty2
then
242 (env
, Typing_make_type.mixed
r)
244 union_ ~approx_cancel_neg env ty1 ty2
r
246 and union_ ~approx_cancel_neg env ty1 ty2
r =
247 union_lists ~approx_cancel_neg env
[ty1
] [ty2
] r
249 (** Simplify the union of two types, for example (int|float) as num.
250 Returns None if there is no simplification.
251 Does not deal with null, options, unions and intersections, which are dealt with by union_lists.
252 If approx_cancel_neg is true, then some unions with negations are treated over-approximately:
253 C<t> | not C simplifies to mixed.*)
254 and simplify_union ~approx_cancel_neg env ty1 ty2
r =
255 Log.log_simplify_union r ty1 ty2
257 if ty_equal ty1 ty2
then
259 else if Typing_utils.is_sub_type_for_union ~coerce
:None env ty1 ty2
then
261 else if Typing_utils.is_sub_type_for_union ~coerce
:None env ty2 ty1
then
264 simplify_union_ ~approx_cancel_neg env ty1 ty2
r
267 match get_node
ty with
271 and simplify_union_ ~approx_cancel_neg env ty1 ty2
r =
272 let (env
, ty1
) = Env.expand_type env ty1
in
273 let (env
, ty2
) = Env.expand_type env ty2
in
275 match (deref ty1
, deref ty2
) with
276 | ((r1, Tprim
Nast.Tint
), (r2, Tprim
Nast.Tfloat
))
277 | ((r1, Tprim
Nast.Tfloat
), (r2, Tprim
Nast.Tint
)) ->
278 let r = union_reason
r1 r2 in
279 (env
, Some
(MakeType.num
r))
280 | ((_
, Tclass
((p
, id1
), e1
, tyl1
)), (_
, Tclass
((_
, id2
), e2
, tyl2
)))
281 when String.equal id1 id2
->
282 let e = exact_least_upper_bound e1 e2
in
283 if List.is_empty tyl1
then
284 (env
, Some
(mk
(r, Tclass
((p
, id1
), e, tyl1
))))
286 let (env
, tyl) = union_class ~approx_cancel_neg env id1 tyl1 tyl2
in
287 (env
, Some
(mk
(r, Tclass
((p
, id1
), e, tyl))))
288 | ((_
, Tgeneric
(name1
, [])), (_
, Tgeneric
(name2
, [])))
289 when String.equal name1 name2
->
290 (* TODO(T69551141) handle type arguments above and below properly *)
291 (env
, Some
(mk
(r, Tgeneric
(name1
, []))))
292 | ((_
, Tvarray ty1
), (_
, Tvarray ty2
)) ->
293 let (env
, ty) = union ~approx_cancel_neg env ty1 ty2
in
294 (env
, Some
(mk
(r, Tvarray
ty)))
295 | ((_
, Tdarray
(tk1
, tv1
)), (_
, Tdarray
(tk2
, tv2
))) ->
296 let (env
, tk
) = union ~approx_cancel_neg env tk1 tk2
in
297 let (env
, tv
) = union ~approx_cancel_neg env tv1 tv2
in
298 (env
, Some
(mk
(r, Tdarray
(tk
, tv
))))
299 | ((_
, Tvarray_or_darray
(tk1
, tv1
)), (_
, Tvarray_or_darray
(tk2
, tv2
))) ->
300 let (env
, tk
) = union ~approx_cancel_neg env tk1 tk2
in
301 let (env
, tv
) = union ~approx_cancel_neg env tv1 tv2
in
302 (env
, Some
(mk
(r, Tvarray_or_darray
(tk
, tv
))))
303 | ((_
, Tvec_or_dict
(tk1
, tv1
)), (_
, Tvec_or_dict
(tk2
, tv2
))) ->
304 let (env
, tk
) = union ~approx_cancel_neg env tk1 tk2
in
305 let (env
, tv
) = union ~approx_cancel_neg env tv1 tv2
in
306 (env
, Some
(mk
(r, Tvec_or_dict
(tk
, tv
))))
307 | ((_
, Tdependent
(dep1
, tcstr1
)), (_
, Tdependent
(dep2
, tcstr2
)))
308 when equal_dependent_type dep1 dep2
->
309 let (env
, tcstr
) = union ~approx_cancel_neg env tcstr1 tcstr2
in
310 (env
, Some
(mk
(r, Tdependent
(dep1
, tcstr
))))
311 | ((_
, Tdependent
(_
, ty1
)), _
) when is_class ty1
->
312 ty_equiv env ty1 ty2 ~are_ty_param
:false
313 | (_
, (_
, Tdependent
(_
, ty2
))) when is_class ty2
->
314 ty_equiv env ty2 ty1 ~are_ty_param
:false
315 | ((_
, Tnewtype
(id1
, tyl1
, tcstr1
)), (_
, Tnewtype
(id2
, tyl2
, tcstr2
)))
316 when String.equal id1 id2
->
317 if List.is_empty tyl1
then
320 let (env
, tyl) = union_newtype ~approx_cancel_neg env id1 tyl1 tyl2
in
321 let (env
, tcstr
) = union ~approx_cancel_neg env tcstr1 tcstr2
in
322 (env
, Some
(mk
(r, Tnewtype
(id1
, tyl, tcstr
))))
323 | ((_
, Ttuple tyl1
), (_
, Ttuple tyl2
)) ->
324 if Int.equal
(List.length tyl1
) (List.length tyl2
) then
326 List.map2_env env tyl1 tyl2 ~f
:(union ~approx_cancel_neg
)
328 (env
, Some
(mk
(r, Ttuple
tyl)))
331 | ((r1, Tshape
(shape_kind1
, fdm1
)), (r2, Tshape
(shape_kind2
, fdm2
))) ->
336 (shape_kind1
, fdm1
, r1)
337 (shape_kind2
, fdm2
, r2)
339 (env
, Some
(mk
(r, ty)))
340 | ((_
, Tfun ft1
), (_
, Tfun ft2
)) ->
341 let (env
, ft
) = union_funs ~approx_cancel_neg env ft1 ft2
in
342 (env
, Some
(mk
(r, Tfun ft
)))
343 | ((_
, Tunapplied_alias _
), _
) ->
344 Typing_defs.error_Tunapplied_alias_in_illegal_context
()
345 | ((_
, Tneg
(Neg_class
(_
, c1
))), (_
, Tclass
((_
, c2
), Exact
, [])))
346 | ((_
, Tclass
((_
, c2
), Exact
, [])), (_
, Tneg
(Neg_class
(_
, c1
))))
347 when String.equal c1 c2
->
348 (env
, Some
(MakeType.mixed
r))
349 | ((_
, Tneg
(Neg_class
(_
, c1
))), (_
, Tclass
((_
, c2
), Nonexact
, [])))
350 | ((_
, Tclass
((_
, c2
), Nonexact
, [])), (_
, Tneg
(Neg_class
(_
, c1
))))
351 when Typing_utils.is_sub_class_refl env c1 c2
->
352 (* This union is mixed iff for all objects o,
353 o not in complement (union tyl. c1<tyl>) implies o in c2,
354 which is equivalent to
355 (union tyl. c1<tyl>) subset c2.
356 This is certainly the case when c1 is a sub-class of c2, since
357 c2 has no type parameters.
359 (env
, Some
(MakeType.mixed
r))
360 | ((_
, Tneg
(Neg_class
(_
, c1
))), (_
, Tclass
((_
, c2
), Nonexact
, _
:: _
)))
361 | ((_
, Tclass
((_
, c2
), Nonexact
, _
:: _
)), (_
, Tneg
(Neg_class
(_
, c1
))))
362 when approx_cancel_neg
&& Typing_utils.is_sub_class_refl env c1 c2
->
363 (* Unlike the case where c2 has no parameters, here we can get a situation
364 where c1 is a sub-class of c2, but they don't union to mixed. For example,
365 Vector<int> | not Vector, which doesn't include Vector<string>, etc. However,
366 we can still approximate up to mixed when it would be both sound and convenient,
367 as controlled by the approx_cancel_neg flag. *)
368 (env
, Some
(MakeType.mixed
r))
369 | ((_
, Tneg
(Neg_class
(_
, c1
))), (_
, Tclass
((_
, c2
), Exact
, _
:: _
)))
370 | ((_
, Tclass
((_
, c2
), Exact
, _
:: _
)), (_
, Tneg
(Neg_class
(_
, c1
))))
371 when approx_cancel_neg
&& String.equal c1 c2
->
372 (env
, Some
(MakeType.mixed
r))
373 | ((_
, Tneg
(Neg_prim tp1
)), (_
, Tprim tp2
))
374 | ((_
, Tprim tp1
), (_
, Tneg
(Neg_prim tp2
)))
375 when Aast_defs.equal_tprim tp1 tp2
->
376 (env
, Some
(MakeType.mixed
r))
377 | ((_
, Tneg
(Neg_prim
Aast.Tnum
)), (_
, Tprim
Aast.Tarraykey
))
378 | ((_
, Tprim
Aast.Tarraykey
), (_
, Tneg
(Neg_prim
Aast.Tnum
))) ->
379 (env
, Some
(MakeType.neg
r (Neg_prim
Aast.Tfloat
)))
380 | ((_
, Tneg
(Neg_prim
Aast.Tarraykey
)), (_
, Tprim
Aast.Tnum
))
381 | ((_
, Tprim
Aast.Tnum
), (_
, Tneg
(Neg_prim
Aast.Tarraykey
))) ->
382 (env
, Some
(MakeType.neg
r (Neg_prim
Aast.Tstring
)))
383 | ((r1, Tintersection tyl1
), (r2, Tintersection tyl2
)) ->
384 (match Typing_algebra.factorize_common_types tyl1 tyl2
with
386 (* No common types, fall back to default case *)
387 ty_equiv env ty1 ty2 ~are_ty_param
:false
388 | (common_tyl
, tyl1'
, tyl2'
) ->
389 (match (tyl1'
, tyl2'
) with
392 (* If one of the intersections is now empty, then the union is mixed,
393 and we just return the common types from the intersetion *)
394 (env
, Some
(MakeType.intersection
r common_tyl
))
396 let (env
, union_ty
) =
397 match (tyl1'
, tyl2'
) with
399 (* It seems like it is wasteful to simplify with the main union function which checks
400 subtyping. If there was a subtype relationship between these two, there
401 would have been one with the common types in, and we wouldn't get here.
402 However, in some cases, that is not the case. E.g., ?#1 & t <: #1 & t will
403 not pass the initial sub-type check, but will here *)
404 union ~approx_cancel_neg env ty1 ty2
411 [MakeType.intersection
r1 tyl]
418 MakeType.intersection
r1 tyl1'
;
419 MakeType.intersection
r2 tyl2'
;
422 (match common_tyl
with
424 let (env
, inter_ty
) =
425 Typing_intersection.intersect env ~
r common_ty union_ty
428 | _
-> (env
, Some
(MakeType.intersection
r (union_ty
:: common_tyl
))))))
429 (* TODO with Tclass, union type arguments if covariant *)
431 ( Tprim _
| Tdynamic
| Tgeneric _
| Tnewtype _
| Tdependent _
432 | Tclass _
| Ttuple _
| Tfun _
| Tobject
| Tshape _
| Terr
| Tvar _
433 | Tvarray _
| Tdarray _
| Tvarray_or_darray _
| Tvec_or_dict _
434 (* If T cannot be null, `union T nonnull = nonnull`. However, it's hard
435 * to say whether a given T can be null - e.g. opaque newtypes, dependent
436 * types, etc. - so for now we leave it here.
437 * TODO improve that. *)
438 | Tnonnull
| Tany _
| Tintersection _
| Toption _
| Tunion _
439 | Taccess _
| Tneg _
) ),
441 ty_equiv env ty1 ty2 ~are_ty_param
:false
443 | Dont_simplify
-> (env
, None
)
445 (* Recognise a pattern t1 | t2 | t3 | (t4 & t5 & t6) which happens frequently
446 at control flow join points, and check for types that drop out of the intersection
447 because they union to mixed with types in the union. Assume that all of the types
448 in tyl1 and tyl2 are not unions, having been processed by decompose in the
449 union_lists function. The semantics are that tyl1 and tyl2 are the union of their
450 elements, and we are building the union of the two lists. Instead of just appending,
451 we look for the special case where one of the elements of tyl1 or tyl2 is itself
452 an intersection, and try to cancel things out. We return the simplified
453 lists for further processing.
455 For example if tyl1 = [bool; int; string] represents bool | int | string, and
456 tyl2 = [float; (dynamic & not int & not string)],
457 then to union tyl1 and tyl2, we can remove the not int and not string conjuncts
458 from tyl2 because they union to mixed with elements of tyl1. Thus we end up with
459 a result [bool; int; string], [float; dynamic]
461 and try_special_union_of_intersection ~approx_cancel_neg env tyl1 tyl2
r :
462 Typing_env_types.env
* locl_ty list
* locl_ty list
=
463 (* Assume that inter_ty is an intersection type and look for a pairs of a conjunct in
464 inter_ty and an element of tyl that union to mixed. Remove these from the intersection,
465 and return the remaining elements of the intersection,
466 or return None indicating that no pairs were found. *)
467 let simplify_inter env inter_ty
tyl : Typing_env_types.env
* locl_ty
option =
468 let changed = ref false in
469 let (env
, (inter_tyl
, inter_r
)) =
470 Typing_intersection.destruct_inter_list env
[inter_ty
]
472 let inter_r = Option.value inter_r ~default
:r in
473 let (env
, new_inter_tyl
) =
474 List.filter_map_env env inter_tyl ~f
:(fun env inter_ty
->
476 List.exists_env env
tyl ~f
:(fun env
ty ->
477 match simplify_union_ ~approx_cancel_neg env inter_ty
ty r with
478 | (env
, None
) -> (env
, false)
479 | (env
, Some
ty) -> (env
, Typing_utils.is_mixed env
ty))
484 | (env
, false) -> (env
, Some inter_ty
))
488 Some
(Typing_make_type.intersection
inter_r new_inter_tyl
)
492 let (inter_tyl1
, not_inter_tyl1
) =
493 List.partition_tf tyl1 ~f
:(Typing_utils.is_tintersection env
)
495 let (inter_tyl2
, not_inter_tyl2
) =
496 List.partition_tf tyl2 ~f
:(Typing_utils.is_tintersection env
)
498 let (env
, res_tyl1
) =
499 match inter_tyl1
with
501 (match simplify_inter env inter_ty1 not_inter_tyl2
with
502 | (env
, None
) -> (env
, tyl1
)
503 | (env
, Some new_inter1
) -> (env
, new_inter1
:: not_inter_tyl1
))
506 let (env
, res_tyl2
) =
507 match inter_tyl2
with
509 (match simplify_inter env inter_ty2 not_inter_tyl1
with
510 | (env
, None
) -> (env
, tyl2
)
511 | (env
, Some new_inter1
) -> (env
, new_inter1
:: not_inter_tyl2
))
514 (env
, res_tyl1
, res_tyl2
)
516 (** Union two lists of types together.
517 This has complexity N*M where N, M are the sized of the two lists.
518 The two lists are first flattened and null and dynamic are extracted from them,
519 then we attempt to simplify each pair of types. *)
520 and union_lists ~approx_cancel_neg env tyl1 tyl2
r =
521 let product_ty_tyl env ty1 tyl2
=
522 let rec product_ty_tyl env ty1 tyl2 tyl_res
=
524 | [] -> (env
, ty1
:: tyl_res
)
526 let (env
, ty_opt
) = simplify_union ~approx_cancel_neg env ty1 ty2
r in
528 | None
-> product_ty_tyl env ty1 tyl2
(ty2
:: tyl_res
)
529 | Some
ty -> product_ty_tyl env
ty tyl2 tyl_res
)
531 product_ty_tyl env ty1 tyl2
[]
533 let rec product env tyl1 tyl2
=
534 match (tyl1
, tyl2
) with
538 | (ty1
:: tyl1
, tyl2
) ->
539 let (env
, tyl2
) = product_ty_tyl env ty1 tyl2
in
540 product env tyl1 tyl2
542 let (env
, (tyl1
, r_null1
, _r_union1
, r_dyn1
)) = dest_union_list env tyl1
in
543 let (env
, (tyl2
, r_null2
, _r_union2
, r_dyn2
)) = dest_union_list env tyl2
in
544 let (env
, tyl1
, tyl2
) =
545 try_special_union_of_intersection ~approx_cancel_neg env tyl1 tyl2
r
547 let (env
, tyl) = product env tyl1 tyl2
in
548 let r_null = Option.first_some r_null1 r_null2
in
549 let r_dyn = Option.first_some r_dyn1 r_dyn2
in
550 make_union env
r tyl r_null r_dyn
552 and union_funs ~approx_cancel_neg env fty1 fty2
=
553 (* TODO: If we later add fields to ft, they will be forgotten here. *)
555 equal_locl_fun_arity fty1 fty2
556 && Int.equal fty1
.ft_flags fty2
.ft_flags
557 && Int.equal
(ft_params_compare fty1
.ft_params fty2
.ft_params
) 0
560 union_possibly_enforced_tys ~approx_cancel_neg env fty1
.ft_ret fty2
.ft_ret
562 (env
, { fty1
with ft_ret
})
566 and union_possibly_enforced_tys ~approx_cancel_neg env ety1 ety2
=
567 let (env
, et_type
) = union ~approx_cancel_neg env ety1
.et_type ety2
.et_type
in
568 (env
, { ety1
with et_type
})
570 and union_class ~approx_cancel_neg env name tyl1 tyl2
=
572 match Env.get_class env name
with
574 | Some c
-> Decl_provider.Class.tparams c
576 union_tylists_w_variances ~approx_cancel_neg env
tparams tyl1 tyl2
578 and union_newtype ~approx_cancel_neg env typename tyl1 tyl2
=
580 match Env.get_typedef env typename
with
582 | Some t
-> t
.td_tparams
584 union_tylists_w_variances ~approx_cancel_neg env
tparams tyl1 tyl2
586 and union_tylists_w_variances ~approx_cancel_neg env
tparams tyl1 tyl2
=
587 let variances = List.map
tparams ~f
:(fun t
-> t
.tp_variance
) in
589 let adjust_list_length l newlen filler
=
590 let len = List.length l
in
592 l
@ List.init
(newlen
- len) ~f
:(fun _
-> filler
)
594 List.sub l ~pos
:0 ~
len:newlen
596 adjust_list_length variances (List.length tyl1
) Ast_defs.Invariant
598 let merge_ty_params env ty1 ty2 variance
=
600 | Ast_defs.Covariant
-> union ~approx_cancel_neg env ty1 ty2
602 let (env
, ty_opt
) = ty_equiv env ty1 ty2 ~are_ty_param
:true in
604 | Some
ty -> (env
, ty)
605 | None
-> raise Dont_simplify
)
608 try List.map3_env env tyl1 tyl2
variances ~f
:merge_ty_params with
609 | Invalid_argument _
-> raise Dont_simplify
614 ~approx_cancel_neg env
(shape_kind1
, fdm1
, r1) (shape_kind2
, fdm2
, r2) =
615 let shape_kind = union_shape_kind shape_kind1 shape_kind2
in
616 let ((env
, shape_kind), fdm
) =
621 ~combine
:(fun (env
, shape_kind) k fieldopt1 fieldopt2
->
622 match ((shape_kind1
, fieldopt1
, r1), (shape_kind2
, fieldopt2
, r2)) with
623 | ((_
, None
, _
), (_
, None
, _
)) -> ((env
, shape_kind), None
)
624 (* key is present on one side but not the other *)
625 | ((_
, Some
{ sft_ty
; _
}, _
), (shape_kind_other
, None
, r))
626 | ((shape_kind_other
, None
, r), (_
, Some
{ sft_ty
; _
}, _
)) ->
628 match shape_kind_other
with
629 | Closed_shape
-> sft_ty
632 Reason.Rmissing_optional_field
633 (Reason.to_pos
r, Utils.get_printable_shape_field_name k
)
637 ((env
, shape_kind), Some
{ sft_optional
= true; sft_ty })
638 (* key is present on both sides *)
639 | ( (_
, Some
{ sft_optional
= optional1
; sft_ty = ty1
}, _
),
640 (_
, Some
{ sft_optional
= optional2
; sft_ty = ty2
}, _
) ) ->
641 let sft_optional = optional1
|| optional2
in
642 let (env
, sft_ty) = union ~approx_cancel_neg env ty1 ty2
in
643 ((env
, shape_kind), Some
{ sft_optional; sft_ty }))
645 (env
, Tshape
(shape_kind, fdm
))
647 and union_shape_kind shape_kind1 shape_kind2
=
648 match (shape_kind1
, shape_kind2
) with
649 | (Closed_shape
, Closed_shape
) -> Closed_shape
652 (* TODO: add a new reason with positions of merge point and possibly merged
654 and union_reason
r1 r2 =
655 if Reason.is_none
r1 then
657 else if Reason.is_none
r2 then
659 else if Reason.compare
r1 r2 <= 0 then
664 (** Takes a list of types, possibly including unions, and flattens it.
665 Returns a set of types.
666 If null is encountered (possibly in an option), get its reason and return it
667 in a separate optional reason (do not add null in the resulting set of types).
668 Do the same if dynamic is encountered.
669 This is because options and dynamic have to be "pushed out" of the resulting union. *)
670 let normalize_union env ?on_tyvar
tyl :
676 let orr r_opt
r = Some
(Option.value r_opt ~default
:r) in
677 let rec normalize_union env
tyl r_null r_union
r_dyn =
679 | [] -> (env
, r_null, r_union
, r_dyn, TySet.empty
)
681 let (env
, ty) = Env.expand_type env
ty in
682 let proceed env
ty = (env
, r_null, r_union
, r_dyn, TySet.singleton
ty) in
683 let (env
, r_null, r_union
, r_dyn, tys'
) =
684 match (deref
ty, on_tyvar
) with
685 | ((r, Tvar v
), Some on_tyvar
) ->
686 let (env
, ty'
) = on_tyvar env
r v
in
687 if ty_equal
ty ty'
then
690 normalize_union env
[ty'
] r_null r_union
r_dyn
691 | ((r, Tprim
Nast.Tnull
), _
) ->
692 normalize_union env
[] (orr r_null r) r_union
r_dyn
693 | ((r, Toption
ty), _
) ->
694 normalize_union env
[ty] (orr r_null r) r_union
r_dyn
695 | ((r, Tunion
tyl'
), _
) ->
696 normalize_union env
tyl'
r_null (orr r_union
r) r_dyn
697 | ((r, Tdynamic
), _
) ->
698 normalize_union env
[] r_null r_union
(orr r_dyn r)
699 | ((_
, Tintersection _
), _
) ->
700 let (env
, ty) = Utils.simplify_intersections env
ty ?on_tyvar
in
701 let (env
, ety
) = Env.expand_type env
ty in
704 | (_
, Tintersection _
) -> proceed env
ty
705 | _
-> normalize_union env
[ty] r_null r_union
r_dyn
707 | _
-> proceed env
ty
709 let (env
, r_null, r_union
, r_dyn, tys
) =
710 normalize_union env
tyl r_null r_union
r_dyn
712 (env
, r_null, r_union
, r_dyn, TySet.union tys' tys
)
714 normalize_union env
tyl None None None
716 (* Is this type minimal with respect to the ground subtype ordering, if we ignore the nothing type?
717 * Examples are exact or final non-generic classes.
718 * Non-examples (surprisingly) are int or string (because an enum can be a subtype).
720 * Another example is classname<C>, if C is minimal, and this pattern occurs
721 * frequently e.g. vec[C::class, D::class].
723 * Note that doesn't work for general covariant classes or built-ins e.g. vec<exact C> is not
724 * minimal because vec<nothing> <: vec<exact C>. But classname<nothing> is uninhabited so it's
725 * ok to suppose that it is equivalent to nothing.
727 * Finally note that if we did treat string, int, and float as minimal, then the code in
728 * union_list_2_by_2 would not simplify int|float to num or string|classname<C> to string.
730 let rec is_minimal env
ty =
731 match get_node
ty with
732 | Tclass
(_
, Exact
, []) -> true
733 | Tclass
((_
, name
), _
, []) ->
735 match Env.get_class env name
with
736 | Some cd
-> Cls.final cd
739 | Tnewtype
(name
, [ty], _
)
740 when String.equal name
Naming_special_names.Classes.cClassname
->
744 (** Construct union of a list of types by applying binary union pairwise.
745 * This uses a quadratic number of subtype tests so we apply the following
746 * optimization that's particularly useful for the common pattern of vec[C1::class,...,Cn::class].
748 * First split the list of types into "final" types (those known to have no non-empty subtypes)
749 * and non-final types (that might have a non-empty subtype).
750 * For the non-final types, we apply binary union pointwise, which is quadratic in the number
751 * of types. Then for each of the final types we drop those that subtype any of the types
754 * If m is the number of final types, and n is the number of non-final types, the first
755 * stage uses O(n^2) subtype tests and the second stage uses O(mn) subtype tests.
756 * One common pattern is a union of exact classnames: constructing this will involve no
757 * subtype tests at all.
759 let union_list_2_by_2 ~approx_cancel_neg env
r tyl =
760 let (tyl_final
, tyl_nonfinal
) = List.partition_tf
tyl ~f
:(is_minimal env
) in
761 let (env
, reduced_nonfinal
) =
762 List.fold tyl_nonfinal ~init
:(env
, []) ~f
:(fun (env
, res_tyl
) ty ->
763 let rec union_ty_w_tyl env
ty tyl tyl_acc
=
765 | [] -> (env
, ty :: tyl_acc
)
767 let (env
, union_opt
) =
768 simplify_union ~approx_cancel_neg env
ty ty'
r
770 (match union_opt
with
771 | None
-> union_ty_w_tyl env
ty tyl (ty'
:: tyl_acc
)
772 | Some
union -> (env
, tyl @ union :: tyl_acc
))
774 union_ty_w_tyl env
ty res_tyl
[])
777 List.filter tyl_final ~f
:(fun fty
->
781 ~f
:(Typing_utils.is_sub_type_for_union ~coerce
:None env fty
)))
783 (env
, reduced_final @ reduced_nonfinal
)
785 let union_list env ?
(approx_cancel_neg
= false) r tyl =
786 Log.log_union_list r tyl
788 let (env
, r_null, _r_union
, r_dyn, tys
) = normalize_union env
tyl in
790 union_list_2_by_2 ~approx_cancel_neg env
r (TySet.elements tys
)
792 make_union env
r tyl r_null r_dyn
794 let fold_union env ?
(approx_cancel_neg
= false) r tyl =
798 ~init
:(MakeType.nothing
r)
799 ~f
:(union ~approx_cancel_neg
)
801 (* See documentation in mli file *)
802 let simplify_unions env ?
(approx_cancel_neg
= false) ?on_tyvar
ty =
803 let r = get_reason
ty in
804 Log.log_simplify_unions r ty
806 let (env
, r_null, r_union
, r_dyn, tys
) = normalize_union env
[ty] ?on_tyvar
in
808 union_list_2_by_2 ~approx_cancel_neg env
r (TySet.elements tys
)
810 let r = Option.value r_union ~default
:r in
811 make_union env
r tyl r_null r_dyn
813 let rec union_i env ?
(approx_cancel_neg
= false) r ty1 lty2
=
814 let ty2 = LoclType lty2
in
815 if Typing_utils.is_sub_type_for_union_i ~coerce
:None env ty1
ty2 then
817 else if Typing_utils.is_sub_type_for_union_i ~coerce
:None env
ty2 ty1
then
823 let (env
, ty) = union ~approx_cancel_neg env lty1 lty2
in
825 | ConstraintType cty1
->
826 (match deref_constraint_type cty1
with
827 | (_
, TCunion
(lty1
, cty1
)) ->
828 let (env
, lty
) = union ~approx_cancel_neg env lty1 lty2
in
829 (env
, ConstraintType
(mk_constraint_type
(r, TCunion
(lty
, cty1
))))
830 | (r'
, TCintersection
(lty1
, cty1
)) ->
831 (* Distribute union over intersection.
832 At the moment local types in TCintersection can only be
833 unions or intersections involving only null and nonnull,
834 so applying distributivity allows for simplifying the types. *)
835 let (env
, lty
) = union ~approx_cancel_neg env lty1 lty2
in
837 union_i ~approx_cancel_neg env
r (ConstraintType cty1
) lty2
841 let (env
, ty) = Typing_intersection.intersect env ~
r:r' lty
ty in
843 | ConstraintType cty
->
846 (mk_constraint_type
(r'
, TCintersection
(lty
, cty
))) ))
848 | (_
, Tdestructure _
) ->
849 (env
, ConstraintType
(mk_constraint_type
(r, TCunion
(lty2
, cty1
)))))
852 | LoclType _
-> (env
, ty)
853 | ConstraintType
ty -> Utils.simplify_constraint_type env
ty
855 let () = Typing_utils.union_ref
:= union
857 let () = Typing_utils.union_i_ref
:= union_i
859 let () = Typing_utils.union_list_ref
:= union_list
861 let () = Typing_utils.fold_union_ref
:= fold_union
863 let () = Typing_utils.simplify_unions_ref
:= simplify_unions
865 let () = Typing_utils.make_union_ref
:= make_union
868 (* discard optional parameters. *)
869 let simplify_unions env
ty = simplify_unions env
ty in
870 Typing_env.simplify_unions_ref
:= simplify_unions