@@ -1795,13 +1795,12 @@ defmodule Module.Types.Descr do
17951795 #
17961796 # A negation only matters when the negated list type is a supertype of the
17971797 # corresponding positive list type; in that case we subtract the negated
1798- # variant from the positive one.
1798+ # variant from the positive one. This is done in list_bdd_to_pos_dnf/1.
17991799 { list_type , last_type } =
18001800 list_bdd_to_pos_dnf ( bdd )
18011801 |> Enum . reduce ( { list_type , last_type_no_list } , fn
1802- { head , tail } , { acc_head , acc_tail } ->
1803- tail = list_tail_unfold ( tail )
1804- { union ( head , acc_head ) , union ( tail , acc_tail ) }
1802+ { list , last , _negs } , { acc_list , acc_last } ->
1803+ { union ( list , acc_list ) , union ( last , acc_last ) }
18051804 end )
18061805
18071806 list_new ( list_type , last_type )
@@ -1829,40 +1828,50 @@ defmodule Module.Types.Descr do
18291828 end
18301829 end
18311830
1832- # Takes all the lines from the root to the leaves finishing with a 1,
1833- # and compile into tuples of positive and negative nodes. Positive nodes are
1834- # those followed by a left path, negative nodes are those followed by a right path.
1835- defp list_bdd_to_dnf ( bdd ) do
1836- bdd_to_dnf ( bdd )
1837- |> Enum . reduce ( [ ] , fn { pos_list , neg_list } , acc ->
1838- case non_empty_list_literals_intersection ( pos_list ) do
1839- :empty ->
1840- acc
1841-
1842- { list , last } ->
1843- if list_line_empty? ( list , last , neg_list ) ,
1844- do: acc ,
1845- else: [ { { list , last } , neg_list } | acc ]
1846- end
1847- end )
1848- end
1849-
18501831 # Takes all the lines from the root to the leaves finishing with a 1,
18511832 # and compile into tuples of positive and negative nodes. Keep only the non-empty positives,
18521833 # and include the impact of negations on the last type.
18531834 # To see if a negation changes the last type or the list type, we just need to check
18541835 # if the negative list type is a supertype of the positive list type. In that case,
18551836 # we can remove the negative last type from the positive one.
1837+
18561838 # (If this subtracted type was empty, the whole type would be empty)
18571839 defp list_bdd_to_pos_dnf ( bdd ) do
18581840 bdd_to_dnf ( bdd )
1859- |> Enum . reduce ( [ ] , fn { pos_list , neg_list } , acc ->
1841+ |> Enum . reduce ( [ ] , fn { pos_list , negs } , acc ->
18601842 case non_empty_list_literals_intersection ( pos_list ) do
18611843 :empty ->
18621844 acc
18631845
18641846 { list , last } ->
1865- if list_line_empty? ( list , last , neg_list ) , do: acc , else: [ { list , last } | acc ]
1847+ if empty? ( list ) or empty? ( last ) do
1848+ acc
1849+ else
1850+ Enum . reduce_while ( negs , { list_tail_unfold ( last ) , [ ] } , fn { neg_type , neg_last } ,
1851+ { acc_last , acc_negs } ->
1852+ if subtype? ( list , neg_type ) do
1853+ difference = difference ( acc_last , neg_last )
1854+ if empty? ( difference ) , do: { :halt , nil } , else: { :cont , { difference , acc_negs } }
1855+ else
1856+ { :cont , { acc_last , [ { neg_type , neg_last } | acc_negs ] } }
1857+ end
1858+ end )
1859+ |> case do
1860+ { :halt , nil } -> acc
1861+ { last , negs } -> [ { list , last , Enum . reverse ( negs ) } | acc ]
1862+ end
1863+ end
1864+ end
1865+ end )
1866+ end
1867+
1868+ # Compute the head of a list (faster because we discard computations on the last type).
1869+ defp list_bdd_to_hd ( bdd ) do
1870+ bdd_to_dnf ( bdd )
1871+ |> Enum . reduce ( none ( ) , fn { pos_list , neg_list } , acc ->
1872+ case non_empty_list_literals_intersection ( pos_list ) do
1873+ :empty -> acc
1874+ { list , last } -> if list_line_empty? ( list , last , neg_list ) , do: acc , else: union ( acc , list )
18661875 end
18671876 end )
18681877 end
@@ -1885,6 +1894,47 @@ defmodule Module.Types.Descr do
18851894 defp list_top? ( bdd_leaf ( :term , :term ) ) , do: true
18861895 defp list_top? ( _ ) , do: false
18871896
1897+ @ doc """
1898+ Checks if a list type is a proper list (terminated by empty list).
1899+ """
1900+ def list_proper? ( :term ) , do: false
1901+
1902+ def list_proper? ( % { } = descr ) do
1903+ case :maps . take ( :dynamic , descr ) do
1904+ :error ->
1905+ list_proper_static? ( descr )
1906+
1907+ { dynamic , static } ->
1908+ list_proper_static? ( static ) and ( list_proper_static? ( dynamic ) or empty? ( dynamic ) )
1909+ end
1910+ end
1911+
1912+ defp list_proper_static? ( :term ) , do: false
1913+
1914+ defp list_proper_static? ( % { } = descr ) do
1915+ # A list is proper if it's either the empty list alone, or all non-empty
1916+ # list types have tails that are subtypes of empty list
1917+ case descr do
1918+ % { bitmap: bitmap , list: bdd } ->
1919+ ( bitmap &&& @ bit_empty_list ) != 0 and empty? ( Map . drop ( descr , [ :list , :bitmap ] ) ) and
1920+ list_bdd_proper? ( bdd )
1921+
1922+ % { bitmap: bitmap } ->
1923+ ( bitmap &&& @ bit_empty_list ) != 0 and empty? ( Map . delete ( descr , :bitmap ) )
1924+
1925+ % { list: bdd } ->
1926+ empty? ( Map . delete ( descr , :list ) ) and list_bdd_proper? ( bdd )
1927+
1928+ % { } ->
1929+ false
1930+ end
1931+ end
1932+
1933+ defp list_bdd_proper? ( bdd ) do
1934+ list_bdd_to_pos_dnf ( bdd )
1935+ |> Enum . all? ( fn { _list , last , _negs } -> subtype? ( last , @ empty_list ) end )
1936+ end
1937+
18881938 defp list_intersection ( bdd_leaf ( list1 , last1 ) , bdd_leaf ( list2 , last2 ) ) do
18891939 try do
18901940 list = non_empty_intersection! ( list1 , list2 )
@@ -1914,10 +1964,16 @@ defmodule Module.Types.Descr do
19141964 # The result may be larger than the initial bdd1, which is maintained in the accumulator.
19151965 defp list_difference ( bdd_leaf ( list1 , last1 ) = bdd1 , bdd_leaf ( list2 , last2 ) = bdd2 ) do
19161966 cond do
1917- disjoint? ( list1 , list2 ) or disjoint? ( last1 , last2 ) -> bdd_leaf ( list1 , last1 )
1918- subtype? ( list1 , list2 ) and subtype? ( last1 , last2 ) -> :bdd_bot
1919- equal? ( list1 , list2 ) -> bdd_leaf ( list1 , difference ( last1 , last2 ) )
1920- true -> bdd_difference ( bdd1 , bdd2 )
1967+ disjoint? ( list1 , list2 ) or disjoint? ( last1 , last2 ) ->
1968+ bdd_leaf ( list1 , last1 )
1969+
1970+ subtype? ( list1 , list2 ) ->
1971+ if subtype? ( last1 , last2 ) ,
1972+ do: :bdd_bot ,
1973+ else: bdd_leaf ( list1 , difference ( last1 , last2 ) )
1974+
1975+ true ->
1976+ bdd_difference ( bdd1 , bdd2 )
19211977 end
19221978 end
19231979
@@ -1989,22 +2045,20 @@ defmodule Module.Types.Descr do
19892045
19902046 defp list_hd_static ( :term ) , do: :term
19912047
1992- defp list_hd_static ( % { list: bdd } ) do
1993- list_bdd_to_pos_dnf ( bdd )
1994- |> Enum . reduce ( none ( ) , fn { list , _ } , acc ->
1995- union ( list , acc )
1996- end )
1997- end
2048+ defp list_hd_static ( % { list: bdd } ) , do: list_bdd_to_hd ( bdd )
19982049
19992050 defp list_hd_static ( % { } ) , do: none ( )
20002051
20012052 @ doc """
2002- Returns the tail of a list.
2053+ Computes the type of the tail of a non-empty list type .
20032054
2004- Errors on a possibly empty list.
2005- On a non empty list of integers, it returns a (possibly empty) list of integers.
2006- On a non empty list of integers, with an atom tail element, it returns either an atom,
2007- or a (possibly empty) list of integers with an atom tail element.
2055+ Returns `{dynamic?, type}` on success, where `dynamic?` indicates whether
2056+ the result contains a dynamic component. Returns `:badnonemptylist` if the
2057+ input type is not guaranteed to be a non-empty list.
2058+
2059+ For a `non_empty_list(t)`, the tail type is `list(t)` (possibly empty).
2060+ For an improper list `non_empty_list(t, s)`, the tail type is
2061+ `list(t, s) or s` (either the rest of the list or the terminator).
20082062 """
20092063 def list_tl ( :term ) , do: :badnonemptylist
20102064
@@ -2042,7 +2096,9 @@ defmodule Module.Types.Descr do
20422096 % { list: bdd }
20432097 end
20442098
2045- list_bdd_to_pos_dnf ( bdd ) |> Enum . reduce ( initial , fn { _ , tail } , acc -> union ( tail , acc ) end )
2099+ list_bdd_to_pos_dnf ( bdd )
2100+ |> Enum . reduce ( none ( ) , fn { _list , last , _negs } , acc -> union ( acc , last ) end )
2101+ |> union ( initial )
20462102 end
20472103
20482104 defp list_tl_static ( % { } ) , do: none ( )
@@ -2053,13 +2109,31 @@ defmodule Module.Types.Descr do
20532109 { unions , list_rendered? } =
20542110 dnf
20552111 |> Enum . reduce ( { [ ] , false } , fn { list_type , last_type , negs } , { acc , list_rendered? } ->
2112+ # <<<<<<< HEAD
2113+ # if subtype?(last_type, @empty_list) do
2114+ # name = if empty?, do: :list, else: :non_empty_list
2115+ # {name, [to_quoted(list_type, opts)], empty?}
2116+ # else
2117+ # args = [to_quoted(list_type, opts), to_quoted(last_type, opts)]
2118+ # {:non_empty_list, args, list_rendered?}
2119+ # =======
20562120 { name , arguments , list_rendered? } =
2057- if subtype? ( last_type , @ empty_list ) do
2058- name = if empty? , do: :list , else: :non_empty_list
2059- { name , [ to_quoted ( list_type , opts ) ] , empty? }
2060- else
2061- args = [ to_quoted ( list_type , opts ) , to_quoted ( last_type , opts ) ]
2062- { :non_empty_list , args , list_rendered? }
2121+ cond do
2122+ # list_type == term() and list_improper_static?(last_type) ->
2123+ # {:improper_list, [], list_rendered?}
2124+
2125+ subtype? ( last_type , @ empty_list ) ->
2126+ name = if empty? , do: :list , else: :non_empty_list
2127+ { name , [ to_quoted ( list_type , opts ) ] , empty? }
2128+
2129+ subtype? ( @ not_non_empty_list , last_type ) ->
2130+ args = [ to_quoted ( list_type , opts ) , { :term , [ ] , [ ] } ]
2131+ { :non_empty_list , args , list_rendered? }
2132+
2133+ true ->
2134+ args = [ to_quoted ( list_type , opts ) , to_quoted ( last_type , opts ) ]
2135+ { :non_empty_list , args , list_rendered? }
2136+ # >>>>>>> 4e5179dd6 (Enhance list type handling and add list_proper? function)
20632137 end
20642138
20652139 acc =
@@ -2096,27 +2170,19 @@ defmodule Module.Types.Descr do
20962170 end
20972171
20982172 # Eliminate empty lists from the union, and redundant types (that are subtypes of others,
2099- # or that can be merged with others).
2173+ # or that can be merged with others). List_bdd_to_pos_dnf already takes into account the
2174+ # impact of negations on the last type.
21002175 defp list_normalize ( bdd ) do
2101- list_bdd_to_dnf ( bdd )
2102- |> Enum . reduce ( [ ] , fn { { list , last } , negs } , acc ->
2103- # First, try to eliminate the negations from the existing type .
2104- { list , last , negs } =
2176+ list_bdd_to_pos_dnf ( bdd )
2177+ |> Enum . reduce ( [ ] , fn { list , last , negs } , acc ->
2178+ # Prune negations from those with empty intersections .
2179+ negs =
21052180 Enum . uniq ( negs )
2106- |> Enum . reduce ( { list , last , [ ] } , fn { nlist , nlast } , { acc_list , acc_last , acc_negs } ->
2107- last = list_tail_unfold ( last )
2108- new_list = intersection ( list , nlist )
2109- new_last = intersection ( last , nlast )
2110-
2111- cond do
2112- # No intersection between the list and the negative
2113- empty? ( new_list ) or empty? ( new_last ) -> { acc_list , acc_last , acc_negs }
2114- subtype? ( list , nlist ) -> { acc_list , difference ( acc_last , nlast ) , acc_negs }
2115- true -> { acc_list , acc_last , [ { nlist , nlast } | acc_negs ] }
2116- end
2181+ |> Enum . filter ( fn { nlist , nlast } ->
2182+ not empty? ( intersection ( list , nlist ) ) and not empty? ( intersection ( last , nlast ) )
21172183 end )
21182184
2119- add_to_list_normalize ( acc , list , last , negs |> Enum . reverse ( ) )
2185+ add_to_list_normalize ( acc , list , last , negs )
21202186 end )
21212187 end
21222188
0 commit comments