Skip to content

Commit b56ed82

Browse files
committed
Type check the function type on fun.()
1 parent a52133f commit b56ed82

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,13 @@ defmodule Module.Types.Descr do
318318

319319
## Bitmaps
320320

321+
@doc """
322+
Optimized version of `not empty?(intersection(fun(), type))`.
323+
"""
324+
def fun_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_fun) != 0, do: true
325+
def fun_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_fun) != 0, do: true
326+
def fun_type?(%{}), do: false
327+
321328
@doc """
322329
Optimized version of `not empty?(intersection(binary(), type))`.
323330
"""

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

+9-2
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,16 @@ defmodule Module.Types.Expr do
301301

302302
# TODO: fun.(args)
303303
def of_expr({{:., _meta1, [fun]}, _meta2, args}, stack, context) do
304-
with {:ok, _fun_type, context} <- of_expr(fun, stack, context),
305-
{:ok, _arg_types, context} <-
304+
with {:ok, fun_type, context} <- of_expr(fun, stack, context),
305+
{:ok, _args_types, context} <-
306306
map_reduce_ok(args, context, &of_expr(&1, stack, &2)) do
307+
context =
308+
if fun_type?(fun_type) do
309+
context
310+
else
311+
Of.incompatible_warn(fun, fun(), fun_type, stack, context)
312+
end
313+
307314
{:ok, dynamic(), context}
308315
end
309316
end

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

+28
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@ defmodule Module.Types.ExprTest do
2121
assert typecheck!(fn -> :ok end) == fun()
2222
end
2323

24+
describe "funs" do
25+
test "incompatible" do
26+
assert typewarn!([%x{}], x.(1, 2)) ==
27+
{dynamic(),
28+
~l"""
29+
incompatible types in expression:
30+
31+
x
32+
33+
expected type:
34+
35+
fun()
36+
37+
but got type:
38+
39+
dynamic(atom())
40+
41+
where "x" was given the type:
42+
43+
# type: dynamic(atom())
44+
# from: types_test.ex:LINE-2
45+
%x{}
46+
47+
typing violation found at:\
48+
"""}
49+
end
50+
end
51+
2452
describe "remotes" do
2553
test "dynamic calls" do
2654
assert typecheck!([%x{}], x.foo_bar()) == dynamic()

0 commit comments

Comments
 (0)