Skip to content

Commit 90f6ced

Browse files
committed
Typing of comparison operator
1 parent a0e061f commit 90f6ced

File tree

3 files changed

+52
-24
lines changed

3 files changed

+52
-24
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ TODO.
1010

1111
* Type checking of all built-in conversion functions, such as `List.to_integer/1` and `Atom.to_string/1`
1212

13+
* Type checking of all functions in `Kernel` which are inlined by the compiler
14+
1315
* Type checking of all functions in the `Tuple` module
1416

1517
## ExUnit improvements
@@ -80,6 +82,7 @@ You may also prever to write using guards:
8082
* [Kernel] Track the type of tuples in patterns and inside `elem/2`
8183
* [List] Add `List.ends_with?/2`
8284
* [Macro] Improve `dbg` handling of `if/2`, `with/1` and of code blocks
85+
* [Macro] Add `Macro.struct_info!/2` to return struct information mirroring `mod.__info__(:struct)`
8386
* [Process] Handle arbitrarily high integer values in `Process.sleep/1`
8487
* [String] Inspect special whitespace and zero-width characters using their Unicode representation
8588

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

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,14 @@ defmodule Module.Types.Of do
333333
{:binary, :copy, [{[binary(), integer()], binary()}]},
334334

335335
# :erlang
336+
{:erlang, :"/=", [{[term(), term()], boolean()}]},
337+
{:erlang, :"=/=", [{[term(), term()], boolean()}]},
338+
{:erlang, :<, [{[term(), term()], boolean()}]},
339+
{:erlang, :"=<", [{[term(), term()], boolean()}]},
340+
{:erlang, :==, [{[term(), term()], boolean()}]},
341+
{:erlang, :"=:=", [{[term(), term()], boolean()}]},
342+
{:erlang, :>, [{[term(), term()], boolean()}]},
343+
{:erlang, :>=, [{[term(), term()], boolean()}]},
336344
{:erlang, :atom_to_binary, [{[atom()], binary()}]},
337345
{:erlang, :atom_to_list, [{[atom()], list(integer())}]},
338346
{:erlang, :band, [{[integer(), integer()], integer()}]},
@@ -492,38 +500,55 @@ defmodule Module.Types.Of do
492500

493501
def apply(:erlang, name, [left, right], expr, stack, context)
494502
when name in [:>=, :"=<", :>, :<, :min, :max] do
495-
result = if name in [:min, :max], do: union(left, right), else: boolean()
503+
context =
504+
cond do
505+
match?({false, _}, map_fetch(left, :__struct__)) or
506+
match?({false, _}, map_fetch(right, :__struct__)) ->
507+
warning = {:struct_comparison, expr, context}
508+
warn(__MODULE__, warning, elem(expr, 1), stack, context)
509+
510+
number_type?(left) and number_type?(right) ->
511+
context
496512

497-
cond do
498-
match?({false, _}, map_fetch(left, :__struct__)) or
499-
match?({false, _}, map_fetch(right, :__struct__)) ->
500-
warning = {:struct_comparison, expr, context}
501-
{result, warn(__MODULE__, warning, elem(expr, 1), stack, context)}
513+
disjoint?(left, right) ->
514+
warning = {:mismatched_comparison, expr, context}
515+
warn(__MODULE__, warning, elem(expr, 1), stack, context)
516+
517+
true ->
518+
context
519+
end
502520

503-
number_type?(left) and number_type?(right) ->
504-
{result, context}
521+
cond do
522+
name in [:min, :max] ->
523+
{union(left, right), context}
505524

506-
disjoint?(left, right) ->
507-
warning = {:mismatched_comparison, expr, context}
508-
{result, warn(__MODULE__, warning, elem(expr, 1), stack, context)}
525+
gradual?(left) or gradual?(right) ->
526+
{dynamic(boolean()), context}
509527

510528
true ->
511-
{result, context}
529+
{boolean(), context}
512530
end
513531
end
514532

515533
def apply(:erlang, name, [left, right], expr, stack, context)
516534
when name in [:==, :"/=", :"=:=", :"=/="] do
517-
cond do
518-
name in [:==, :"/="] and number_type?(left) and number_type?(right) ->
519-
{boolean(), context}
535+
context =
536+
cond do
537+
name in [:==, :"/="] and number_type?(left) and number_type?(right) ->
538+
context
520539

521-
disjoint?(left, right) ->
522-
warning = {:mismatched_comparison, expr, context}
523-
{boolean(), warn(__MODULE__, warning, elem(expr, 1), stack, context)}
540+
disjoint?(left, right) ->
541+
warning = {:mismatched_comparison, expr, context}
542+
warn(__MODULE__, warning, elem(expr, 1), stack, context)
524543

525-
true ->
526-
{boolean(), context}
544+
true ->
545+
context
546+
end
547+
548+
if gradual?(left) or gradual?(right) do
549+
{dynamic(boolean()), context}
550+
else
551+
{boolean(), context}
527552
end
528553
end
529554

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,8 @@ defmodule Module.Types.ExprTest do
577577
describe "comparison" do
578578
test "works across numbers" do
579579
assert typecheck!([x = 123, y = 456.0], min(x, y)) == dynamic(union(integer(), float()))
580-
assert typecheck!([x = 123, y = 456.0], x < y) == boolean()
581-
assert typecheck!([x = 123, y = 456.0], x == y) == boolean()
580+
assert typecheck!([x = 123, y = 456.0], x < y) == dynamic(boolean())
581+
assert typecheck!([x = 123, y = 456.0], x == y) == dynamic(boolean())
582582
end
583583

584584
test "warns when comparison is constant" do
@@ -606,7 +606,7 @@ defmodule Module.Types.ExprTest do
606606
"""}
607607

608608
assert typewarn!([x = 123, y = 456.0], x === y) ==
609-
{boolean(),
609+
{dynamic(boolean()),
610610
~l"""
611611
comparison between incompatible types found:
612612
@@ -631,7 +631,7 @@ defmodule Module.Types.ExprTest do
631631

632632
test "warns on comparison with struct across dynamic call" do
633633
assert typewarn!([x = :foo, y = %Point{}, mod = Kernel], mod.<=(x, y)) ==
634-
{boolean(),
634+
{dynamic(boolean()),
635635
~l"""
636636
comparison with structs found:
637637

0 commit comments

Comments
 (0)