Skip to content

Commit 36888ad

Browse files
committed
Rely on more consistent return types for fun descr
1 parent 0d828d8 commit 36888ad

File tree

2 files changed

+64
-63
lines changed

2 files changed

+64
-63
lines changed

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

+48-47
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,7 @@ defmodule Module.Types.Descr do
973973
For a function type, the domain is the set of valid input types.
974974
975975
Returns:
976-
- `:badfunction` if the type is not a function type
976+
- `:badfun` if the type is not a function type
977977
- A tuple type representing the domain for valid function types
978978
979979
Handles both static and dynamic function types:
@@ -990,7 +990,7 @@ defmodule Module.Types.Descr do
990990
iex> fun_domain(fun([integer(), float()], boolean()))
991991
domain_repr([integer(), float()])
992992
"""
993-
def fun_domain(:term), do: :badfunction
993+
def fun_domain(:term), do: :badfun
994994

995995
def fun_domain(type) do
996996
result =
@@ -1000,7 +1000,7 @@ defmodule Module.Types.Descr do
10001000
with true <- fun_only?(type), {:ok, domain} <- fun_domain_static(type) do
10011001
domain
10021002
else
1003-
_ -> :badfunction
1003+
_ -> :badfun
10041004
end
10051005

10061006
{dynamic, static} when static == @none ->
@@ -1012,34 +1012,33 @@ defmodule Module.Types.Descr do
10121012
{:ok, dynamic_domain} <- fun_domain_static(dynamic) do
10131013
union(dynamic_domain, dynamic(static_domain))
10141014
else
1015-
_ -> :badfunction
1015+
_ -> :badfun
10161016
end
10171017
end
10181018

10191019
case result do
1020-
:badfunction -> :badfunction
1021-
result -> if empty?(result), do: :badfunction, else: result
1020+
:badfun -> :badfun
1021+
result -> if empty?(result), do: :badfun, else: result
10221022
end
10231023
end
10241024

10251025
# Returns {:ok, domain} if the domain of the static type is well-defined.
10261026
# For that, it has to contain a non-empty function type.
1027-
# Otherwise, returns :badfunction.
1027+
# Otherwise, returns :badfun.
10281028
defp fun_domain_static(%{fun: bdd}) do
10291029
case fun_normalize(bdd) do
10301030
{domain, _, _} -> {:ok, domain}
10311031
_ -> {:ok, none()}
10321032
end
10331033
end
10341034

1035-
defp fun_domain_static(:term), do: :badfunction
1035+
defp fun_domain_static(:term), do: :badfun
10361036
defp fun_domain_static(%{}), do: {:ok, none()}
1037-
defp fun_domain_static(:empty_function), do: {:ok, none()}
10381037

10391038
@doc """
10401039
Applies a function type to a list of argument types.
10411040
1042-
Returns the result type if the application is valid, or `:badarguments` if not.
1041+
Returns the result type if the application is valid, or `:badarg` if not.
10431042
10441043
Handles both static and dynamic function types:
10451044
1. For static functions: checks exact argument types
@@ -1061,14 +1060,14 @@ defmodule Module.Types.Descr do
10611060
atom()
10621061
10631062
iex> fun_apply(fun([integer()], atom()), [float()])
1064-
:badarguments
1063+
:badarg
10651064
10661065
iex> fun_apply(fun([dynamic()], atom()), [dynamic()])
10671066
atom()
10681067
"""
10691068
def fun_apply(fun, arguments) do
10701069
if empty?(domain_descr(arguments)) do
1071-
:badarguments
1070+
:badarg
10721071
else
10731072
case :maps.take(:dynamic, fun) do
10741073
:error -> fun_apply_with_strategy(fun, nil, arguments)
@@ -1096,7 +1095,7 @@ defmodule Module.Types.Descr do
10961095
{:ok, res2} <- fun_apply_static(dynamic_fun, dynamic_args) do
10971096
union(res1, dynamic(res2))
10981097
else
1099-
_ -> :badarguments
1098+
_ -> :badarg
11001099
end
11011100
end
11021101
end
@@ -1110,40 +1109,42 @@ defmodule Module.Types.Descr do
11101109
defp fun_apply_static(%{fun: fun_bdd}, arguments) do
11111110
type_args = domain_descr(arguments)
11121111

1113-
if empty?(type_args) do
1114-
# At this stage we do not check that the function can be applied to the arguments (using domain)
1115-
with {_domain, arrows, arity} <- fun_normalize(fun_bdd),
1116-
true <- arity == length(arguments) do
1117-
# Opti: short-circuits when inner loop is none() or outer loop is term()
1118-
result =
1119-
Enum.reduce_while(arrows, none(), fn intersection_of_arrows, acc ->
1120-
Enum.reduce_while(intersection_of_arrows, term(), fn
1121-
{_dom, _ret}, acc when acc == @none -> {:halt, acc}
1122-
{_dom, ret}, acc -> {:cont, intersection(acc, ret)}
1123-
end)
1124-
|> case do
1125-
:term -> {:halt, :term}
1126-
inner -> {:cont, union(inner, acc)}
1127-
end
1128-
end)
1112+
case fun_normalize(fun_bdd) do
1113+
{domain, arrows, arity} when arity == length(arguments) ->
1114+
cond do
1115+
empty?(type_args) ->
1116+
# Opti: short-circuits when inner loop is none() or outer loop is term()
1117+
result =
1118+
Enum.reduce_while(arrows, none(), fn intersection_of_arrows, acc ->
1119+
Enum.reduce_while(intersection_of_arrows, term(), fn
1120+
{_dom, _ret}, acc when acc == @none -> {:halt, acc}
1121+
{_dom, ret}, acc -> {:cont, intersection(acc, ret)}
1122+
end)
1123+
|> case do
1124+
:term -> {:halt, :term}
1125+
inner -> {:cont, union(inner, acc)}
1126+
end
1127+
end)
11291128

1130-
{:ok, result}
1131-
else
1132-
false -> :badarity
1133-
end
1134-
else
1135-
with {domain, arrows, arity} <- fun_normalize(fun_bdd),
1136-
true <- arity == length(arguments),
1137-
true <- subtype?(type_args, domain) do
1138-
result =
1139-
Enum.reduce(arrows, none(), fn intersection_of_arrows, acc ->
1140-
aux_apply(acc, type_args, term(), intersection_of_arrows)
1141-
end)
1129+
{:ok, result}
11421130

1143-
{:ok, result}
1144-
else
1145-
_ -> :badarguments
1146-
end
1131+
subtype?(type_args, domain) ->
1132+
result =
1133+
Enum.reduce(arrows, none(), fn intersection_of_arrows, acc ->
1134+
aux_apply(acc, type_args, term(), intersection_of_arrows)
1135+
end)
1136+
1137+
{:ok, result}
1138+
1139+
true ->
1140+
:badarg
1141+
end
1142+
1143+
{_, _, arity} ->
1144+
{:badarity, arity}
1145+
1146+
:badfun ->
1147+
:badfun
11471148
end
11481149
end
11491150

@@ -1218,7 +1219,7 @@ defmodule Module.Types.Descr do
12181219
## Return Values
12191220
#
12201221
# - `{domain, arrows, arity}` for valid function BDDs
1221-
# - `:empty_function` if the BDD represents an empty function type
1222+
# - `:badfun` if the BDD represents an empty function type
12221223
#
12231224
# ## Internal Use
12241225
#
@@ -1245,7 +1246,7 @@ defmodule Module.Types.Descr do
12451246
end
12461247
end)
12471248

1248-
if arrows == [], do: :empty_function, else: {domain, arrows, arity}
1249+
if arrows == [], do: :badfun, else: {domain, arrows, arity}
12491250
end
12501251

12511252
# Checks if a function type is empty.

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

+16-16
Original file line numberDiff line numberDiff line change
@@ -764,19 +764,19 @@ defmodule Module.Types.DescrTest do
764764
# For function domain:
765765
# 1. The domain of an intersection of functions is the union of the domains of the functions
766766
# 2. The domain of a union of functions is the intersection of the domains of the functions
767-
# 3. If a type is not a function or its domain is empty, return :badfunction
767+
# 3. If a type is not a function or its domain is empty, return :badfun
768768

769769
# For gradual domain of a function type t:
770770
# It is dom(t) = dom(up(t)) ∪ dynamic(dom(down(t)))
771771
# where dom is the static domain, up is the upcast, and down is the downcast.
772772

773773
## Basic domain tests
774-
assert fun_domain(term()) == :badfunction
775-
assert fun_domain(none()) == :badfunction
776-
assert fun_domain(intersection(fun(1), fun(2))) == :badfunction
777-
assert union(atom(), intersection(fun(1), fun(2))) |> fun_domain() == :badfunction
778-
assert fun_domain(fun([none()], term())) == :badfunction
779-
assert fun_domain(difference(fun([pid()], pid()), fun([pid()], term()))) == :badfunction
774+
assert fun_domain(term()) == :badfun
775+
assert fun_domain(none()) == :badfun
776+
assert fun_domain(intersection(fun(1), fun(2))) == :badfun
777+
assert union(atom(), intersection(fun(1), fun(2))) |> fun_domain() == :badfun
778+
assert fun_domain(fun([none()], term())) == :badfun
779+
assert fun_domain(difference(fun([pid()], pid()), fun([pid()], term()))) == :badfun
780780

781781
assert_domain(fun([], term()), [])
782782
assert_domain(fun([term()], atom()), [term()])
@@ -787,8 +787,8 @@ defmodule Module.Types.DescrTest do
787787
assert_domain(union(fun([number()], term()), fun([float()], term())), [float()])
788788

789789
## Gradual domain tests
790-
assert fun_domain(dynamic()) == :badfunction
791-
assert fun_domain(intersection(dynamic(), fun([none()], term()))) == :badfunction
790+
assert fun_domain(dynamic()) == :badfun
791+
assert fun_domain(intersection(dynamic(), fun([none()], term()))) == :badfun
792792
assert_domain(fun([dynamic()], dynamic()), [dynamic()])
793793
assert_domain(fun([dynamic(), dynamic()], dynamic()), [dynamic(), dynamic()])
794794
assert_domain(intersection(fun([integer()], atom()), dynamic()), [integer()])
@@ -817,7 +817,7 @@ defmodule Module.Types.DescrTest do
817817

818818
# Intersection of domains int and float is empty
819819
assert union(fun([integer()], atom()), fun([float()], boolean())) |> fun_domain() ==
820-
:badfunction
820+
:badfun
821821
end
822822

823823
test "function application" do
@@ -826,14 +826,14 @@ defmodule Module.Types.DescrTest do
826826

827827
# Basic function application scenarios
828828
assert fun_apply(fun([integer()], atom()), [integer()]) == atom()
829-
assert fun_apply(fun([integer()], atom()), [float()]) == :badarguments
830-
assert fun_apply(fun([integer()], atom()), [term()]) == :badarguments
829+
assert fun_apply(fun([integer()], atom()), [float()]) == :badarg
830+
assert fun_apply(fun([integer()], atom()), [term()]) == :badarg
831831
assert fun_apply(fun([integer()], none()), [integer()]) == none()
832832
assert fun_apply(fun([integer()], term()), [integer()]) == term()
833833

834834
# Arity mismatches
835-
assert fun_apply(fun([dynamic()], integer()), [dynamic(), dynamic()]) == :badarguments
836-
assert fun_apply(fun([integer(), atom()], boolean()), [integer()]) == :badarguments
835+
assert fun_apply(fun([dynamic()], integer()), [dynamic(), dynamic()]) == :badarg
836+
assert fun_apply(fun([integer(), atom()], boolean()), [integer()]) == {:badarity, 2}
837837

838838
# Dynamic type handling
839839
assert fun_apply(fun([dynamic()], term()), [dynamic()]) == term()
@@ -866,9 +866,9 @@ defmodule Module.Types.DescrTest do
866866

867867
fun9 = fun([intersection(dynamic(), integer())], atom())
868868
assert fun_apply(fun9, [dynamic(integer())]) |> equal?(atom())
869-
assert fun_apply(fun9, [dynamic()]) == :badarguments
869+
assert fun_apply(fun9, [dynamic()]) == :badarg
870870
# TODO: discuss this case
871-
assert fun_apply(fun9, [integer()]) == :badarguments
871+
assert fun_apply(fun9, [integer()]) == :badarg
872872

873873
# Dynamic with function type combinations
874874
fun12 =

0 commit comments

Comments
 (0)