Skip to content

Commit f405d31

Browse files
committed
Return domain in badarg
1 parent 981ef72 commit f405d31

File tree

2 files changed

+34
-21
lines changed

2 files changed

+34
-21
lines changed

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ defmodule Module.Types.Descr do
144144
def args_to_domain(types) when is_list(types), do: tuple(types)
145145

146146
@doc """
147-
Converts the domain to arguments.
147+
Converts the domain to a list of arguments.
148148
149149
The domain is expected to be closed tuples. They may have complex negations
150150
which are then simplified to a union of positive tuple literals only.
@@ -156,27 +156,39 @@ defmodule Module.Types.Descr do
156156
157157
Internally it uses `tuple_reduce/4` with concatenation as the join function
158158
and a transform that is simply the identity.
159+
160+
The list of arguments can be flattened into a broad domain by calling:
161+
162+
|> Enum.zip_with(fn types -> Enum.reduce(types, &union/2) end)
159163
"""
160164
def domain_to_args(descr) do
161165
case :maps.take(:dynamic, descr) do
162166
:error ->
163-
{tuple_elim_negations_static(descr), []}
167+
tuple_elim_negations_static(descr, &Function.identity/1)
164168

165169
{dynamic, static} ->
166-
{tuple_elim_negations_static(static), tuple_elim_negations_static(dynamic)}
170+
tuple_elim_negations_static(static, &Function.identity/1) ++
171+
tuple_elim_negations_static(dynamic, fn elems -> Enum.map(elems, &dynamic/1) end)
167172
end
168173
end
169174

170175
# Call tuple_reduce to build the simple union of tuples that come from each map literal.
171176
# Thus, initial is `[]`, join is concatenation, and the transform of a map literal
172177
# with no negations is just to keep the map literal as is.
173-
defp tuple_elim_negations_static(%{tuple: dnf} = descr) when map_size(descr) == 1 do
178+
defp tuple_elim_negations_static(%{tuple: dnf} = descr, transform) when map_size(descr) == 1 do
174179
tuple_reduce(dnf, [], &Kernel.++/2, fn :closed, elements ->
175-
[elements]
180+
[transform.(elements)]
176181
end)
177182
end
178183

179-
defp tuple_elim_negations_static(descr) when descr == %{}, do: []
184+
defp tuple_elim_negations_static(descr, _transform) when descr == %{}, do: []
185+
186+
defp domain_to_flat_args(domain, arity) do
187+
case domain_to_args(domain) do
188+
[] -> List.duplicate(none(), arity)
189+
args -> Enum.zip_with(args, fn types -> Enum.reduce(types, &union/2) end)
190+
end
191+
end
180192

181193
## Optional
182194

@@ -987,7 +999,12 @@ defmodule Module.Types.Descr do
987999
Applies a function type to a list of argument types.
9881000
9891001
Returns `{:ok, result}` if the application is valid
990-
or one `:badarg`, `:badfun`, `{:badarity, arities}` if not.
1002+
or one `{:badarg, to_succeed_domain}`, `:badfun`,
1003+
`{:badarity, arities}` if not.
1004+
1005+
Note the domain returned by `:badarg` is not the strong
1006+
domain, but the domain that must be satisfied for the
1007+
function application to succeed.
9911008
9921009
Handles both static and dynamic function types:
9931010
@@ -1059,11 +1076,11 @@ defmodule Module.Types.Descr do
10591076
fun_normalize_both(fun_static, fun_dynamic, arity) do
10601077
cond do
10611078
empty?(args_domain) ->
1062-
{:badarg, domain}
1079+
{:badarg, domain_to_flat_args(domain, arity)}
10631080

10641081
not subtype?(args_domain, domain) ->
10651082
if static? or not compatible?(fun, fun(arguments, term())) do
1066-
{:badarg, domain}
1083+
{:badarg, domain_to_flat_args(domain, arity)}
10671084
else
10681085
{:ok, dynamic()}
10691086
end

lib/elixir/test/elixir/module/types/descr_test.exs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -761,13 +761,13 @@ defmodule Module.Types.DescrTest do
761761

762762
test "static" do
763763
# Full static
764-
assert fun_apply(fun(), [integer()]) == :badarg
765-
assert fun_apply(difference(fun(), none_fun(2)), [integer()]) == :badarg
764+
assert fun_apply(fun(), [integer()]) == {:badarg, [none()]}
765+
assert fun_apply(difference(fun(), none_fun(2)), [integer()]) == {:badarg, [none()]}
766766

767767
# Basic function application scenarios
768768
assert fun_apply(fun([integer()], atom()), [integer()]) == {:ok, atom()}
769-
assert fun_apply(fun([integer()], atom()), [float()]) == :badarg
770-
assert fun_apply(fun([integer()], atom()), [term()]) == :badarg
769+
assert fun_apply(fun([integer()], atom()), [float()]) == {:badarg, [integer()]}
770+
assert fun_apply(fun([integer()], atom()), [term()]) == {:badarg, [integer()]}
771771
assert fun_apply(fun([integer()], none()), [integer()]) == {:ok, none()}
772772
assert fun_apply(fun([integer()], term()), [integer()]) == {:ok, term()}
773773

@@ -826,7 +826,7 @@ defmodule Module.Types.DescrTest do
826826
assert fun_apply(fun, [dynamic(integer())]) |> elem(1) |> equal?(atom())
827827
assert fun_apply(fun, [dynamic(number())]) == {:ok, dynamic()}
828828
assert fun_apply(fun, [integer()]) == {:ok, dynamic()}
829-
assert fun_apply(fun, [float()]) == :badarg
829+
assert fun_apply(fun, [float()]) == {:badarg, [dynamic(integer())]}
830830
end
831831

832832
defp dynamic_fun(args, return), do: dynamic(fun(args, return))
@@ -926,7 +926,7 @@ defmodule Module.Types.DescrTest do
926926
)
927927

928928
assert fun_args |> fun_apply([atom()]) == {:ok, dynamic()}
929-
assert fun_args |> fun_apply([integer()]) == :badarg
929+
assert fun_args |> fun_apply([integer()]) == {:badarg, [dynamic(atom())]}
930930

931931
# Badfun
932932
assert union(
@@ -1254,14 +1254,10 @@ defmodule Module.Types.DescrTest do
12541254
)
12551255
]
12561256

1257-
multi_args_to_domain = fn args ->
1258-
Enum.reduce(args, none(), &union(args_to_domain(&1), &2))
1259-
end
1260-
12611257
Enum.each(complex_tuples, fn domain ->
1262-
{static, dynamic} = domain_to_args(domain)
1258+
args = domain_to_args(domain)
12631259

1264-
assert union(multi_args_to_domain.(static), dynamic(multi_args_to_domain.(dynamic)))
1260+
assert Enum.reduce(args, none(), &union(args_to_domain(&1), &2))
12651261
|> equal?(domain)
12661262
end)
12671263
end

0 commit comments

Comments
 (0)