Skip to content

Commit e4867f4

Browse files
committed
Short-circuit empty branches in function and list emptiness checks
Similar to #15619, avoid exploring branches whose result is already known. In phi, a branch whose refined return type (ret ∧ return) or refined argument (arg ∖ type) is empty is true, as empty types are never refined back, so we return true without recursing over the remaining positives. In list_line_empty?, subtype_seen?/3 already computes the difference between the last types, so compute it once instead of computing it again and re-checking its emptiness when the subtype check fails. Timings per subtype?/2 call, before and after: | Case | Before | After | |---------------------------------------------|---------|--------| | fun, 5 arrows with none() args (holds) | 114µs | 6.7µs | | fun, 5 arrows with none() args (fails) | 58µs | 4.4µs | | non_empty_list, 4 negatives cover the tail | 1.62µs | 1.49µs | | non_empty_list, partial cover | 1.27µs | 1.13µs | Assisted-by: Claude Code:claude-opus-5-5
1 parent 5313b1b commit e4867f4

1 file changed

Lines changed: 18 additions & 13 deletions

File tree

‎lib/elixir/lib/module/types/descr.ex‎

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,9 +1872,14 @@ defmodule Module.Types.Descr do
18721872
{value, cache}
18731873

18741874
%{} ->
1875-
# Compute result and cache it
1875+
# Compute result and cache it. Empty types are never refined back,
1876+
# so branches that reach one are true without recursing.
1877+
meet = bare_intersection(ret, return)
1878+
18761879
{result1, cache} =
1877-
phi(args, {true, bare_intersection(ret, return)}, rest_positive, cache, seen)
1880+
if empty_seen?(meet, seen),
1881+
do: {true, cache},
1882+
else: phi(args, {true, meet}, rest_positive, cache, seen)
18781883

18791884
if not result1 do
18801885
cache = Map.put(cache, cache_key, false)
@@ -1883,10 +1888,16 @@ defmodule Module.Types.Descr do
18831888
{_index, result, cache} =
18841889
Enum.reduce_while(arguments, {0, true, cache}, fn
18851890
type, {index, acc_result, acc_cache} ->
1891+
{_, arg} = Enum.at(args, index)
1892+
diff = bare_difference(arg, type)
1893+
18861894
{new_result, new_cache} =
1887-
args
1888-
|> List.update_at(index, fn {_, arg} -> {true, bare_difference(arg, type)} end)
1889-
|> phi({b, ret}, rest_positive, acc_cache, seen)
1895+
if empty_seen?(diff, seen) do
1896+
{true, acc_cache}
1897+
else
1898+
args = List.replace_at(args, index, {true, diff})
1899+
phi(args, {b, ret}, rest_positive, acc_cache, seen)
1900+
end
18901901

18911902
if new_result do
18921903
{:cont, {index + 1, acc_result and new_result, new_cache}}
@@ -2474,14 +2485,8 @@ defmodule Module.Types.Descr do
24742485
empty_seen?(list_type, seen) or empty_seen?(last_type, seen) or
24752486
Enum.reduce_while(negs, last_type, fn bdd_leaf(neg_type, neg_last), acc_last_type ->
24762487
if subtype_seen?(list_type, neg_type, seen) do
2477-
neg_last = list_tail_unfold(neg_last)
2478-
2479-
if subtype_seen?(acc_last_type, neg_last, seen) do
2480-
{:halt, nil}
2481-
else
2482-
d = bare_difference(acc_last_type, neg_last)
2483-
if empty_seen?(d, seen), do: {:halt, nil}, else: {:cont, d}
2484-
end
2488+
d = bare_difference(acc_last_type, list_tail_unfold(neg_last))
2489+
if empty_seen?(d, seen), do: {:halt, nil}, else: {:cont, d}
24852490
else
24862491
{:cont, acc_last_type}
24872492
end

0 commit comments

Comments
 (0)