diff options
author | José Valim <jose.valim@dashbit.co> | 2020-09-29 14:54:03 +0200 |
---|---|---|
committer | José Valim <jose.valim@dashbit.co> | 2020-09-29 15:06:17 +0200 |
commit | be3f3f205300127f6820507b5f53c0ea6607017a (patch) | |
tree | c5ad877b2cadd037cd2c6f5d2490aef35ccfc5df | |
parent | 5d212030bcb7458bb72f338430efdbf2146ac531 (diff) | |
download | elixir-jv-left-union.tar.gz |
Handle var | left_type < right_typejv-left-union
-rw-r--r-- | lib/elixir/lib/module/types/unify.ex | 20 | ||||
-rw-r--r-- | lib/elixir/test/elixir/module/types/pattern_test.exs | 11 | ||||
-rw-r--r-- | lib/elixir/test/elixir/module/types/unify_test.exs | 31 |
3 files changed, 44 insertions, 18 deletions
diff --git a/lib/elixir/lib/module/types/unify.ex b/lib/elixir/lib/module/types/unify.ex index df1774530..9dcd301aa 100644 --- a/lib/elixir/lib/module/types/unify.ex +++ b/lib/elixir/lib/module/types/unify.ex @@ -98,14 +98,22 @@ defmodule Module.Types.Unify do {:ok, target, context} end + defp do_unify({:union, types}, target, stack, context) do + unify_result = + map_reduce_ok(types, context, fn type, context -> + unify(type, target, stack, context) + end) + + case unify_result do + {:ok, types, context} -> {:ok, to_union(types, context), context} + {:error, context} -> {:error, context} + end + end + defp do_unify(source, target, stack, context) do cond do - # This condition exists to handle unions with unbound vars. - # TODO: handle unions properly. Note we can easily unify - # "union < type" even if union has vars as the vars must be - # type - (match?({:union, _}, source) and has_unbound_var?(source, context)) or - (match?({:union, _}, target) and has_unbound_var?(target, context)) -> + # TODO: This condition exists to handle unions with unbound vars. + match?({:union, _}, target) and has_unbound_var?(target, context) -> {:ok, source, context} subtype?(source, target, context) -> diff --git a/lib/elixir/test/elixir/module/types/pattern_test.exs b/lib/elixir/test/elixir/module/types/pattern_test.exs index 458d0645e..8bfdfde39 100644 --- a/lib/elixir/test/elixir/module/types/pattern_test.exs +++ b/lib/elixir/test/elixir/module/types/pattern_test.exs @@ -339,7 +339,7 @@ defmodule Module.Types.PatternTest do assert {:error, {:unable_unify, {:tuple, :atom, _}}} = quoted_head([x], [is_tuple(x) and is_atom(x)]) - assert {:error, {:unable_unify, {{:union, [atom: true, atom: false]}, :tuple, _}}} = + assert {:error, {:unable_unify, {{:atom, true}, :tuple, _}}} = quoted_head([x], [is_tuple(is_atom(x))]) end @@ -354,16 +354,13 @@ defmodule Module.Types.PatternTest do assert {:error, {:unable_unify, {{:atom, :foo}, {:list, :dynamic}, _}}} = quoted_head([x], [length(:foo)]) - assert {:error, - {:unable_unify, {{:union, [atom: true, atom: false]}, {:list, :dynamic}, _}}} = + assert {:error, {:unable_unify, {{:atom, true}, {:list, :dynamic}, _}}} = quoted_head([x], [length(is_tuple(x))]) - assert {:error, {:unable_unify, {{:union, [atom: true, atom: false]}, :tuple, _}}} = + assert {:error, {:unable_unify, {{:atom, true}, :tuple, _}}} = quoted_head([x], [elem(is_tuple(x), 0)]) - assert {:error, - {:unable_unify, - {{:union, [atom: true, atom: false]}, {:union, [:integer, :float]}, _}}} = + assert {:error, {:unable_unify, {{:atom, true}, {:union, [:integer, :float]}, _}}} = quoted_head([x], [elem({}, is_tuple(x))]) assert quoted_head([x], [elem({}, 1)]) == {:ok, [var: 0]} diff --git a/lib/elixir/test/elixir/module/types/unify_test.exs b/lib/elixir/test/elixir/module/types/unify_test.exs index 6ab44c358..eb9a73e6d 100644 --- a/lib/elixir/test/elixir/module/types/unify_test.exs +++ b/lib/elixir/test/elixir/module/types/unify_test.exs @@ -10,10 +10,10 @@ defmodule Module.Types.UnifyTest do |> lift_result() end - defp unify_directed_lift(left, right) do + defp unify_directed_lift(left, right, context \\ new_context()) do stack = %{new_stack() | context: :expr} - unify(left, right, stack, new_context()) + unify(left, right, stack, context) |> lift_result() end @@ -268,9 +268,9 @@ defmodule Module.Types.UnifyTest do {:ok, {:union, [:integer, :atom]}} assert unify_lift({:union, [:atom]}, {:union, [{:atom, :bar}]}) == - {:ok, {:union, [{:atom, :bar}]}} + {:ok, {:atom, :bar}} - assert {:error, {:unable_unify, {{:union, [:integer]}, {:union, [:atom]}, _}}} = + assert {:error, {:unable_unify, {:integer, {:union, [:atom]}, _}}} = unify_lift({:union, [:integer]}, {:union, [:atom]}) end @@ -342,7 +342,28 @@ defmodule Module.Types.UnifyTest do unify_lift({:tuple, 1, [{:var, 0}]}, {:tuple, 1, [{:var, 1}]}, context) end - # TODO: Vars inside unions + # TODO: Vars inside right unions + + test "vars inside left unions" do + assert {{:var, 0}, var_context} = new_var({:foo, [version: 0], nil}, new_context()) + + assert {:ok, {:var, 0}, context} = + unify({:union, [{:var, 0}, :integer]}, :integer, var_context) + + assert Types.lift_type({:var, 0}, context) == :integer + + assert {:ok, {:var, 0}, context} = + unify({:union, [{:var, 0}, :integer]}, {:union, [:integer, :atom]}, var_context) + + assert Types.lift_type({:var, 0}, context) == {:union, [:integer, :atom]} + + assert {:error, {:unable_unify, {:integer, {:union, [:binary, :atom]}, _}}} = + unify_directed_lift( + {:union, [{:var, 0}, :integer]}, + {:union, [:binary, :atom]}, + var_context + ) + end test "recursive type" do assert {{:var, 0}, var_context} = new_var({:foo, [version: 0], nil}, new_context()) |