Skip to content

Commit ccab6a9

Browse files
authored
Merge pull request #10358 from jhogberg/john/compiler/fix-mapfoldl-type-inference/GH-10354/OTP-19845
beam_call_types: Fix incorrect typing for lists:mapfold[lr]/3
2 parents bb2b34d + 40d203c commit ccab6a9

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

lib/compiler/src/beam_call_types.erl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,18 +1294,22 @@ lists_map_type_1(_, ElementType) ->
12941294

12951295
lists_mapfold_type(Fun, Init, List) ->
12961296
case {meet(Fun, #t_fun{type=#t_tuple{size=2}}), meet(List, #t_list{})} of
1297-
{_, nil} ->
1298-
make_two_tuple(nil, Init);
1299-
{#t_fun{type=#t_tuple{elements=Es}}, ListType} ->
1297+
{#t_fun{type=T}, ListType} when T =/= none ->
1298+
#t_tuple{elements=Es} = normalize(T),
13001299
ElementType = beam_types:get_tuple_element(1, Es),
13011300
AccType = beam_types:get_tuple_element(2, Es),
13021301
lists_mapfold_type_1(ListType, ElementType, Init, AccType);
1303-
{#t_fun{type=none}, #t_list{}} ->
1304-
%% The fun never returns, so the only way we could return normally
1305-
%% is if the list is empty, in which case we'll return [] and the
1306-
%% initial value.
1302+
{_, #t_list{}} ->
1303+
%% The fun never returns, or is not a fun. The only way this can
1304+
%% succeed is if the given list is empty, in which case we'll
1305+
%% return [] and the initial value.
13071306
make_two_tuple(nil, Init);
1308-
_ ->
1307+
{_, nil} ->
1308+
make_two_tuple(nil, Init);
1309+
{_, _} ->
1310+
%% The fun never returns, or is not a fun, and the list is either
1311+
%% not a list or is guaranteed not to be empty. This will never
1312+
%% return.
13091313
none
13101314
end.
13111315

lib/compiler/test/beam_type_SUITE.erl

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
cons/1,tuple/1,
2828
record_float/1,binary_float/1,float_compare/1,float_overflow/1,
2929
arity_checks/1,elixir_binaries/1,find_best/1,
30-
test_size/1,cover_lists_functions/1,list_append/1,bad_binary_unit/1,
30+
test_size/1,cover_lists_functions/1,list_append/1,lists_mapfold/1,
31+
bad_binary_unit/1,
3132
none_argument/1,success_type_oscillation/1,type_subtraction/1,
3233
container_subtraction/1,is_list_opt/1,connected_tuple_elements/1,
3334
switch_fail_inference/1,failures/1,
@@ -63,6 +64,7 @@ groups() ->
6364
test_size,
6465
cover_lists_functions,
6566
list_append,
67+
lists_mapfold,
6668
bad_binary_unit,
6769
none_argument,
6870
success_type_oscillation,
@@ -1014,6 +1016,27 @@ list_append(_Config) ->
10141016
hello = id([]) ++ id(hello),
10151017
ok.
10161018

1019+
%% GH-10354: Type inference broke when the fun passed to mapfoldl/mapfoldr
1020+
%% returned a union of 2-tuples.
1021+
lists_mapfold(_Config) ->
1022+
expected_result = id(lists_mapfold_1()),
1023+
ok.
1024+
1025+
lists_mapfold_1() ->
1026+
List = [{key,[{number,1}]}],
1027+
{_, FinalAcc} =
1028+
lists:mapfoldl(
1029+
fun({_, PropListItem}, Acc) ->
1030+
Number = proplists:get_value(number, PropListItem),
1031+
case Number > 0 of
1032+
true ->
1033+
{false, Acc ++ [expected_result]};
1034+
_ ->
1035+
{true, Acc}
1036+
end
1037+
end, [], List),
1038+
hd(FinalAcc).
1039+
10171040
%% OTP-15872: The compiler would treat the "Unit" of bs_init instructions as
10181041
%% the unit of the result instead of the required unit of the input, causing
10191042
%% is_binary checks to be wrongly optimized away.

0 commit comments

Comments
 (0)