diff --git a/lib/elixir/lib/module/types/descr.ex b/lib/elixir/lib/module/types/descr.ex index 00552078cc4..6ae3eed9a28 100644 --- a/lib/elixir/lib/module/types/descr.ex +++ b/lib/elixir/lib/module/types/descr.ex @@ -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: