Skip to content

Commit f4e1b34

Browse files
authored
Fix incorrect guard type refinement with length/map_size and != operator (#15327)
Previously `empty_list()` and `empty_map` were returned for `length(x) != n` and `map_size(x) != n` when n > 0. Now the types are widened to `list()` and `open_map()`
1 parent 55a3899 commit f4e1b34

2 files changed

Lines changed: 53 additions & 6 deletions

File tree

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -590,17 +590,29 @@ defmodule Module.Types.Apply do
590590

591591
{expected, precise?} =
592592
case fun do
593-
:length when :erlang.xor(polarity, literal > 0) ->
594-
{@empty_list, literal == 0}
593+
:length when polarity and literal == 0 ->
594+
{@empty_list, true}
595+
596+
:length when not polarity and literal == 0 ->
597+
{@non_empty_list, true}
598+
599+
:length when polarity ->
600+
{@non_empty_list, false}
595601

596602
:length ->
597-
{@non_empty_list, literal == 0}
603+
{@list, false}
604+
605+
:map_size when polarity and literal == 0 ->
606+
{@empty_map, true}
607+
608+
:map_size when not polarity and literal == 0 ->
609+
{@non_empty_map, true}
598610

599-
:map_size when :erlang.xor(polarity, literal > 0) ->
600-
{@empty_map, literal == 0}
611+
:map_size when polarity ->
612+
{@non_empty_map, false}
601613

602614
:map_size ->
603-
{@non_empty_map, literal == 0}
615+
{open_map(), false}
604616

605617
:tuple_size when polarity ->
606618
{tuple(List.duplicate(term(), literal)), true}

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,17 @@ defmodule Module.Types.PatternTest do
11591159

11601160
assert typecheck!([x], 0 != length(x), x) == dynamic(non_empty_list(term()))
11611161
assert typecheck!([x], not (0 != length(x)), x) == dynamic(empty_list())
1162+
1163+
assert typecheck!([x], length(x) == 1, x) == dynamic(non_empty_list(term()))
1164+
assert typecheck!([x], not (length(x) != 1), x) == dynamic(non_empty_list(term()))
1165+
assert typecheck!([x], 1 == length(x), x) == dynamic(non_empty_list(term()))
1166+
1167+
assert typecheck!([x], length(x) != 1, x) == dynamic(list(term()))
1168+
assert typecheck!([x], not (length(x) == 1), x) == dynamic(list(term()))
1169+
assert typecheck!([x], 1 != length(x), x) == dynamic(list(term()))
1170+
1171+
assert typecheck!([x], is_list(x) and length(x) != 1 and x != [], hd(x)) ==
1172+
dynamic(term())
11621173
end
11631174

11641175
test "length ordered" do
@@ -1206,6 +1217,20 @@ defmodule Module.Types.PatternTest do
12061217
assert typecheck!([x], 0 != map_size(x), x) == dynamic(@non_empty_map)
12071218
assert typecheck!([x], not (0 == map_size(x)), x) == dynamic(@non_empty_map)
12081219
assert typecheck!([x], not (0 != map_size(x)), x) == dynamic(empty_map())
1220+
1221+
assert typecheck!([x], map_size(x) == 1, x) == dynamic(@non_empty_map)
1222+
assert typecheck!([x], not (map_size(x) != 1), x) == dynamic(@non_empty_map)
1223+
assert typecheck!([x], 1 == map_size(x), x) == dynamic(@non_empty_map)
1224+
1225+
assert typecheck!([x], map_size(x) != 1, x) == dynamic(open_map())
1226+
assert typecheck!([x], not (map_size(x) == 1), x) == dynamic(open_map())
1227+
assert typecheck!([x], 1 != map_size(x), x) == dynamic(open_map())
1228+
1229+
assert typecheck!(
1230+
[x],
1231+
is_map(x) and map_size(x) != 1 and is_map_key(x, :a) and is_map_key(x, :b),
1232+
x
1233+
) == dynamic(open_map(a: term(), b: term()))
12091234
end
12101235

12111236
test "map_size ordered" do
@@ -1270,6 +1295,16 @@ defmodule Module.Types.PatternTest do
12701295
assert typecheck!([x], 2 != tuple_size(x), x) == dynamic(@non_binary_tuple)
12711296
assert typecheck!([x], not (2 == tuple_size(x)), x) == dynamic(@non_binary_tuple)
12721297
assert typecheck!([x], not (2 != tuple_size(x)), x) == dynamic(tuple([term(), term()]))
1298+
1299+
assert typecheck!([x], tuple_size(x) != 1, x) ==
1300+
dynamic(difference(open_tuple([]), tuple([term()])))
1301+
1302+
assert typecheck!(
1303+
[x],
1304+
is_tuple(x) and tuple_size(x) != 1 and tuple_size(x) != 0,
1305+
x
1306+
)
1307+
|> equal?(dynamic(open_tuple([term(), term()])))
12731308
end
12741309

12751310
test "tuple_size ordered" do

0 commit comments

Comments
 (0)