@@ -721,18 +721,42 @@ defmodule Module.Types.Descr do
721721 { :term , static , [ ] }
722722
723723 { dynamic , static } ->
724- # Computing term_type?(difference(dynamic, static)) can be
725- # expensive, so we check for term type before hand and check
726- # for :term exclusively in dynamic_to_quoted/2.
727- if term_type? ( dynamic ) do
728- { :term , static , [ ] }
729- else
730- # Denormalize functions before we do the difference
731- { static , dynamic , extra } = fun_denormalize ( static , dynamic , opts )
732- { difference ( dynamic , static ) , static , extra }
724+ cond do
725+ # Computing term_type?(difference(dynamic, static)) can be
726+ # expensive, so we check for term type before hand and check
727+ # for :term exclusively in dynamic_to_quoted/2.
728+ term_type? ( dynamic ) ->
729+ { :term , static , [ ] }
730+
731+ # We need to check for quoted here before we compute the difference below
732+ quoted = maybe_negated_term_type_to_quoted ( static , opts ) ->
733+ { dynamic , % { } , [ quoted ] }
734+
735+ # Denormalize functions (only unions) before we do the difference
736+ true ->
737+ { static , dynamic , extra } = fun_denormalize ( static , dynamic , opts )
738+ { difference ( dynamic , static ) , static , extra }
733739 end
734740 end
735741
742+ { static , extra } =
743+ if quoted = maybe_negated_term_type_to_quoted ( static , opts ) do
744+ { % { } , [ quoted | extra ] }
745+ else
746+ { static , extra }
747+ end
748+
749+ # Dynamic always come first for visibility
750+ unions =
751+ to_quoted ( :dynamic , dynamic , opts ) ++ static_non_term_type_to_quoted ( static , extra , opts )
752+
753+ case unions do
754+ [ ] -> { :none , [ ] , [ ] }
755+ unions -> Enum . reduce ( unions , & { :or , [ ] , [ & 2 , & 1 ] } )
756+ end
757+ end
758+
759+ defp static_non_term_type_to_quoted ( static , extra , opts ) do
736760 # Merge empty list and list together if they both exist
737761 { extra , static } =
738762 case static do
@@ -748,17 +772,7 @@ defmodule Module.Types.Descr do
748772 { extra , static }
749773 end
750774
751- # Dynamic always come first for visibility
752- unions =
753- to_quoted ( :dynamic , dynamic , opts ) ++
754- Enum . sort (
755- extra ++ Enum . flat_map ( static , fn { key , value } -> to_quoted ( key , value , opts ) end )
756- )
757-
758- case unions do
759- [ ] -> { :none , [ ] , [ ] }
760- unions -> Enum . reduce ( unions , & { :or , [ ] , [ & 2 , & 1 ] } )
761- end
775+ Enum . sort ( extra ++ Enum . flat_map ( static , fn { key , value } -> to_quoted ( key , value , opts ) end ) )
762776 end
763777
764778 defp to_quoted ( :atom , val , _opts ) , do: atom_to_quoted ( val )
@@ -769,6 +783,54 @@ defmodule Module.Types.Descr do
769783 defp to_quoted ( :tuple , bdd , opts ) , do: tuple_to_quoted ( bdd , opts )
770784 defp to_quoted ( :fun , bdd , opts ) , do: fun_to_quoted ( bdd , opts )
771785
786+ defp maybe_negated_term_type_to_quoted ( static , opts ) do
787+ if print_as_negated_type? ( static ) do
788+ static
789+ |> negation ( )
790+ |> unfold ( )
791+ |> static_non_term_type_to_quoted ( [ ] , opts )
792+ |> case do
793+ [ ] ->
794+ nil
795+
796+ [ head | tail ] ->
797+ Enum . reduce ( tail , { :not , [ ] , [ head ] } , & { :and , [ ] , [ & 2 , { :not , [ ] , [ & 1 ] } ] } )
798+ end
799+ end
800+ end
801+
802+ # Unions would be printed as negations within negations
803+ defp print_as_negated_type? ( % { atom: { :union , _ } } ) , do: false
804+ defp print_as_negated_type? ( % { fun: { :union , _ } } ) , do: false
805+
806+ defp print_as_negated_type? ( descr ) do
807+ # In here we compute the score of negations.
808+ # If we have more than 6, we print it as a negated type.
809+ result =
810+ Enum . sum_by ( Map . to_list ( descr ) , fn
811+ { :tuple , bdd } -> print_as_negated_bdd ( bdd , @ tuple_top )
812+ { :map , bdd } -> print_as_negated_bdd ( bdd , @ map_top )
813+ { :list , bdd } -> print_as_negated_bdd ( bdd , @ non_empty_list_top )
814+ { :bitmap , bitmap } -> Integer . popcount ( bitmap )
815+ { :atom , { :union , _ } } -> - 100
816+ { :fun , { :union , _ } } -> - 100
817+ { _ , _ } -> 1
818+ end )
819+
820+ result > 8
821+ end
822+
823+ # A bdd leaf can be trivially printed in negated format
824+ # but we don't count it towards the amount of negatives.
825+ defp print_as_negated_bdd ( top , top ) , do: 1
826+ defp print_as_negated_bdd ( bdd_leaf ( _ , _ ) , _top ) , do: 0
827+ defp print_as_negated_bdd ( bdd , top ) , do: if ( negated_bdd? ( bdd , top ) , do: 1 , else: - 100 )
828+
829+ defp negated_bdd? ( { _ , :bdd_bot , :bdd_bot , bdd } , top ) ,
830+ do: bdd in [ :bdd_top , top ] or negated_bdd? ( bdd , top )
831+
832+ defp negated_bdd? ( _ , _ ) , do: false
833+
772834 @ doc """
773835 Converts a descr to its quoted string representation.
774836
@@ -1738,7 +1800,7 @@ defmodule Module.Types.Descr do
17381800 end
17391801
17401802 # Converts the static and dynamic parts of descr to its quoted
1741- # representation. The goal here is to the opposite of fun_descr
1803+ # representation. The goal here is to do the opposite of fun_descr
17421804 # and put static and dynamic parts back together to improve
17431805 # pretty printing.
17441806 defp fun_denormalize ( % { fun: { :union , static_repr } } , % { fun: { :union , dynamic_repr } } , opts ) do
@@ -2173,6 +2235,12 @@ defmodule Module.Types.Descr do
21732235 end
21742236 end
21752237
2238+ defp list_difference ( bdd_leaf ( :term , :term ) , bdd_leaf ( :term , :term ) ) ,
2239+ do: :bdd_bot
2240+
2241+ defp list_difference ( bdd_leaf ( :term , :term ) , bdd2 ) ,
2242+ do: bdd_negation ( bdd2 )
2243+
21762244 # Computes the difference between two BDD (Binary Decision Diagram) list types.
21772245 # It progressively subtracts each type in bdd2 from all types in bdd1.
21782246 # The algorithm handles three cases:
@@ -2183,22 +2251,20 @@ defmodule Module.Types.Descr do
21832251 # 3. Base case: adds bdd2 type to negations of bdd1 type
21842252 # The result may be larger than the initial bdd1, which is maintained in the accumulator.
21852253 defp list_difference ( bdd_leaf ( list1 , last1 ) = bdd1 , bdd_leaf ( list2 , last2 ) = bdd2 ) do
2186- cond do
2187- disjoint? ( list1 , list2 ) or disjoint? ( last1 , last2 ) ->
2188- bdd_leaf ( list1 , last1 )
2189-
2190- subtype? ( list1 , list2 ) ->
2191- if subtype? ( last1 , last2 ) ,
2192- do: :bdd_bot ,
2193- else: bdd_leaf ( list1 , difference ( last1 , last2 ) )
2194-
2195- true ->
2196- bdd_difference ( bdd1 , bdd2 )
2254+ if subtype? ( list1 , list2 ) do
2255+ if subtype? ( last1 , last2 ) ,
2256+ do: :bdd_bot ,
2257+ else: bdd_leaf ( list1 , difference ( last1 , last2 ) )
2258+ else
2259+ bdd_difference ( bdd1 , bdd2 , & list_leaf_disjoint? / 2 )
21972260 end
21982261 end
21992262
22002263 defp list_difference ( bdd1 , bdd2 ) ,
2201- do: bdd_difference ( bdd1 , bdd2 )
2264+ do: bdd_difference ( bdd1 , bdd2 , & list_leaf_disjoint? / 2 )
2265+
2266+ defp list_leaf_disjoint? ( bdd_leaf ( list1 , last1 ) , bdd_leaf ( list2 , last2 ) ) ,
2267+ do: disjoint? ( list1 , list2 ) or disjoint? ( last1 , last2 )
22022268
22032269 defp list_empty? ( @ non_empty_list_top ) , do: false
22042270
0 commit comments