Skip to content

Commit 2bce2ca

Browse files
committed
Still check for disjoint types in guards
1 parent 118492e commit 2bce2ca

2 files changed

Lines changed: 67 additions & 10 deletions

File tree

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -523,18 +523,21 @@ defmodule Module.Types.Apply do
523523

524524
{actual, context} = of_fun.(arg, expected, expr, stack, context)
525525
result = if compatible?(actual, expected), do: return, else: boolean()
526+
527+
# We can skip return compare because literal is always an integer,
528+
# so it cannot be a disjoint comparison
526529
{result, context}
527530
end
528531
end
529532

530533
defp custom_compare(name, arg, literal, expected, expr, stack, context, of_fun) do
531-
{literal_type, context} = of_fun.(literal, term(), expr, stack, context)
532-
533534
case booleaness(expected) do
534535
booleaness when booleaness in [:maybe_both, :none] ->
535536
compare(name, arg, literal, false, expr, stack, context, of_fun)
536537

537538
booleaness ->
539+
{literal_type, context} = of_fun.(literal, term(), expr, stack, context)
540+
538541
{polarity, return} =
539542
case booleaness do
540543
:maybe_true -> {name in [:==, :"=:="], @atom_true}
@@ -544,9 +547,13 @@ defmodule Module.Types.Apply do
544547
# If it is a singleton, we can always be precise
545548
if singleton?(literal_type) do
546549
expected = if polarity, do: literal_type, else: negation(literal_type)
547-
{actual, context} = of_fun.(arg, expected, expr, stack, context)
548-
result = if compatible?(actual, expected), do: return, else: boolean()
549-
{result, context}
550+
{arg_type, context} = of_fun.(arg, expected, expr, stack, context)
551+
result = if compatible?(arg_type, expected), do: return, else: boolean()
552+
553+
# Because reverse polarity means we will infer negated types
554+
# (which are naturally disjoint), we skip checks in such cases
555+
skip_check? = not polarity
556+
return_compare(name, arg_type, literal_type, result, skip_check?, expr, stack, context)
550557
else
551558
expected =
552559
cond do
@@ -558,19 +565,23 @@ defmodule Module.Types.Apply do
558565
true -> literal_type
559566
end
560567

561-
{_, context} = of_fun.(arg, expected, expr, stack, context)
562-
{boolean(), context}
568+
{arg_type, context} = of_fun.(arg, expected, expr, stack, context)
569+
return_compare(name, arg_type, literal_type, boolean(), false, expr, stack, context)
563570
end
564571
end
565572
end
566573

567-
defp compare(name, left, right, literal?, expr, stack, context, of_fun) do
574+
defp compare(name, left, right, both_literal?, expr, stack, context, of_fun) do
568575
{left_type, context} = of_fun.(left, term(), expr, stack, context)
569576
{right_type, context} = of_fun.(right, term(), expr, stack, context)
570-
result = return(boolean(), [left_type, right_type], stack)
577+
return_compare(name, left_type, right_type, boolean(), both_literal?, expr, stack, context)
578+
end
579+
580+
defp return_compare(name, left_type, right_type, result, skip_check?, expr, stack, context) do
581+
result = return(result, [left_type, right_type], stack)
571582

572583
cond do
573-
literal? or not is_warning(stack) ->
584+
skip_check? or not is_warning(stack) ->
574585
{result, context}
575586

576587
name in [:==, :"/="] and number_type?(left_type) and number_type?(right_type) ->

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,52 @@ defmodule Module.Types.PatternTest do
874874
assert typecheck!([x], not (x != []), x) == dynamic(empty_list())
875875
assert typecheck!([x], not (x !== []), x) == dynamic(empty_list())
876876
end
877+
878+
test "warnings" do
879+
assert typeerror!([x = {}], x == 0, x) =~ ~l"""
880+
comparison between distinct types found:
881+
882+
x == 0
883+
884+
given types:
885+
886+
dynamic({}) == integer()
887+
"""
888+
889+
assert typeerror!([x = {}], x != 0, x) =~ ~l"""
890+
comparison between distinct types found:
891+
892+
x != 0
893+
894+
given types:
895+
896+
dynamic({}) != integer()
897+
"""
898+
899+
assert typeerror!([x = {}], x == :foo, x) =~ ~l"""
900+
comparison between distinct types found:
901+
902+
x == :foo
903+
904+
given types:
905+
906+
dynamic({}) == :foo
907+
"""
908+
909+
assert typeerror!([x = {}], not (x != :foo), x) =~ ~l"""
910+
comparison between distinct types found:
911+
912+
x != :foo
913+
914+
given types:
915+
916+
dynamic({}) != :foo
917+
"""
918+
919+
# We cannot warn in this case because the inference itself will lead to disjoint types
920+
assert typecheck!([x = {}], not (x == :foo), x) == dynamic(tuple([]))
921+
assert typecheck!([x = {}], x != :foo, x) == dynamic(tuple([]))
922+
end
877923
end
878924

879925
describe "comparison in guards" do

0 commit comments

Comments
 (0)