Optimize tuple and map emptiness checks (#15619)
Short-circuit disjoint and subtype tuple elements before exploring the general difference branches. Assisted-by: Codex:GPT-5.6
This commit is contained in:
@@ -4432,13 +4432,22 @@ defmodule Module.Types.Descr do
|
||||
end
|
||||
|
||||
defp map_line_meet_empty?(key, type, neg_type, t1, t2, tag, neg_tag, acc_meet, negs, seen) do
|
||||
diff = bare_difference(type, neg_type)
|
||||
meet = bare_intersection(type, neg_type)
|
||||
|
||||
(empty_seen?(diff, seen) or
|
||||
map_line_empty?(tag, Enum.reverse(acc_meet, [{key, diff} | t1]), negs, seen)) and
|
||||
(empty_seen?(meet, seen) or
|
||||
map_line_meet_empty?(t1, t2, tag, neg_tag, [{key, meet} | acc_meet], negs, seen))
|
||||
if empty_seen?(meet, seen) do
|
||||
# This negative map is disjoint from the current line at this field.
|
||||
map_line_empty?(tag, Enum.reverse(acc_meet, [{key, type} | t1]), negs, seen)
|
||||
else
|
||||
diff = bare_difference(type, neg_type)
|
||||
|
||||
if empty_seen?(diff, seen) do
|
||||
# The field is a subtype of the negative field, so their intersection is type.
|
||||
map_line_meet_empty?(t1, t2, tag, neg_tag, [{key, type} | acc_meet], negs, seen)
|
||||
else
|
||||
map_line_empty?(tag, Enum.reverse(acc_meet, [{key, diff} | t1]), negs, seen) and
|
||||
map_line_meet_empty?(t1, t2, tag, neg_tag, [{key, meet} | acc_meet], negs, seen)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp map_line_fields_empty?(
|
||||
@@ -5001,15 +5010,22 @@ defmodule Module.Types.Descr do
|
||||
# Handles the case where {tag, elements} is an open tuple, like {:open, []}
|
||||
{ty, elements} = List.pop_at(elements, 0, term())
|
||||
|
||||
# In this case, there is no intersection between the positive and this negative.
|
||||
# So we should just "go next"
|
||||
diff = bare_difference(ty, neg_type)
|
||||
meet = bare_intersection(ty, neg_type)
|
||||
|
||||
(empty_seen?(diff, seen) or
|
||||
tuple_line_empty?(tag, Enum.reverse(acc_meet, [diff | elements]), negs, seen)) and
|
||||
(empty_seen?(meet, seen) or
|
||||
tuple_elements_empty?([meet | acc_meet], tag, elements, neg_elements, negs, seen))
|
||||
if empty_seen?(meet, seen) do
|
||||
# This negative tuple is disjoint from the current line at this element.
|
||||
tuple_line_empty?(tag, Enum.reverse(acc_meet, [ty | elements]), negs, seen)
|
||||
else
|
||||
diff = bare_difference(ty, neg_type)
|
||||
|
||||
if empty_seen?(diff, seen) do
|
||||
# The element is a subtype of the negative element, so their intersection is ty.
|
||||
tuple_elements_empty?([ty | acc_meet], tag, elements, neg_elements, negs, seen)
|
||||
else
|
||||
tuple_line_empty?(tag, Enum.reverse(acc_meet, [diff | elements]), negs, seen) and
|
||||
tuple_elements_empty?([meet | acc_meet], tag, elements, neg_elements, negs, seen)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Determines if the set difference is empty when:
|
||||
|
||||
Reference in New Issue
Block a user