Skip to content

Commit 65bfdb6

Browse files
committed
Add fun from inferred clauses
1 parent e27cd15 commit 65bfdb6

File tree

6 files changed

+80
-47
lines changed

6 files changed

+80
-47
lines changed

lib/elixir/lib/module/types/apply.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ defmodule Module.Types.Apply do
487487
{union(type, fun_from_non_overlapping_clauses(clauses)), fallback?, context}
488488

489489
{{:infer, _, clauses}, context} when length(clauses) <= @max_clauses ->
490-
{union(type, fun_from_overlapping_clauses(clauses)), fallback?, context}
490+
{union(type, fun_from_inferred_clauses(clauses)), fallback?, context}
491491

492492
{_, context} ->
493493
{type, true, context}
@@ -705,7 +705,7 @@ defmodule Module.Types.Apply do
705705
result =
706706
case info do
707707
{:infer, _, clauses} when length(clauses) <= @max_clauses ->
708-
fun_from_overlapping_clauses(clauses)
708+
fun_from_inferred_clauses(clauses)
709709

710710
_ ->
711711
dynamic(fun(arity))

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,18 +137,20 @@ defmodule Module.Types.Descr do
137137
@doc """
138138
Creates a function from overlapping function clauses.
139139
"""
140-
def fun_from_overlapping_clauses(args_clauses) do
140+
def fun_from_inferred_clauses(args_clauses) do
141141
domain_clauses =
142142
Enum.reduce(args_clauses, [], fn {args, return}, acc ->
143-
pivot_overlapping_clause(args_to_domain(args), return, acc)
143+
domain = args |> Enum.map(&upper_bound/1) |> args_to_domain()
144+
pivot_overlapping_clause(domain, upper_bound(return), acc)
144145
end)
145146

146147
funs =
147148
for {domain, return} <- domain_clauses,
148149
args <- domain_to_args(domain),
149-
do: fun(args, return)
150+
do: fun(args, dynamic(return))
150151

151152
Enum.reduce(funs, &intersection/2)
153+
# dynamic(fun())
152154
end
153155

154156
defp pivot_overlapping_clause(domain, return, [{acc_domain, acc_return} | acc]) do
@@ -203,10 +205,10 @@ defmodule Module.Types.Descr do
203205
def domain_to_args(descr) do
204206
case :maps.take(:dynamic, descr) do
205207
:error ->
206-
tuple_elim_negations_static(descr, &Function.identity/1)
208+
tuple_elim_negations_static(descr, & &1)
207209

208210
{dynamic, static} ->
209-
tuple_elim_negations_static(static, &Function.identity/1) ++
211+
tuple_elim_negations_static(static, & &1) ++
210212
tuple_elim_negations_static(dynamic, fn elems -> Enum.map(elems, &dynamic/1) end)
211213
end
212214
end
@@ -2124,9 +2126,6 @@ defmodule Module.Types.Descr do
21242126

21252127
defp dynamic_to_quoted(descr, opts) do
21262128
cond do
2127-
descr == %{} ->
2128-
[]
2129-
21302129
# We check for :term literally instead of using term_type?
21312130
# because we check for term_type? in to_quoted before we
21322131
# compute the difference(dynamic, static).
@@ -2136,6 +2135,9 @@ defmodule Module.Types.Descr do
21362135
single = indivisible_bitmap(descr, opts) ->
21372136
[single]
21382137

2138+
empty?(descr) ->
2139+
[]
2140+
21392141
true ->
21402142
case non_term_type_to_quoted(descr, opts) do
21412143
{:none, _meta, []} = none -> [none]

lib/elixir/lib/module/types/expr.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ defmodule Module.Types.Expr do
340340
add_inferred(acc, args, body)
341341
end)
342342

343-
{fun_from_overlapping_clauses(acc), context}
343+
{fun_from_inferred_clauses(acc), context}
344344
end
345345
end
346346

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

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -767,54 +767,72 @@ defmodule Module.Types.DescrTest do
767767
intersection(fun([integer()], atom()), fun([float()], binary()))
768768
end
769769

770-
test "fun_from_overlapping_clauses" do
770+
test "fun_from_inferred_clauses" do
771771
# No overlap
772-
assert fun_from_overlapping_clauses([{[integer()], atom()}, {[float()], binary()}])
772+
assert fun_from_inferred_clauses([{[integer()], atom()}, {[float()], binary()}])
773773
|> equal?(
774-
fun_from_non_overlapping_clauses([{[integer()], atom()}, {[float()], binary()}])
774+
intersection(
775+
fun_from_non_overlapping_clauses([{[integer()], atom()}, {[float()], binary()}]),
776+
fun([number()], dynamic())
777+
)
775778
)
776779

777780
# Subsets
778-
assert fun_from_overlapping_clauses([{[integer()], atom()}, {[number()], binary()}])
781+
assert fun_from_inferred_clauses([{[integer()], atom()}, {[number()], binary()}])
779782
|> equal?(
780-
fun_from_non_overlapping_clauses([
781-
{[integer()], union(atom(), binary())},
782-
{[float()], binary()}
783-
])
783+
intersection(
784+
fun_from_non_overlapping_clauses([
785+
{[integer()], union(atom(), binary())},
786+
{[float()], binary()}
787+
]),
788+
fun([number()], dynamic())
789+
)
784790
)
785791

786-
assert fun_from_overlapping_clauses([{[number()], binary()}, {[integer()], atom()}])
792+
assert fun_from_inferred_clauses([{[number()], binary()}, {[integer()], atom()}])
787793
|> equal?(
788-
fun_from_non_overlapping_clauses([
789-
{[integer()], union(atom(), binary())},
790-
{[float()], binary()}
791-
])
794+
intersection(
795+
fun_from_non_overlapping_clauses([
796+
{[integer()], union(atom(), binary())},
797+
{[float()], binary()}
798+
]),
799+
fun([number()], dynamic())
800+
)
792801
)
793802

794803
# Partial
795-
assert fun_from_overlapping_clauses([
804+
assert fun_from_inferred_clauses([
796805
{[union(integer(), pid())], atom()},
797806
{[union(float(), pid())], binary()}
798807
])
799808
|> equal?(
800-
fun_from_non_overlapping_clauses([
801-
{[integer()], atom()},
802-
{[float()], binary()},
803-
{[pid()], union(atom(), binary())}
804-
])
809+
intersection(
810+
fun_from_non_overlapping_clauses([
811+
{[integer()], atom()},
812+
{[float()], binary()},
813+
{[pid()], union(atom(), binary())}
814+
]),
815+
fun([union(number(), pid())], dynamic())
816+
)
805817
)
806818

807819
# Difference
808-
assert fun_from_overlapping_clauses([
820+
assert fun_from_inferred_clauses([
809821
{[integer(), union(pid(), atom())], atom()},
810822
{[number(), pid()], binary()}
811823
])
812824
|> equal?(
813-
fun_from_non_overlapping_clauses([
814-
{[float(), pid()], binary()},
815-
{[integer(), atom()], atom()},
816-
{[integer(), pid()], union(atom(), binary())}
817-
])
825+
intersection(
826+
fun_from_non_overlapping_clauses([
827+
{[float(), pid()], binary()},
828+
{[integer(), atom()], atom()},
829+
{[integer(), pid()], union(atom(), binary())}
830+
]),
831+
fun_from_non_overlapping_clauses([
832+
{[integer(), union(pid(), atom())], dynamic()},
833+
{[number(), pid()], dynamic()}
834+
])
835+
)
818836
)
819837
end
820838
end

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

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,24 +138,37 @@ defmodule Module.Types.ExprTest do
138138
end
139139

140140
test "infers functions" do
141-
assert typecheck!(& &1) == fun([dynamic()], dynamic())
142-
assert typecheck!(fn -> :ok end) == fun([], atom([:ok]))
141+
assert typecheck!(& &1) |> equal?(fun([term()], dynamic()))
142+
143+
assert typecheck!(fn -> :ok end) |> equal?(fun([], dynamic(atom([:ok]))))
143144

144145
assert typecheck!(fn
145146
<<"ok">>, {} -> :ok
146147
<<"error">>, {} -> :error
147148
[_ | _], %{} -> :list
148-
end) ==
149+
end)
150+
|> equal?(
149151
intersection(
150152
fun(
151-
[dynamic(non_empty_list(term(), term())), dynamic(open_map())],
152-
atom([:list])
153+
[non_empty_list(term(), term()), open_map()],
154+
dynamic(atom([:list]))
153155
),
154156
fun(
155-
[dynamic(binary()), dynamic(tuple([]))],
156-
atom([:ok, :error])
157+
[binary(), tuple([])],
158+
dynamic(atom([:ok, :error]))
157159
)
158160
)
161+
)
162+
end
163+
164+
test "application" do
165+
assert typecheck!(
166+
[map],
167+
(fn
168+
%{a: a} = data -> %{data | b: a}
169+
data -> data
170+
end).(map)
171+
) == dynamic()
159172
end
160173

161174
test "bad function" do
@@ -253,7 +266,7 @@ defmodule Module.Types.ExprTest do
253266
254267
but function has type:
255268
256-
(dynamic(map()) -> :map)
269+
(map() -> dynamic(:map))
257270
"""
258271
end
259272

@@ -265,7 +278,7 @@ defmodule Module.Types.ExprTest do
265278
266279
because the right-hand side has type:
267280
268-
(dynamic() -> dynamic({:ok, term()}))
281+
(term() -> dynamic({:ok, term()}))
269282
"""
270283
end
271284
end

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ defmodule Module.Types.IntegrationTest do
180180
assert return.(:captured, 0)
181181
|> equal?(
182182
fun_from_non_overlapping_clauses([
183-
{[dynamic(binary())], atom([:ok, :error])},
184-
{[dynamic(non_empty_list(term(), term()))], atom([:list])}
183+
{[binary()], dynamic(atom([:ok, :error]))},
184+
{[non_empty_list(term(), term())], dynamic(atom([:list]))}
185185
])
186186
)
187187
end

0 commit comments

Comments
 (0)