Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 55 additions & 22 deletions lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ defmodule Module.Types.Descr do
# Remark: those are explicit BDD constructors. The functional constructors are `bdd_new/1` and `bdd_new/3`.
@fun_top {:negation, %{}}
@atom_top {:negation, :sets.new(version: 2)}
@map_top {:erlang.phash2([:open | @fields_new]), :open, @fields_new}
# The hash range is also the stride between field-count buckets.
# Since phash2/2 returns less than the range, the count remains the primary sort key.
@map_leaf_hash_range 1 <<< 28
@tuple_leaf_hash_range 1 <<< 28
Comment on lines +53 to +54
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe phash2 is 32bit so maybe we want to use 33?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently the default is to 27, so we are good!

@map_top {:erlang.phash2([:open | @fields_new], @map_leaf_hash_range), :open, @fields_new}
@non_empty_list_top {:erlang.phash2([:term | :term]), :term, :term}
@tuple_top {:erlang.phash2([:open | []]), :open, []}
@map_empty {-:erlang.phash2(@fields_new), :closed, @fields_new}
@tuple_top {:erlang.phash2([:open | []], @tuple_leaf_hash_range), :open, []}
@map_empty {-:erlang.phash2([:closed | @fields_new], @map_leaf_hash_range), :closed,
@fields_new}

defmacrop bdd_leaf(arg1, arg2) do
quote do
Expand Down Expand Up @@ -643,12 +648,14 @@ defmodule Module.Types.Descr do

defp numberize(:map, bdd) do
bdd_map(bdd, fn bdd_leaf(tag, fields) ->
bdd_leaf_new(tag, fields_map(fn _key, value -> numberize(value) end, fields))
map_new(tag, fields_map(fn _key, value -> numberize(value) end, fields))
end)
end

defp numberize(:tuple, bdd) do
bdd_map(bdd, fn bdd_leaf(tag, fields) -> bdd_leaf_new(tag, Enum.map(fields, &numberize/1)) end)
bdd_map(bdd, fn bdd_leaf(tag, elements) ->
tuple_new(tag, Enum.map(elements, &numberize/1))
end)
end

defp numberize(:list, bdd) do
Expand Down Expand Up @@ -2886,7 +2893,20 @@ defmodule Module.Types.Descr do
defguardp is_optional_static(map)
when is_map(map) and is_map_key(map, :optional)

defp map_new(tag, fields), do: bdd_leaf_new(tag, fields)
# Order broader map contexts before more specific ones so shared requirements
# are factored once in the BDD instead of copied under every specialized branch.
defp map_new(:closed, fields) do
{-map_leaf_order(:closed, fields), :closed, fields}
end

defp map_new(tag, fields) do
{map_leaf_order(tag, fields), tag, fields}
end

defp map_leaf_order(tag, fields) do
fields_size(fields) * @map_leaf_hash_range +
:erlang.phash2([tag | fields_keys(fields)], @map_leaf_hash_range)
end

defp map_only?(descr), do: empty?(Map.delete(descr, :map))

Expand All @@ -2905,8 +2925,8 @@ defmodule Module.Types.Descr do

defp map_union(bdd_leaf(tag1, fields1), bdd_leaf(tag2, fields2)) do
case maybe_optimize_map_union(tag1, fields1, tag2, fields2) do
{tag, fields} -> bdd_leaf_new(tag, fields)
nil -> bdd_union(bdd_leaf_new(tag1, fields1), bdd_leaf_new(tag2, fields2))
{tag, fields} -> map_new(tag, fields)
nil -> bdd_union(map_new(tag1, fields1), map_new(tag2, fields2))
end
end

Expand Down Expand Up @@ -3071,7 +3091,7 @@ defmodule Module.Types.Descr do
defp map_leaf_intersection(bdd_leaf(tag1, fields1), bdd_leaf(tag2, fields2)) do
try do
{tag, fields} = map_literal_intersection(tag1, fields1, tag2, fields2)
bdd_leaf_new(tag, fields)
map_new(tag, fields)
catch
:empty -> :bdd_bot
end
Expand Down Expand Up @@ -3138,22 +3158,22 @@ defmodule Module.Types.Descr do
if empty?(v_diff) do
:subtype
else
a_diff = bdd_leaf_new(tag, fields_store(key, v_diff, fields))
a_diff = map_new(tag, fields_store(key, v_diff, fields))

a_type =
case type do
:none ->
:bdd_bot

:union ->
bdd_leaf_new(tag, fields_store(key, union(v1, v2), fields))
map_new(tag, fields_store(key, union(v1, v2), fields))

:intersection ->
v_int = intersection(v1, v2)

if empty?(v_int),
do: :bdd_bot,
else: bdd_leaf_new(tag, fields_store(key, v_int, fields))
else: map_new(tag, fields_store(key, v_int, fields))
end

{:one_key_difference, a_diff, a_type}
Expand Down Expand Up @@ -3891,8 +3911,8 @@ defmodule Module.Types.Descr do
defp map_put_key_static(%{map: bdd} = descr, key, type) do
bdd =
bdd_map(bdd, fn
bdd_leaf(:closed, fields) when type == @not_set -> bdd_leaf_new(:closed, fields)
bdd_leaf(tag, fields) -> bdd_leaf_new(tag, fields_store(key, type, fields))
bdd_leaf(:closed, fields) when type == @not_set -> map_new(:closed, fields)
bdd_leaf(tag, fields) -> map_new(tag, fields_store(key, type, fields))
end)

%{descr | map: bdd}
Expand Down Expand Up @@ -4062,7 +4082,7 @@ defmodule Module.Types.Descr do
defp map_update_put_domains(bdd, domain_keys, type_fun, force?) do
bdd =
bdd_map(bdd, fn bdd_leaf(tag, fields) ->
bdd_leaf_new(map_update_put_domain(tag, domain_keys, type_fun, force?), fields)
map_new(map_update_put_domain(tag, domain_keys, type_fun, force?), fields)
end)

%{map: bdd}
Expand Down Expand Up @@ -5010,7 +5030,20 @@ defmodule Module.Types.Descr do
{acc, dynamic_acc, dynamic?, static_empty?}
end

defp tuple_new(tag, elements), do: bdd_leaf_new(tag, elements)
# Order broader tuple contexts before more specific ones so shared requirements
# are factored once in the BDD instead of copied under every specialized branch.
defp tuple_new(:closed, elements) do
{-tuple_leaf_order(:closed, elements), :closed, elements}
end

defp tuple_new(tag, elements) do
{tuple_leaf_order(tag, elements), tag, elements}
end

defp tuple_leaf_order(tag, elements) do
length(elements) * @tuple_leaf_hash_range +
:erlang.phash2([tag | elements], @tuple_leaf_hash_range)
end

defp tuple_intersection(bdd_leaf(:open, []), bdd), do: bdd
defp tuple_intersection(bdd, bdd_leaf(:open, [])), do: bdd
Expand All @@ -5021,7 +5054,7 @@ defmodule Module.Types.Descr do

defp tuple_leaf_intersection(bdd_leaf(tag1, elements1), bdd_leaf(tag2, elements2)) do
case tuple_literal_intersection(tag1, elements1, tag2, elements2) do
{tag, elements} -> bdd_leaf_new(tag, elements)
{tag, elements} -> tuple_new(tag, elements)
:empty -> :bdd_bot
end
end
Expand Down Expand Up @@ -5291,12 +5324,12 @@ defmodule Module.Types.Descr do
do: leaf

defp tuple_union(
bdd_leaf(tag1, elements1) = tuple1,
bdd_leaf(tag2, elements2) = tuple2
bdd_leaf(tag1, elements1),
bdd_leaf(tag2, elements2)
) do
case maybe_optimize_tuple_union({tag1, elements1}, {tag2, elements2}) do
{tag, elements} -> bdd_leaf_new(tag, elements)
nil -> bdd_union(tuple1, tuple2)
{tag, elements} -> tuple_new(tag, elements)
nil -> bdd_union(tuple_new(tag1, elements1), tuple_new(tag2, elements2))
end
end

Expand Down Expand Up @@ -5920,7 +5953,7 @@ defmodule Module.Types.Descr do
elements
end

bdd_leaf_new(tag, List.insert_at(elements, index, type))
tuple_new(tag, List.insert_at(elements, index, type))
end)
end)
end
Expand Down
Loading