From 793e2fcd266545d5b1a80c452857f03ea8601e10 Mon Sep 17 00:00:00 2001 From: Guillaume Duboc Date: Mon, 13 Apr 2026 21:01:29 +0200 Subject: [PATCH 1/2] Deduplicate tuple DNF entries --- lib/elixir/lib/module/types/descr.ex | 1 + lib/elixir/test/elixir/module/types/descr_test.exs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/elixir/lib/module/types/descr.ex b/lib/elixir/lib/module/types/descr.ex index bf63ca6d6c7..8e233b89e60 100644 --- a/lib/elixir/lib/module/types/descr.ex +++ b/lib/elixir/lib/module/types/descr.ex @@ -5278,6 +5278,7 @@ defmodule Module.Types.Descr do {tag, elements} -> tuple_eliminate_negations(tag, elements, negs) end end) + |> Enum.uniq() end defp tuple_bdd_to_dnf_with_negations(bdd) do diff --git a/lib/elixir/test/elixir/module/types/descr_test.exs b/lib/elixir/test/elixir/module/types/descr_test.exs index 2779810e30a..a931fda63fc 100644 --- a/lib/elixir/test/elixir/module/types/descr_test.exs +++ b/lib/elixir/test/elixir/module/types/descr_test.exs @@ -1422,6 +1422,16 @@ defmodule Module.Types.DescrTest do refute singleton?(open_tuple([])) refute singleton?(union(tuple([atom([:value])]), tuple([atom([:other_value])]))) refute singleton?(union(tuple([atom([:value])]), closed_map(other: atom([:value])))) + + # Both BDD lines produce the same singleton tuple, so the tuple DNF must not duplicate it. + a = tuple([union(integer(), atom([:ok])), atom([:x])]) + b = tuple([integer(), atom([:x, :y])]) + c = tuple([integer(), union(atom([:x]), binary())]) + + t = union(difference(a, b), difference(a, c)) + # Semantically t ~= {:ok, :x}, confirmed by equal? + assert equal?(t, tuple([atom([:ok]), atom([:x])])) + assert singleton?(t) end end From aae51323b1a4bbd0905e15540e7d44f6b8136e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 13 Apr 2026 21:26:36 +0200 Subject: [PATCH 2/2] Update lib/elixir/lib/module/types/descr.ex --- lib/elixir/lib/module/types/descr.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/elixir/lib/module/types/descr.ex b/lib/elixir/lib/module/types/descr.ex index 8e233b89e60..5150a70d57d 100644 --- a/lib/elixir/lib/module/types/descr.ex +++ b/lib/elixir/lib/module/types/descr.ex @@ -5278,7 +5278,9 @@ defmodule Module.Types.Descr do {tag, elements} -> tuple_eliminate_negations(tag, elements, negs) end end) - |> Enum.uniq() + # We want to avoid each_singleton? from failing, + # so we remove contiguous duplicates (cheaper than uniq) + |> Enum.dedup() end defp tuple_bdd_to_dnf_with_negations(bdd) do