Skip to content

Proper subtyping btween fun(n) and (none...->term) - #16033

Merged
gldubc merged 1 commit into
elixir-lang:mainfrom
gldubc:fix-function-top-printer-15829
Oct 28, 2026
Merged

gldubc merged 1 commit into
elixir-lang:mainfrom
gldubc:fix-function-top-printer-15829

Conversation

@gldubc

@gldubc gldubc commented Oct 28, 2026

Copy link
Copy Markdown
Member

Fixes #15829

A function line with no positive arrows means "every function of this arity". This clause says such a line is never empty, whatever the negatives are. So when equal? computes fun(1) and not (none -> term), the result has no positives and one negative, gets reported as non-empty, and equal? returns false.

Arrows that are built explicitly don't hit this, because they always have positives. That's why (int -> atom) ≤ (none -> term) is already true, while fun(1) ≤ (none -> term) is false.

Fix

Treat the empty-positives case as one explicit top arrow and reuse the existing algorithm:

defp fun_line_empty?([], [], _seen), do: false

# With no positives, the line is the top function of its arity,
# which is (none(), ..., none() -> term())
defp fun_line_empty?([], [bdd_leaf(args, _) | _] = negatives, seen) do
  top = bdd_leaf_new(Enum.map(args, fn _ -> none() end), term())
  fun_line_empty?([top], negatives, seen)
end

Assisted by: Claude Opus 5.5

@gldubc
gldubc merged commit 5313b1b into elixir-lang:main Oct 28, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Type printer collision between arity-specific function top and maximal explicit arrow

1 participant