Skip to content

Commit 771cfe6

Browse files
committed
Typing of basic arithmetic operators
1 parent 77f1a85 commit 771cfe6

File tree

4 files changed

+79
-18
lines changed

4 files changed

+79
-18
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,27 +74,28 @@ defmodule Module.Types do
7474
no_warn_undefined: no_warn_undefined,
7575
# A list of cached modules received from the parallel compiler
7676
cache: cache,
77-
# The mode control what happens on function application when
77+
# The mode controls what happens on function application when
7878
# there are gradual arguments. Non-gradual arguments always
7979
# perform subtyping and return its output (OUT).
8080
#
81-
# The mode may also control exhaustiveness checks in the future
82-
# (to be decided).
83-
#
8481
# * :strict - Requires types signatures (not implemented).
8582
# * Strong arrows with gradual performs subtyping and returns OUT
8683
# * Weak arrows with gradual performs subtyping and returns OUT
8784
#
8885
# * :static - Type signatures have been given.
8986
# * Strong arrows with gradual performs compatibility and returns OUT
90-
# * Weak arrows with gradual performs compatibility and returns OUT
87+
# * Weak arrows with gradual performs compatibility and returns dynamic()
9188
#
9289
# * :dynamic - Type signatures have not been given.
9390
# * Strong arrows with gradual performs compatibility and returns dynamic(OUT)
9491
# * Weak arrows with gradual performs compatibility and returns dynamic()
9592
#
9693
# * :infer - Same as :dynamic but skips remote calls.
9794
#
95+
# The mode may also control exhaustiveness checks in the future (to be decided).
96+
# We may also want for applications with subtyping in dynamic mode to always
97+
# intersect with dynamic, but this mode may be too lax (to be decided based on
98+
# feedback).
9899
mode: mode
99100
}
100101
end

lib/elixir/lib/module/types/of.ex

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,11 +328,21 @@ defmodule Module.Types.Of do
328328
|> union(atom())
329329
|> union(tuple([atom(), atom()]))
330330

331+
basic_arith_2_args_clauses = [
332+
{[integer(), integer()], integer()},
333+
{[integer(), float()], float()},
334+
{[float(), integer()], float()},
335+
{[float(), float()], float()}
336+
]
337+
331338
for {mod, fun, clauses} <- [
332339
# :binary
333340
{:binary, :copy, [{[binary(), integer()], binary()}]},
334341

335342
# :erlang
343+
{:erlang, :+, basic_arith_2_args_clauses},
344+
{:erlang, :-, basic_arith_2_args_clauses},
345+
{:erlang, :*, basic_arith_2_args_clauses},
336346
{:erlang, :"/=", [{[term(), term()], boolean()}]},
337347
{:erlang, :"=/=", [{[term(), term()], boolean()}]},
338348
{:erlang, :<, [{[term(), term()], boolean()}]},
@@ -869,9 +879,8 @@ defmodule Module.Types.Of do
869879
870880
#{args_to_quoted_string(mod, fun, args_types) |> indent(4)}
871881
872-
but expected types:
873-
874-
#{clauses_args_to_quoted_string(mod, fun, clauses, args_types) |> indent(4)}
882+
but expected one of:
883+
#{clauses_args_to_quoted_string(mod, fun, clauses, args_types)}
875884
""",
876885
format_traces(traces)
877886
])
@@ -1020,6 +1029,18 @@ defmodule Module.Types.Of do
10201029
alias Inspect.Algebra, as: IA
10211030

10221031
defp clauses_args_to_quoted_string(mod, fun, [{args, _return}], args_types) do
1032+
"\n " <> (clause_args_to_quoted_string(mod, fun, args, args_types) |> indent(4))
1033+
end
1034+
1035+
defp clauses_args_to_quoted_string(mod, fun, clauses, args_types) do
1036+
clauses
1037+
|> Enum.with_index(fn {args, _return}, index ->
1038+
"\n##{index + 1}\n#{clause_args_to_quoted_string(mod, fun, args, args_types)}" |> indent(4)
1039+
end)
1040+
|> Enum.join("\n")
1041+
end
1042+
1043+
defp clause_args_to_quoted_string(mod, fun, args, args_types) do
10231044
ansi? = IO.ANSI.enabled?()
10241045

10251046
docs =

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

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ defmodule Module.Types.ExprTest do
5555
5656
empty_list()
5757
58-
but expected types:
58+
but expected one of:
5959
6060
non_empty_list(term(), term())
6161
"""
@@ -70,7 +70,7 @@ defmodule Module.Types.ExprTest do
7070
7171
integer()
7272
73-
but expected types:
73+
but expected one of:
7474
7575
non_empty_list(term(), term())
7676
"""
@@ -92,7 +92,7 @@ defmodule Module.Types.ExprTest do
9292
9393
empty_list()
9494
95-
but expected types:
95+
but expected one of:
9696
9797
non_empty_list(term(), term())
9898
"""
@@ -107,7 +107,7 @@ defmodule Module.Types.ExprTest do
107107
108108
integer()
109109
110-
but expected types:
110+
but expected one of:
111111
112112
non_empty_list(term(), term())
113113
"""
@@ -327,7 +327,7 @@ defmodule Module.Types.ExprTest do
327327
328328
integer(), integer()
329329
330-
but expected types:
330+
but expected one of:
331331
332332
integer(), {...}
333333
@@ -372,7 +372,7 @@ defmodule Module.Types.ExprTest do
372372
373373
integer(), integer(), binary()
374374
375-
but expected types:
375+
but expected one of:
376376
377377
integer(), {...}, term()
378378
@@ -411,7 +411,7 @@ defmodule Module.Types.ExprTest do
411411
412412
integer(), integer()
413413
414-
but expected types:
414+
but expected one of:
415415
416416
integer(), {...}
417417
@@ -673,6 +673,45 @@ defmodule Module.Types.ExprTest do
673673
end
674674

675675
describe ":erlang rewrites" do
676+
test "Kernel.+/2" do
677+
assert typeerror!([x = :foo, y = 123], x + y) |> strip_ansi() ==
678+
~l"""
679+
incompatible types given to Kernel.+/2:
680+
681+
x + y
682+
683+
given types:
684+
685+
dynamic(:foo), integer()
686+
687+
but expected one of:
688+
689+
#1
690+
integer(), integer()
691+
692+
#2
693+
integer(), float()
694+
695+
#3
696+
float(), integer()
697+
698+
#4
699+
float(), float()
700+
701+
where "x" was given the type:
702+
703+
# type: dynamic(:foo)
704+
# from: types_test.ex:677
705+
x = :foo
706+
707+
where "y" was given the type:
708+
709+
# type: integer()
710+
# from: types_test.ex:677
711+
y = 123
712+
"""
713+
end
714+
676715
test "Integer.to_string/1" do
677716
assert typecheck!([x = 123], Integer.to_string(x)) == binary()
678717
assert typedyn!([x = 123], Integer.to_string(x)) == dynamic(binary())
@@ -687,7 +726,7 @@ defmodule Module.Types.ExprTest do
687726
688727
dynamic(:foo)
689728
690-
but expected types:
729+
but expected one of:
691730
692731
integer()
693732
@@ -713,7 +752,7 @@ defmodule Module.Types.ExprTest do
713752
714753
dynamic(:foo)
715754
716-
but expected types:
755+
but expected one of:
717756
718757
integer()
719758

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ defmodule Module.Types.PatternTest do
3636
3737
empty_list() or non_empty_list(integer())
3838
39-
but expected types:
39+
but expected one of:
4040
4141
non_empty_list(term(), term())
4242

0 commit comments

Comments
 (0)