From dfb5bb8a470cca53c877c9f80af8e4ff133cad6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 7 Jun 2025 15:06:51 +0200 Subject: [PATCH] Optimize subtype? by aborting early In the same way we introduced non_disjoint_intersection to optimize compatibility, we introduce empty_difference to optimize subtyping. --- lib/elixir/lib/module/types/descr.ex | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/elixir/lib/module/types/descr.ex b/lib/elixir/lib/module/types/descr.ex index 1b4aebb96d7..5c18b28a891 100644 --- a/lib/elixir/lib/module/types/descr.ex +++ b/lib/elixir/lib/module/types/descr.ex @@ -464,6 +464,26 @@ defmodule Module.Types.Descr do defp iterator_difference_static(:none, map), do: map + defp empty_difference_static?(left, :term), do: not Map.has_key?(left, :optional) + + defp empty_difference_static?(left, right) do + iterator_empty_difference_static?(:maps.next(:maps.iterator(unfold(left))), unfold(right)) + end + + defp iterator_empty_difference_static?({key, v1, iterator}, map) do + case map do + %{^key => v2} -> + value = difference(key, v1, v2) + value in @empty_difference or empty_key?(key, value) + + %{} -> + empty_key?(key, v1) + end and + iterator_empty_difference_static?(:maps.next(iterator), map) + end + + defp iterator_empty_difference_static?(:none, _map), do: true + # Returning 0 from the callback is taken as none() for that subtype. defp difference(:atom, v1, v2), do: atom_difference(v1, v2) defp difference(:bitmap, v1, v2), do: v1 - (v1 &&& v2) @@ -491,7 +511,7 @@ defmodule Module.Types.Descr do def empty?(:term), do: false def empty?(%{} = descr) do - case :maps.get(:dynamic, descr, _default = descr) do + case :maps.get(:dynamic, descr, descr) do :term -> false @@ -511,9 +531,11 @@ defmodule Module.Types.Descr do # For atom, bitmap, and optional, if the key is present, # then they are not empty, + defp empty_key?(:fun, value), do: fun_empty?(value) defp empty_key?(:map, value), do: map_empty?(value) defp empty_key?(:list, value), do: list_empty?(value) defp empty_key?(:tuple, value), do: tuple_empty?(value) + defp empty_key?(:dynamic, value), do: empty?(value) defp empty_key?(_, _value), do: false @doc """ @@ -640,7 +662,7 @@ defmodule Module.Types.Descr do end defp subtype_static?(same, same), do: true - defp subtype_static?(left, right), do: empty?(difference_static(left, right)) + defp subtype_static?(left, right), do: empty_difference_static?(left, right) @doc """ Check if a type is equal to another.