@@ -1879,45 +1879,113 @@ defmodule Module.Types.Descr do
18791879 defp list_top? ( _ ) , do: false
18801880
18811881 @ doc """
1882- Checks if a list type is a proper list (terminated by empty list).
1882+ Returns the element type of a list, assuming the list is proper.
1883+
1884+ It returns a two-element tuple. The first element dictates the
1885+ empty list type. The second element returns the value type.
1886+
1887+ {:static or :dynamic or nil, t() or nil}
1888+
1889+ If the value is `nil`, it means that component is missing.
1890+ However, both cannot be `nil` together. In such cases,
1891+ we return `:badproperlist`.
18831892 """
1884- def list_proper? ( :term ) , do: false
1893+ def list_of ( :term ) , do: :badproperlist
18851894
1886- def list_proper? ( descr ) do
1887- { dynamic , static } = Map . pop ( descr , :dynamic , descr )
1895+ def list_of ( descr ) do
1896+ case :maps . take ( :dynamic , descr ) do
1897+ :error ->
1898+ with { empty_list , value } <- list_of_static ( descr ) do
1899+ if empty? ( value ) and empty_list == nil do
1900+ :badproperlist
1901+ else
1902+ { empty_list , value }
1903+ end
1904+ end
18881905
1889- # A list is proper if it's either the empty list alone, or all non-empty
1890- # list types have tails that are subtypes of empty list.
1891- case static do
1892- % { list: bdd } ->
1893- Map . get ( static , :bitmap , @ bit_empty_list ) == @ bit_empty_list and
1894- empty? ( Map . drop ( static , [ :list , :bitmap ] ) ) and
1895- list_bdd_to_pos_dnf ( bdd )
1896- |> Enum . all? ( fn { _list , last , _negs } -> subtype? ( last , @ empty_list ) end )
1906+ { dynamic , static } ->
1907+ with { empty_list , static_value } <- list_of_static ( static ) do
1908+ empty_list =
1909+ case dynamic do
1910+ % { bitmap: bitmap } when ( bitmap &&& @ bit_empty_list ) != 0 -> :dynamic
1911+ _ -> empty_list
1912+ end
18971913
1898- _ when static == % { bitmap: @ bit_empty_list } ->
1899- true
1914+ dynamic_value =
1915+ case dynamic do
1916+ :term ->
1917+ dynamic ( )
1918+
1919+ % { list: bdd } ->
1920+ Enum . reduce ( list_bdd_to_pos_dnf ( bdd ) , none ( ) , fn { list , last , _negs } , acc ->
1921+ if last == @ empty_list or subtype? ( last , @ empty_list ) do
1922+ union ( acc , list )
1923+ else
1924+ acc
1925+ end
1926+ end )
1927+
1928+ % { } ->
1929+ none ( )
1930+ end
19001931
1901- % { } ->
1902- # Dynamic requires only the empty list or a single proper list
1903- empty? ( static ) and
1904- case dynamic do
1905- :term ->
1906- true
1932+ if empty? ( dynamic_value ) do
1933+ # list_bdd_to_pos_dnf guarantees the lists actually exists,
1934+ # so we can match on none() rather than empty.
1935+ if empty_list == nil do
1936+ :badproperlist
1937+ else
1938+ { empty_list , nil }
1939+ end
1940+ else
1941+ { empty_list , union ( static_value , dynamic ( dynamic_value ) ) }
1942+ end
1943+ end
1944+ end
1945+ end
19071946
1908- % { bitmap: bitmap } when ( bitmap &&& @ bit_empty_list ) != 0 ->
1909- true
1947+ defp list_of_static ( descr ) do
1948+ case descr do
1949+ % { bitmap: @ bit_empty_list } ->
1950+ case empty? ( Map . drop ( descr , [ :bitmap , :list ] ) ) do
1951+ true -> list_of_static ( descr , :static )
1952+ false -> :badproperlist
1953+ end
19101954
1911- % { list: bdd } ->
1912- list_bdd_to_pos_dnf ( bdd )
1913- |> Enum . any? ( fn { _list , last , _negs } -> subtype? ( last , @ empty_list ) end )
1955+ % { bitmap: _ } ->
1956+ :badproperlist
19141957
1915- % { } ->
1916- false
1958+ % { } ->
1959+ case empty? ( Map . delete ( descr , :list ) ) do
1960+ true -> list_of_static ( descr , nil )
1961+ false -> :badproperlist
1962+ end
1963+ end
1964+ end
1965+
1966+ # A list is proper if it's either the empty list alone, or all non-empty
1967+ # list types have tails that are subtypes of empty list.
1968+ defp list_of_static ( % { list: bdd } , empty_list ) do
1969+ try do
1970+ result =
1971+ Enum . reduce ( list_bdd_to_pos_dnf ( bdd ) , none ( ) , fn { list , last , _negs } , acc ->
1972+ if last == @ empty_list or subtype? ( last , @ empty_list ) do
1973+ union ( acc , list )
1974+ else
1975+ throw ( :badproperlist )
19171976 end
1977+ end )
1978+
1979+ { empty_list , result }
1980+ catch
1981+ :badproperlist -> :badproperlist
19181982 end
19191983 end
19201984
1985+ defp list_of_static ( % { } , empty_list ) do
1986+ { empty_list , none ( ) }
1987+ end
1988+
19211989 defp list_intersection ( bdd_leaf ( list1 , last1 ) , bdd_leaf ( list2 , last2 ) ) do
19221990 try do
19231991 list = non_empty_intersection! ( list1 , list2 )
0 commit comments