Deal with final types specially when constructing unions
[hiphop-php.git] / hphp / hack / src / typing / typing_union.ml
blob89c6beb81a9df59530c89d15b87dfc4bbb57b654
1 (*
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
10 open Hh_prelude
11 open Common
12 open Typing_defs
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
18 module Nast = Aast
19 module Cls = Decl_provider.Class
21 exception Dont_simplify
23 module Log = struct
24 let log_union r ty1 ty2 (env, result) =
25 Typing_log.(
26 log_with_level env "union" ~level:1 (fun () ->
27 log_types
28 (Reason.to_pos r)
29 env
31 Log_head
32 ( "Typing_union.union",
34 Log_type ("ty1", ty1);
35 Log_type ("ty2", ty2);
36 Log_type ("result", result);
37 ] );
38 ]));
39 (env, result)
41 let log_simplify_union r ty1 ty2 (env, result) =
42 Typing_log.(
43 log_with_level env "union" ~level:2 (fun () ->
44 log_types
45 (Reason.to_pos r)
46 env
48 Log_head
49 ( "Typing_union.simplify_union",
51 Log_type ("ty1", ty1);
52 Log_type ("ty2", ty2);
53 (match result with
54 | None -> Log_head ("result: None", [])
55 | Some ty -> Log_type ("result: Some", ty));
56 ] );
57 ]));
58 (env, result)
60 let log_union_list r tyl (env, result) =
61 Typing_log.(
62 log_with_level env "union" ~level:1 (fun () ->
63 log_types
64 (Reason.to_pos r)
65 env
67 Log_head
68 ( "Typing_union.union_list",
69 List.map tyl ~f:(fun ty -> Log_type ("ty", ty))
70 @ [Log_type ("result", result)] );
71 ]));
72 (env, result)
74 let log_simplify_unions r ty (env, result) =
75 Typing_log.(
76 log_with_level env "union" ~level:1 (fun () ->
77 log_types
78 (Reason.to_pos r)
79 env
81 Log_head
82 ( "Typing_union.simplify_unions",
83 [Log_type ("ty", ty); Log_type ("result", result)] );
84 ]));
85 (env, result)
86 end
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
106 let ty =
107 match ((get_node ety1, ty1), (get_node ety2, ty2)) with
108 | ((Tunion [], _), (_, ty))
109 | ((_, ty), (Tunion [], _))
110 when are_ty_param ->
111 Some ty
112 | _ when ty_equal ety1 ety2 ->
113 begin
114 match get_node ty1 with
115 | Tvar _ -> Some ty1
116 | _ -> Some ty2
118 | _ -> None
120 (env, ty)
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
129 match deref ty with
130 | (r, Tunion tyl') ->
131 dest_union_list env (tyl' @ tyl) tyl_res r_null (orr r_union r) r_dyn
132 | (r, Toption ty) ->
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
136 | (r, Tdynamic) ->
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 =
140 match tyl with
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 =
152 let ty =
153 match tyl with
154 | [ty] -> ty
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
167 match deref ty with
168 | (_, Toption ty) ->
169 (* ok to remove the Toption since we'll re-add it at the end. *)
170 simplify env ty
171 | (r, Tunion tyl) ->
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)
175 else
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)
181 | _ -> (env, ty)
183 simplify env ty
185 let (env, ty) =
186 if Option.is_some reason_nullable_opt then
187 simplify_nonnull env ty
188 else
189 (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. *)
193 let ty =
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
208 (env, ty)
210 let exact_least_upper_bound e1 e2 =
211 match (e1, e2) with
212 | (Exact, Exact) -> Exact
213 | (_, _) -> Nonexact
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
221 (env, ty1)
222 else if Typing_utils.is_sub_type_for_union ~coerce:None env ty1 ty2 then
223 (env, ty2)
224 else if Typing_utils.is_sub_type_for_union ~coerce:None env ty2 ty1 then
225 (env, ty1)
226 else
227 let r = union_reason r1 r2 in
228 let (env, non_ty2) =
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)
233 else
234 let (env, non_ty1) =
235 Typing_intersection.negate_type
237 Reason.none
239 ~approx:Utils.ApproxUp
241 if Utils.is_sub_type_for_union env non_ty1 ty2 then
242 (env, Typing_make_type.mixed r)
243 else
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
258 (env, Some ty1)
259 else if Typing_utils.is_sub_type_for_union ~coerce:None env ty1 ty2 then
260 (env, Some ty2)
261 else if Typing_utils.is_sub_type_for_union ~coerce:None env ty2 ty1 then
262 (env, Some ty1)
263 else
264 simplify_union_ ~approx_cancel_neg env ty1 ty2 r
266 and is_class ty =
267 match get_node ty with
268 | Tclass _ -> true
269 | _ -> false
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))))
285 else
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
318 (env, Some ty1)
319 else
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
325 let (env, tyl) =
326 List.map2_env env tyl1 tyl2 ~f:(union ~approx_cancel_neg)
328 (env, Some (mk (r, Ttuple tyl)))
329 else
330 (env, None)
331 | ((r1, Tshape (shape_kind1, fdm1)), (r2, Tshape (shape_kind2, fdm2))) ->
332 let (env, ty) =
333 union_shapes
334 ~approx_cancel_neg
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
385 | ([], _, _) ->
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
390 | ([], _)
391 | (_, []) ->
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))
395 | _ ->
396 let (env, union_ty) =
397 match (tyl1', tyl2') with
398 | ([ty1], [ty2]) ->
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
405 | ([ty], tyl)
406 | (tyl, [ty]) ->
407 union_lists
408 ~approx_cancel_neg
410 [ty]
411 [MakeType.intersection r1 tyl]
413 | _ ->
414 ( env,
415 MakeType.union
418 MakeType.intersection r1 tyl1';
419 MakeType.intersection r2 tyl2';
422 (match common_tyl with
423 | [common_ty] ->
424 let (env, inter_ty) =
425 Typing_intersection.intersect env ~r common_ty union_ty
427 (env, Some inter_ty)
428 | _ -> (env, Some (MakeType.intersection r (union_ty :: common_tyl))))))
429 (* TODO with Tclass, union type arguments if covariant *)
430 | ( ( _,
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 _ ) ),
440 (_, _) ) ->
441 ty_equiv env ty1 ty2 ~are_ty_param:false
442 with
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 ->
475 match
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))
480 with
481 | (env, true) ->
482 changed := true;
483 (env, None)
484 | (env, false) -> (env, Some inter_ty))
486 ( env,
487 if !changed then
488 Some (Typing_make_type.intersection inter_r new_inter_tyl)
489 else
490 None )
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
500 | [inter_ty1] ->
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))
504 | _ -> (env, tyl1)
506 let (env, res_tyl2) =
507 match inter_tyl2 with
508 | [inter_ty2] ->
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))
512 | _ -> (env, 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 =
523 match tyl2 with
524 | [] -> (env, ty1 :: tyl_res)
525 | ty2 :: tyl2 ->
526 let (env, ty_opt) = simplify_union ~approx_cancel_neg env ty1 ty2 r in
527 (match ty_opt with
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
535 | ([], tyl)
536 | (tyl, []) ->
537 (env, tyl)
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
558 then
559 let (env, ft_ret) =
560 union_possibly_enforced_tys ~approx_cancel_neg env fty1.ft_ret fty2.ft_ret
562 (env, { fty1 with ft_ret })
563 else
564 raise Dont_simplify
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 =
571 let tparams =
572 match Env.get_class env name with
573 | None -> []
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 =
579 let tparams =
580 match Env.get_typedef env typename with
581 | None -> []
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
588 let variances =
589 let adjust_list_length l newlen filler =
590 let len = List.length l in
591 if len < newlen then
592 l @ List.init (newlen - len) ~f:(fun _ -> filler)
593 else
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 =
599 match variance with
600 | Ast_defs.Covariant -> union ~approx_cancel_neg env ty1 ty2
601 | _ ->
602 let (env, ty_opt) = ty_equiv env ty1 ty2 ~are_ty_param:true in
603 (match ty_opt with
604 | Some ty -> (env, ty)
605 | None -> raise Dont_simplify)
607 let (env, tyl) =
608 try List.map3_env env tyl1 tyl2 variances ~f:merge_ty_params with
609 | Invalid_argument _ -> raise Dont_simplify
611 (env, tyl)
613 and union_shapes
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) =
617 TShapeMap.merge_env
618 (env, shape_kind)
619 fdm1
620 fdm2
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; _ }, _)) ->
627 let sft_ty =
628 match shape_kind_other with
629 | Closed_shape -> sft_ty
630 | Open_shape ->
631 let r =
632 Reason.Rmissing_optional_field
633 (Reason.to_pos r, Utils.get_printable_shape_field_name k)
635 MakeType.mixed r
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
650 | _ -> Open_shape
652 (* TODO: add a new reason with positions of merge point and possibly merged
653 * envs.*)
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
661 else
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 :
671 Typing_env_types.env
672 * Reason.t option
673 * Reason.t option
674 * Reason.t option
675 * TySet.t =
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 =
678 match tyl with
679 | [] -> (env, r_null, r_union, r_dyn, TySet.empty)
680 | ty :: tyl ->
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
688 proceed env ty'
689 else
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
702 begin
703 match deref ety with
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), _, []) ->
734 begin
735 match Env.get_class env name with
736 | Some cd -> Cls.final cd
737 | None -> false
739 | Tnewtype (name, [ty], _)
740 when String.equal name Naming_special_names.Classes.cClassname ->
741 is_minimal env ty
742 | _ -> false
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
752 * in the result.
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 =
764 match tyl with
765 | [] -> (env, ty :: tyl_acc)
766 | ty' :: tyl ->
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 [])
776 let reduced_final =
777 List.filter tyl_final ~f:(fun fty ->
779 (List.exists
780 reduced_nonfinal
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
789 let (env, tyl) =
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 =
795 List.fold_left_env
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
807 let (env, tyl) =
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
816 (env, ty2)
817 else if Typing_utils.is_sub_type_for_union_i ~coerce:None env ty2 ty1 then
818 (env, ty1)
819 else
820 let (env, ty) =
821 match ty1 with
822 | LoclType lty1 ->
823 let (env, ty) = union ~approx_cancel_neg env lty1 lty2 in
824 (env, LoclType ty)
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
836 let (env, ty) =
837 union_i ~approx_cancel_neg env r (ConstraintType cty1) lty2
839 (match ty with
840 | LoclType ty ->
841 let (env, ty) = Typing_intersection.intersect env ~r:r' lty ty in
842 (env, LoclType ty)
843 | ConstraintType cty ->
844 ( env,
845 ConstraintType
846 (mk_constraint_type (r', TCintersection (lty, cty))) ))
847 | (_, Thas_member _)
848 | (_, Tdestructure _) ->
849 (env, ConstraintType (mk_constraint_type (r, TCunion (lty2, cty1)))))
851 match ty with
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
867 let () =
868 (* discard optional parameters. *)
869 let simplify_unions env ty = simplify_unions env ty in
870 Typing_env.simplify_unions_ref := simplify_unions