@@ -132,10 +132,29 @@ module TyPredicate = struct
132132 | Tnonnull -> Result. Error " nonnull"
133133 | Tdynamic -> Result. Error " dynamic"
134134 | Tany _ -> Result. Error " any"
135- | Toption _ -> Result. Error " option"
135+ | Toption ty_opt -> begin
136+ let open Hh_prelude.Result.Let_syntax in
137+ let * (next_wildcard_id, pred_opt) = of_ty env next_wildcard_id ty_opt in
138+ let pred_null = (get_reason ty, IsTag NullTag ) in
139+ return (next_wildcard_id, IsUnionOf [pred_opt; pred_null])
140+ |> Result. map_error ~f: (fun err -> " option-" ^ err)
141+ end
136142 | Tfun _ -> Result. Error " fun"
137143 | Tgeneric _ -> Result. Error " generic"
138- | Tunion _ -> Result. Error " union"
144+ | Tunion tys -> begin
145+ match
146+ List. fold_result tys ~init: (next_wildcard_id, [] ) ~f: (fun acc ty ->
147+ let (next_wildcard_id, predicates) = acc in
148+ let open Hh_prelude.Result.Let_syntax in
149+ let * (next_wildcard_id, predicate) =
150+ of_ty env next_wildcard_id ty
151+ in
152+ return (next_wildcard_id, predicate :: predicates))
153+ with
154+ | Result. Error err -> Result. Error (" union-" ^ err)
155+ | Result. Ok (next_wildcard_id , predicates ) ->
156+ Result. Ok (next_wildcard_id, IsUnionOf (List. rev predicates))
157+ end
139158 | Tintersection _ -> Result. Error " intersection"
140159 | Tvec_or_dict _ -> Result. Error " vec_or_dict"
141160 | Taccess _ -> Result. Error " access"
@@ -270,6 +289,15 @@ module TyPredicate = struct
270289 IMap. empty
271290 in
272291 (env, new_tparams)
292+ | (_reason , IsUnionOf predicates ) ->
293+ let (env, new_tparams) =
294+ List. map_env env predicates ~f: (fun env predicate ->
295+ let (env, new_tparams) =
296+ instantiate_wildcards_for_predicate env predicate p
297+ in
298+ (env, new_tparams))
299+ in
300+ (env, List. fold new_tparams ~init: IMap. empty ~f: IMap. union)
273301
274302 let rec to_ty lookup_wildcard predicate =
275303 let tag_to_ty reason tag =
@@ -312,6 +340,23 @@ module TyPredicate = struct
312340 sp_fields
313341 in
314342 Typing_make_type. shape reason (Typing_make_type. nothing reason) map
343+ | (reason , IsUnionOf predicates ) -> begin
344+ match
345+ List. partition_tf predicates ~f: (fun p ->
346+ match p with
347+ | (_ , IsTag NullTag) -> true
348+ | _ -> false )
349+ with
350+ | (_ :: _ , [other ]) ->
351+ Typing_make_type. nullable reason (to_ty lookup_wildcard other)
352+ | (_ :: _ , others ) ->
353+ Typing_make_type. nullable reason
354+ @@ Typing_make_type. union reason
355+ @@ List. map others ~f: (to_ty lookup_wildcard)
356+ | ([] , _ ) ->
357+ Typing_make_type. union reason
358+ @@ List. map predicates ~f: (to_ty lookup_wildcard)
359+ end
315360
316361 exception FoundWildcard
317362
@@ -564,6 +609,11 @@ module TyPartition = struct
564609 let meet (partition1 , t1 , f1 ) (partition2 , t2 , f2 ) =
565610 (Partition. meet partition1 partition2, UTL. conj t1 t2, UTL. conj f1 f2)
566611
612+ let union_combine (partition1 , t1 , f1 ) (partition2 , t2 , f2 ) =
613+ ( Partition. union_combine partition1 partition2,
614+ UTL. disj t1 t2,
615+ UTL. conj f1 f2 )
616+
567617 let product ~f sub_splits_and_assumptions =
568618 let sub_splits = List. map sub_splits_and_assumptions ~f: fst3 in
569619 let assumption_pairs =
@@ -770,6 +820,36 @@ and split_ty_by_tag
770820 else
771821 (env, TyPartition. mk_span ~env ~predicate ty)
772822
823+ and split_ty_by_union
824+ ~(other_intersected_tys : locl_ty list )
825+ ~(expansions : SSet.t )
826+ (env : env )
827+ (ty : locl_ty )
828+ (predicates : type_predicate list ) : env * TyPartition.t =
829+ (* Split the type by each predicate in the union.
830+
831+ When splitting T by union predicate (P1 | P2 | ... | Pn):
832+ - Left: parts that pass at least one predicate = L1 ∪ L2 ∪ ... ∪ Ln
833+ - Right: parts that fail all predicates = R1 ∩ R2 ∩ ... ∩ Rn
834+ - Span: parts that may or may not pass = everything else
835+
836+ This is NOT the same as joining the partitions (which would give L1∪L2, S1∪S2, R1∪R2).
837+ *)
838+ let (env, partitions) =
839+ List. fold_map
840+ ~init: env
841+ ~f: (fun env pred ->
842+ split_ty ~other_intersected_tys ~expansions env ty ~predicate: pred)
843+ predicates
844+ in
845+ (* Combine partitions using the correct algebra for union predicates *)
846+ let partition =
847+ match partitions with
848+ | [] -> TyPartition. mk_bottom
849+ | first :: rest -> List. fold rest ~init: first ~f: TyPartition. union_combine
850+ in
851+ (env, partition)
852+
773853and split_ty
774854 ~(other_intersected_tys : locl_ty list )
775855 ~(expansions : SSet.t )
@@ -797,6 +877,8 @@ and split_ty
797877 shape_predicate
798878 predicate
799879 | IsTag tag -> split_ty_by_tag ~ty_datatype env ety tag predicate
880+ | IsUnionOf predicates ->
881+ split_ty_by_union ~other_intersected_tys ~expansions env ety predicates
800882 in
801883 let split_union ~other_intersected_tys ~expansions env (tys : locl_ty list ) =
802884 let (env, partitions) =
@@ -855,12 +937,21 @@ and split_ty
855937 partition_f
856938 DataType. (of_ty ~safe_for_are_disjoint: true env @@ Class (name, args))
857939 | Tneg (_ , predicate ) ->
858- let ( env, dty) =
940+ let rec to_dty env predicate =
859941 match predicate with
860942 | IsTag tag -> DataType. of_tag ~safe_for_are_disjoint: false env tag
861943 | IsTupleOf _ -> DataType. (of_ty ~safe_for_are_disjoint: false env Tuple )
862944 | IsShapeOf _ -> DataType. (of_ty ~safe_for_are_disjoint: false env Shape )
945+ | IsUnionOf preds ->
946+ List. fold_left_env
947+ env
948+ preds
949+ ~init: DataType. empty
950+ ~f: (fun env dt_acc pred ->
951+ let (env, dt) = to_dty env (snd pred) in
952+ (env, DataType. union dt_acc dt))
863953 in
954+ let (env, dty) = to_dty env predicate in
864955 let dty = DataType. complement dty in
865956 partition_f (env, dty)
866957 | Tprim Aast. Tnoreturn -> (env, TyPartition. mk_bottom)
0 commit comments