diff --git a/erts/doc/guides/absform.md b/erts/doc/guides/absform.md index 53cfd0405bce..ad8a092ddbd8 100644 --- a/erts/doc/guides/absform.md +++ b/erts/doc/guides/absform.md @@ -449,6 +449,8 @@ represented in the same way as the corresponding expression. - If T is an atom, a character, or an integer literal L, then Rep(T) = Rep(L). - If T is a bitstring type `<<_:M,_:_*N>>`, where `M` and `N` are singleton integer types, then Rep(T) = `{type,ANNO,binary,[Rep(M),Rep(N)]}`. +- If T is a singleton binary type `<<"bytes">>`, then Rep(T) = + `{bin_type,ANNO,Bin}`, where `Bin` is the binary value. - If T is the empty list type `[]`, then Rep(T) = `{type,ANNO,nil,[]}`, that is, the empty list type `[]` cannot be distinguished from the predefined type `t:nil/0`. diff --git a/lib/dialyzer/src/erl_types.erl b/lib/dialyzer/src/erl_types.erl index d021dfd4fa2f..5dc1416f57ce 100644 --- a/lib/dialyzer/src/erl_types.erl +++ b/lib/dialyzer/src/erl_types.erl @@ -51,6 +51,7 @@ t_atoms/1, t_atom_vals/1, t_binary/0, + t_binary_val/1, t_bitstr/0, t_bitstr/2, t_bitstr_base/1, @@ -290,7 +291,8 @@ -define(unknown_qual, unknown). -type qual() :: ?float_qual | ?integer_qual | ?nonempty_qual | ?pid_qual - | ?port_qual | ?reference_qual | ?unknown_qual | {_, _}. + | ?port_qual | ?reference_qual | ?unknown_qual | {_, _} + | [binary()]. %%----------------------------------------------------------------------------- %% The type representation @@ -330,6 +332,9 @@ -define(atom(Set), #c{tag=?atom_tag, elements=Set}). -define(bitstr(Unit, Base), #c{tag=?binary_tag, elements={Unit,Base}}). +-define(bitstr_vals(Unit, Base, Vals), + #c{tag=?binary_tag, elements={Unit,Base}, + qualifier=Vals}). -define(float, ?number(?any, ?float_qual)). -define(function(Domain, Range), #c{tag=?function_tag, elements={Domain,Range}}). @@ -664,6 +669,11 @@ t_bitstr(U, B) -> end, ?bitstr(U, NewB). +-spec t_binary_val(binary()) -> erl_type(). + +t_binary_val(Bin) -> + ?bitstr_vals(0, bit_size(Bin), set_singleton(Bin)). + -spec t_bitstr_unit(erl_type()) -> non_neg_integer(). t_bitstr_unit(?bitstr(U, _)) -> U. @@ -1971,6 +1981,7 @@ t_collect_vars_list([], Acc) -> Acc. t_from_term([H|T]) -> t_cons(t_from_term(H), t_from_term(T)); t_from_term([]) -> t_nil(); t_from_term(T) when is_atom(T) -> t_atom(T); +t_from_term(T) when erlang:is_binary(T) -> t_binary_val(T); t_from_term(T) when is_bitstring(T) -> t_bitstr(0, erlang:bit_size(T)); t_from_term(T) when is_float(T) -> t_float(); t_from_term(T) when is_function(T) -> @@ -2150,6 +2161,14 @@ t_sup_aux(?var(_), _) -> ?any; t_sup_aux(_, ?var(_)) -> ?any; t_sup_aux(?atom(Set1), ?atom(Set2)) -> ?atom(set_union(Set1, Set2)); +t_sup_aux(?bitstr_vals(U1, B1, V1), ?bitstr_vals(U2, B2, V2)) + when is_list(V1), is_list(V2) -> + SupU = gcd(gcd(U1, U2), abs(B1 - B2)), + SupB = lists:min([B1, B2]), + case set_union(V1, V2) of + ?any -> t_bitstr(SupU, SupB); + NewVals -> ?bitstr_vals(SupU, SupB, NewVals) + end; t_sup_aux(?bitstr(U1, B1), ?bitstr(U2, B2)) -> t_bitstr(gcd(gcd(U1, U2), abs(B1-B2)), lists:min([B1, B2])); t_sup_aux(?function(Domain1, Range1), ?function(Domain2, Range2)) -> @@ -2603,6 +2622,8 @@ t_elements(?nil = T) -> [T]; t_elements(?atom(?any) = T) -> [T]; t_elements(?atom(Atoms)) -> [t_atom(A) || A <- Atoms]; +t_elements(#c{tag=?binary_tag, qualifier=Vals}) when is_list(Vals) -> + [t_binary_val(V) || V <- Vals]; t_elements(?bitstr(_, _) = T) -> [T]; t_elements(?function(_, _) = T) -> [T]; t_elements(?identifier(?any) = T) -> [T]; @@ -2686,6 +2707,22 @@ t_inf_aux(?atom(Set1), ?atom(Set2)) -> ?none -> ?none; NewSet -> ?atom(NewSet) end; +t_inf_aux(?bitstr_vals(_U1, _B1, V1), ?bitstr_vals(_U2, _B2, V2)) + when is_list(V1), is_list(V2) -> + case ordsets:intersection(V1, V2) of + [] -> ?none; + NewVals -> make_bitstr_vals(NewVals) + end; +t_inf_aux(?bitstr_vals(_U1, _B1, Vals), ?bitstr(U2, B2)) + when is_list(Vals) -> + Filtered = [V || V <- Vals, bitstr_val_matches(V, U2, B2)], + case Filtered of + [] -> ?none; + _ -> make_bitstr_vals(Filtered) + end; +t_inf_aux(?bitstr(U1, B1), ?bitstr_vals(U2, B2, Vals)) + when is_list(Vals) -> + t_inf_aux(?bitstr_vals(U2, B2, Vals), ?bitstr(U1, B1)); t_inf_aux(?bitstr(U1, B1), ?bitstr(0, B2)) -> if B2 >= B1 andalso (B2-B1) rem U1 =:= 0 -> t_bitstr(0, B2); true -> ?none @@ -3149,6 +3186,19 @@ findfirst(N1, N2, U1, B1, U2, B2) -> findfirst(N1_1, N2, U1, B1, U2, B2) end. +bitstr_val_matches(Bin, Unit, Base) -> + Sz = bit_size(Bin), + case Unit of + 0 -> Sz =:= Base; + _ -> Sz >= Base andalso (Sz - Base) rem Unit =:= 0 + end. + +make_bitstr_vals(Vals) -> + Sizes = [bit_size(V) || V <- Vals], + Base = lists:min(Sizes), + Unit = lists:foldl(fun(S, U) -> gcd(abs(S - Base), U) end, 0, Sizes), + ?bitstr_vals(Unit, Base, Vals). + %%----------------------------------------------------------------------------- %% Substitution of variables %% @@ -3403,6 +3453,12 @@ t_subtract_aux(?atom(Set1), ?atom(Set2)) -> ?none -> ?none; Set -> ?atom(Set) end; +t_subtract_aux(?bitstr_vals(U1, B1, V1), ?bitstr_vals(_U2, _B2, V2)) + when is_list(V1), is_list(V2) -> + case ordsets:subtract(V1, V2) of + [] -> ?none; + NewVals -> ?bitstr_vals(U1, B1, NewVals) + end; t_subtract_aux(?bitstr(U1, B1), ?bitstr(U2, B2)) -> subtract_bin(t_bitstr(U1, B1), t_inf(t_bitstr(U1, B1), t_bitstr(U2, B2))); t_subtract_aux(?function(_, _) = T1, ?function(_, _) = T2) -> @@ -3973,6 +4029,9 @@ t_to_string(?atom(Set), _RecDict) -> _ -> set_to_string(Set) end; +t_to_string(#c{tag=?binary_tag, qualifier=Vals}, _RecDict) + when is_list(Vals) -> + flat_join([bin_val_to_string(V) || V <- Vals], " | "); t_to_string(?bitstr(0, 0), _RecDict) -> "<<>>"; t_to_string(?bitstr(8, 0), _RecDict) -> @@ -4391,6 +4450,8 @@ from_form({paren_type, _Anno, [Type]}, S, D, L, C) -> from_form({remote_type, Anno, [{atom, _, Module}, {atom, _, Type}, Args]}, S, D, L, C) -> remote_from_form(Anno, Module, Type, Args, S, D, L, C); +from_form({bin_type, _Anno, Bin}, _S, _D, L, C) -> + {t_binary_val(Bin), L, C}; from_form({atom, _Anno, Atom}, _S, _D, L, C) -> {t_atom(Atom), L, C}; from_form({integer, _Anno, Int}, _S, _D, L, C) -> @@ -4875,6 +4936,8 @@ separate_key(?atom(Atoms)) when Atoms =/= ?any -> [t_atom(A) || A <- Atoms]; separate_key(?number(_, _) = T) -> t_elements(T); +separate_key(#c{tag=?binary_tag, qualifier=Vals}) when is_list(Vals) -> + [t_binary_val(V) || V <- Vals]; separate_key(?union(List)) -> lists:append([separate_key(K) || K <- List, not t_is_none(K)]); separate_key(Key) -> @@ -4972,6 +5035,7 @@ check_record_fields({remote_type, _Anno, [{atom, _, _}, {atom, _, _}, Args]}, check_record_fields({atom, _Anno, _}, _S, C) -> C; check_record_fields({integer, _Anno, _}, _S, C) -> C; check_record_fields({char, _Anno, _}, _S, C) -> C; +check_record_fields({bin_type, _Anno, _}, _S, C) -> C; check_record_fields({op, _Anno, _Op, _Arg}, _S, C) -> C; check_record_fields({op, _Anno, _Op, _Arg1, _Arg2}, _S, C) -> C; check_record_fields({type, _Anno, tuple, any}, _S, C) -> C; @@ -5115,6 +5179,8 @@ t_form_to_string({var, _Anno, Name}) -> atom_to_list(Name); t_form_to_string({atom, _Anno, Atom}) -> io_lib:write_string(atom_to_list(Atom), $'); % To quote or not to quote... ' t_form_to_string({integer, _Anno, Int}) -> integer_to_list(Int); +t_form_to_string({bin_type, _Anno, Bin}) -> + "<<" ++ io_lib:write_string(binary_to_list(Bin), $") ++ ">>"; t_form_to_string({char, _Anno, Char}) -> integer_to_list(Char); t_form_to_string({op, _Anno, _Op, _Arg} = Op) -> case erl_eval:partial_eval(Op) of @@ -5397,6 +5463,8 @@ is_singleton_type(?int_range(V, V)) -> true; % cannot happen is_singleton_type(?int_set([_])) -> true; +is_singleton_type(#c{tag=?binary_tag, qualifier=[_]}) -> + true; is_singleton_type(_) -> false. @@ -5470,6 +5538,9 @@ set_max(Set) -> flat_format(F, S) -> lists:flatten(io_lib:format(F, S)). +bin_val_to_string(Bin) -> + flat_format("<<~ts>>", [io_lib:write_string(binary_to_list(Bin), $")]). + flat_join(List, Sep) -> lists:flatten(lists:join(Sep, List)). @@ -5614,6 +5685,8 @@ get_modules_mentioned({integer, _L, _Int}, _D, L, Acc) -> {L, Acc}; get_modules_mentioned({char, _L, _Char}, _D, L, Acc) -> {L, Acc}; +get_modules_mentioned({bin_type, _L, _Bin}, _D, L, Acc) -> + {L, Acc}; get_modules_mentioned({op, _L, _Op, _Arg}, _D, L, Acc) -> {L, Acc}; get_modules_mentioned({op, _L, _Op, _Arg1, _Arg2}, _D, L, Acc) -> diff --git a/lib/dialyzer/test/erl_types_SUITE.erl b/lib/dialyzer/test/erl_types_SUITE.erl index 5674ffd1cbdf..c9a57ee9ccf5 100644 --- a/lib/dialyzer/test/erl_types_SUITE.erl +++ b/lib/dialyzer/test/erl_types_SUITE.erl @@ -1,5 +1,11 @@ %% -*- erlang-indent-level: 4 -*- %% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2025-2026. All Rights Reserved. +%% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at @@ -12,10 +18,13 @@ %% See the License for the specific language governing permissions and %% limitations under the License. %% +%% %CopyrightEnd% +%% -module(erl_types_SUITE). -export([all/0,groups/0,init_per_group/2,end_per_group/2, consistency_and_to_string/1,misc/1,map_multiple_representations/1, + bin_type_modules_mentioned/1,bin_type_operations/1, absorption/1,associativity/1,commutativity/1,idempotence/1, identity/1,limit/1 ]). @@ -29,6 +38,8 @@ all() -> [consistency_and_to_string, misc, map_multiple_representations, + bin_type_modules_mentioned, + bin_type_operations, {group,property_tests} ]. @@ -357,6 +368,85 @@ map_multiple_representations(_Config) -> end(), ok. +bin_type_modules_mentioned(_Config) -> + %% A standalone bin_type form has no module dependencies. + [] = ?M:type_form_to_remote_modules({bin_type, 0, <<"foo">>}), + + %% A union of bin_type forms has no module dependencies. + [] = ?M:type_form_to_remote_modules( + {type, 0, union, [{bin_type, 0, <<"a">>}, + {bin_type, 0, <<"b">>}]}), + + %% A union of bin_type and remote_type extracts the remote module. + [some_mod] = ?M:type_form_to_remote_modules( + {type, 0, union, + [{bin_type, 0, <<"foo">>}, + {remote_type, 0, [{atom, 0, some_mod}, + {atom, 0, some_type}, + []]}]}), + + %% bin_type inside a list type has no module dependencies. + [] = ?M:type_form_to_remote_modules( + {type, 0, list, [{bin_type, 0, <<"x">>}]}), + + ok. + +bin_type_operations(_Config) -> + %% Construction and t_to_string. + Foo = ?M:t_binary_val(<<"foo">>), + Bar = ?M:t_binary_val(<<"bar">>), + Baz = ?M:t_binary_val(<<"baz">>), + Empty = ?M:t_binary_val(<<"">>), + "<<\"foo\">>" = ?M:t_to_string(Foo), + "<<\"bar\">>" = ?M:t_to_string(Bar), + "<<\"\">>" = ?M:t_to_string(Empty), + + %% t_is_binary / t_is_bitstr. + true = ?M:t_is_binary(Foo), + true = ?M:t_is_bitstr(Foo), + + %% t_from_term for binaries. + Foo = ?M:t_from_term(<<"foo">>), + Empty = ?M:t_from_term(<<"">>), + + %% t_sup — same-size vals merge into a multi-val type. + FooBar = ?M:t_sup(Foo, Bar), + "<<\"bar\">> | <<\"foo\">>" = ?M:t_to_string(FooBar), + %% t_sup — different-size vals fall back to generic binary. + Hi = ?M:t_binary_val(<<"hi">>), + FooHi = ?M:t_sup(Foo, Hi), + true = ?M:t_is_binary(FooHi), + + %% t_inf — val∩val. + None = ?M:t_none(), + Foo = ?M:t_inf(Foo, Foo), + None = ?M:t_inf(Foo, Bar), + %% t_inf — val∩binary(). + Foo = ?M:t_inf(Foo, ?M:t_binary()), + %% t_inf — val∩incompatible. + None = ?M:t_inf(Foo, ?M:t_atom()), + + %% t_subtract. + FooBarBaz = ?M:t_sup([Foo, Bar, Baz]), + FooBaz = ?M:t_subtract(FooBarBaz, Bar), + "<<\"baz\">> | <<\"foo\">>" = ?M:t_to_string(FooBaz), + + %% t_elements — decompose into individual vals. + [Foo] = ?M:t_elements(Foo), + 2 = length(?M:t_elements(FooBar)), + + %% is_singleton_type — single vs multiple vals. + %% (is_singleton_type is not exported, test indirectly via t_elements) + [_] = ?M:t_elements(Foo), + [_, _] = ?M:t_elements(FooBar), + + %% UTF-8 encoded binary literal types produce the same type as raw bytes. + %% <<"é"/utf8>> encodes to <<195,169>> — same as the raw binary. + Utf8Bin = ?M:t_binary_val(<<195,169>>), + Utf8Bin = ?M:t_binary_val(unicode:characters_to_binary([233], unicode, utf8)), + + ok. + absorption(Config) -> %% manual test: proper:quickcheck(erl_types_prop:absorption()). true = ct_property_test:quickcheck(erl_types_prop:absorption(), Config). diff --git a/lib/dialyzer/test/indent_SUITE_data/results/map_galore b/lib/dialyzer/test/indent_SUITE_data/results/map_galore index 38da8e623a73..f4ea4a5546d1 100644 --- a/lib/dialyzer/test/indent_SUITE_data/results/map_galore +++ b/lib/dialyzer/test/indent_SUITE_data/results/map_galore @@ -11,7 +11,17 @@ map_galore.erl:1002:51: A key of type float() => 'c' | 'v'} map_galore.erl:1082:52: A key of type 'nonexisting' cannot exist in a map of type - #{10 := 'a0', + #{<<"50">> := [48 | 101, ...], + <<"51">> := [49 | 101, ...], + <<"52">> := [50 | 101, ...], + <<"53">> := [51 | 101, ...], + <<"54">> := [52 | 101, ...], + <<"55">> := [53 | 101, ...], + <<"56">> := [54 | 101, ...], + <<"57">> := [55 | 101, ...], + <<"58">> := [56 | 101, ...], + <<"59">> := [57 | 101, ...], + 10 := 'a0', 11 := 'a1', 12 := 'a2', 13 := 'a3', @@ -41,7 +51,6 @@ map_galore.erl:1082:52: A key of type 37 := [55 | 99, ...], 38 := [56 | 99, ...], 39 := [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...], @@ -53,6 +62,16 @@ map_galore.erl:1082:52: A key of type 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -83,17 +102,25 @@ map_galore.erl:1082:52: A key of type 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...], ...]} => - [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - 100 | 101, + [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100, ...]} => atom() | [1..255, ...]} map_galore.erl:1084:51: A key of type 42 cannot exist in a map of type - #{10 := 'a0', + #{<<"50">> := [48 | 101, ...], + <<"51">> := [49 | 101, ...], + <<"52">> := [50 | 101, ...], + <<"53">> := [51 | 101, ...], + <<"54">> := [52 | 101, ...], + <<"55">> := [53 | 101, ...], + <<"56">> := [54 | 101, ...], + <<"57">> := [55 | 101, ...], + <<"58">> := [56 | 101, ...], + <<"59">> := [57 | 101, ...], + 10 := 'a0', 11 := 'a1', 12 := 'a2', 13 := 'a3', @@ -123,7 +150,6 @@ map_galore.erl:1084:51: A key of type 37 := [55 | 99, ...], 38 := [56 | 99, ...], 39 := [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...], @@ -135,6 +161,16 @@ map_galore.erl:1084:51: A key of type 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -165,18 +201,26 @@ map_galore.erl:1084:51: A key of type 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...], ...]} => - [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - 100 | 101, + [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100, ...]} => atom() | [1..255, ...]} map_galore.erl:1142:61: The call map_galore:map_guard_sequence_1 (#{seq => 6, val => "e"}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq' := 1 | 2 | 3 | 4 | 5, 'val' := [97 | 98 | 99 | 100 | 101, ...], + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -207,7 +251,6 @@ map_galore.erl:1142:61: The call map_galore:map_guard_sequence_1 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[any(), ...]} | @@ -218,6 +261,16 @@ map_galore.erl:1142:61: The call map_galore:map_guard_sequence_1 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [any(), ...], + <<"51">> => [any(), ...], + <<"52">> => [any(), ...], + <<"53">> => [any(), ...], + <<"54">> => [any(), ...], + <<"55">> => [any(), ...], + <<"56">> => [any(), ...], + <<"57">> => [any(), ...], + <<"58">> => [any(), ...], + <<"59">> => [any(), ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -248,13 +301,23 @@ map_galore.erl:1142:61: The call map_galore:map_guard_sequence_1 37 => [any(), ...], 38 => [any(), ...], 39 => [any(), ...], - <<_:16>> | [any(), ...] | {_} => [any(), ...]} => + [any(), ...] | {_} => [any(), ...]} => atom() | [1..255, ...]}) map_galore.erl:1143:61: The call map_galore:map_guard_sequence_2 (#{'b' => 5}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a' := 'gg' | 'kk' | 'sc' | 3 | 4, 'b' => 'other' | 3 | 4 | 5, 'c' => 'sc2', + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -285,7 +348,6 @@ map_galore.erl:1143:61: The call map_galore:map_guard_sequence_2 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[any(), ...]} | @@ -296,6 +358,16 @@ map_galore.erl:1143:61: The call map_galore:map_guard_sequence_2 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [any(), ...], + <<"51">> => [any(), ...], + <<"52">> => [any(), ...], + <<"53">> => [any(), ...], + <<"54">> => [any(), ...], + <<"55">> => [any(), ...], + <<"56">> => [any(), ...], + <<"57">> => [any(), ...], + <<"58">> => [any(), ...], + <<"59">> => [any(), ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -326,11 +398,21 @@ map_galore.erl:1143:61: The call map_galore:map_guard_sequence_2 37 => [any(), ...], 38 => [any(), ...], 39 => [any(), ...], - <<_:16>> | [any(), ...] | {_} => [any(), ...]} => + [any(), ...] | {_} => [any(), ...]} => atom() | [1..255, ...]}) map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 (#{'seq' := 6, 'val' := [101, ...], + <<"50">> := [48 | 101, ...], + <<"51">> := [49 | 101, ...], + <<"52">> := [50 | 101, ...], + <<"53">> := [51 | 101, ...], + <<"54">> := [52 | 101, ...], + <<"55">> := [53 | 101, ...], + <<"56">> := [54 | 101, ...], + <<"57">> := [55 | 101, ...], + <<"58">> := [56 | 101, ...], + <<"59">> := [57 | 101, ...], 10 := 'a0', 11 := 'a1', 12 := 'a2', @@ -361,7 +443,6 @@ map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 37 := [55 | 99, ...], 38 := [56 | 99, ...], 39 := [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | 3, @@ -373,6 +454,16 @@ map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -403,16 +494,24 @@ map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...], ...]} => - [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - 100 | 101, + [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100, ...]} => atom() | [1..255, ...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq' := 1 | 2 | 3 | 4 | 5, 'val' := [97 | 98 | 99 | 100 | 101, ...], + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -443,7 +542,6 @@ map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[any(), ...]} | @@ -454,6 +552,16 @@ map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [any(), ...], + <<"51">> => [any(), ...], + <<"52">> => [any(), ...], + <<"53">> => [any(), ...], + <<"54">> => [any(), ...], + <<"55">> => [any(), ...], + <<"56">> => [any(), ...], + <<"57">> => [any(), ...], + <<"58">> => [any(), ...], + <<"59">> => [any(), ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -484,10 +592,20 @@ map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1 37 => [any(), ...], 38 => [any(), ...], 39 => [any(), ...], - <<_:16>> | [any(), ...] | {_} => [any(), ...]} => + [any(), ...] | {_} => [any(), ...]} => atom() | [1..255, ...]}) map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 (#{'b' := 5, + <<"50">> := [48 | 101, ...], + <<"51">> := [49 | 101, ...], + <<"52">> := [50 | 101, ...], + <<"53">> := [51 | 101, ...], + <<"54">> := [52 | 101, ...], + <<"55">> := [53 | 101, ...], + <<"56">> := [54 | 101, ...], + <<"57">> := [55 | 101, ...], + <<"58">> := [56 | 101, ...], + <<"59">> := [57 | 101, ...], 10 := 'a0', 11 := 'a1', 12 := 'a2', @@ -518,7 +636,6 @@ map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 37 := [55 | 99, ...], 38 := [56 | 99, ...], 39 := [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | 3, @@ -530,6 +647,16 @@ map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -560,17 +687,25 @@ map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...], ...]} => - [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | - 100 | 101, + [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100, ...]} => atom() | [1..255, ...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a' := 'gg' | 'kk' | 'sc' | 3 | 4, 'b' => 'other' | 3 | 4 | 5, 'c' => 'sc2', + <<"50">> => [48 | 101, ...], + <<"51">> => [49 | 101, ...], + <<"52">> => [50 | 101, ...], + <<"53">> => [51 | 101, ...], + <<"54">> => [52 | 101, ...], + <<"55">> => [53 | 101, ...], + <<"56">> => [54 | 101, ...], + <<"57">> => [55 | 101, ...], + <<"58">> => [56 | 101, ...], + <<"59">> => [57 | 101, ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -601,7 +736,6 @@ map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 37 => [55 | 99, ...], 38 => [56 | 99, ...], 39 => [57 | 99, ...], - <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57, ...] | float() | {[any(), ...]} | @@ -612,6 +746,16 @@ map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 'one' => 'small', 'second' => 'small', 'third' => 'small', + <<"50">> => [any(), ...], + <<"51">> => [any(), ...], + <<"52">> => [any(), ...], + <<"53">> => [any(), ...], + <<"54">> => [any(), ...], + <<"55">> => [any(), ...], + <<"56">> => [any(), ...], + <<"57">> => [any(), ...], + <<"58">> => [any(), ...], + <<"59">> => [any(), ...], 10 => 'a0', 11 => 'a1', 12 => 'a2', @@ -642,16 +786,15 @@ map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2 37 => [any(), ...], 38 => [any(), ...], 39 => [any(), ...], - <<_:16>> | [any(), ...] | {_} => [any(), ...]} => + [any(), ...] | {_} => [any(), ...]} => atom() | [1..255, ...]}) map_galore.erl:1420:84: Fun application with arguments (#{'s' => 'none', 'v' => 'none'}) will never return since it differs in the 1st argument from the success typing arguments: (#{'s' := 'l' | 't' | 'v', 'v' := - 'none' | - <<_:16>> | - [<<_:16>>, ...] | - {<<_:16>>, <<_:16>>}}) + 'none' | <<"hi">> | + [<<"hi">>, ...] | + {<<"hi">>, <<"hi">>}}) map_galore.erl:1493:13: The test #{} =:= #{'a' := 1} can never evaluate to 'true' @@ -688,6 +831,11 @@ map_galore.erl:1767:9: The call maps:get map_galore.erl:188:41: The pattern #{'x' := 2} can never match the type #{'x' := 3} +map_galore.erl:1895:5: The pattern + #{10 := 10, "10" := 10, <<0,0,0,10>> := 10} can never match the type + #{<<"\000\000\000\001">> := 1, + 1 := 1, + [any()] | number() => integer()} map_galore.erl:189:41: The pattern #{'x' := 3} can never match the type {'a', 'b', 'c'} diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_galore b/lib/dialyzer/test/map_SUITE_data/results/map_galore index 29c325069693..0cc43ec2765e 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/map_galore +++ b/lib/dialyzer/test/map_SUITE_data/results/map_galore @@ -1,13 +1,13 @@ map_galore.erl:1000:52: A key of type 'nonexisting' cannot exist in a map of type #{1:='a', 2:='b', 4:='d', 5:='e', float()=>'c'} map_galore.erl:1002:51: A key of type 42 cannot exist in a map of type #{1:='a', 2:='b', 4:='d', 5:='e', float()=>'c' | 'v'} -map_galore.erl:1082:52: A key of type 'nonexisting' cannot exist in a map of type #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]} -map_galore.erl:1084:51: A key of type 42 cannot exist in a map of type #{10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]} -map_galore.erl:1142:61: The call map_galore:map_guard_sequence_1(#{'seq'=>6, 'val'=>"e"}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq':=1 | 2 | 3 | 4 | 5, 'val':=[97 | 98 | 99 | 100 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) -map_galore.erl:1143:61: The call map_galore:map_guard_sequence_2(#{'b'=>5}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='gg' | 'kk' | 'sc' | 3 | 4, 'b'=>'other' | 3 | 4 | 5, 'c'=>'sc2', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) -map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1(#{'seq':=6, 'val':=[101,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | 3,...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq':=1 | 2 | 3 | 4 | 5, 'val':=[97 | 98 | 99 | 100 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) -map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2(#{'b':=5, 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | 3,...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100 | 101,...]}=>atom() | [1..255,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='gg' | 'kk' | 'sc' | 3 | 4, 'b'=>'other' | 3 | 4 | 5, 'c'=>'sc2', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], <<_:16>> | [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], <<_:16>> | [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) -map_galore.erl:1420:84: Fun application with arguments (#{'s'=>'none', 'v'=>'none'}) will never return since it differs in the 1st argument from the success typing arguments: (#{'s':='l' | 't' | 'v', 'v':='none' | <<_:16>> | [<<_:16>>,...] | {<<_:16>>,<<_:16>>}}) +map_galore.erl:1082:52: A key of type 'nonexisting' cannot exist in a map of type #{<<"50">>:=[48 | 101,...], <<"51">>:=[49 | 101,...], <<"52">>:=[50 | 101,...], <<"53">>:=[51 | 101,...], <<"54">>:=[52 | 101,...], <<"55">>:=[53 | 101,...], <<"56">>:=[54 | 101,...], <<"57">>:=[55 | 101,...], <<"58">>:=[56 | 101,...], <<"59">>:=[57 | 101,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100,...]}=>atom() | [1..255,...]} +map_galore.erl:1084:51: A key of type 42 cannot exist in a map of type #{<<"50">>:=[48 | 101,...], <<"51">>:=[49 | 101,...], <<"52">>:=[50 | 101,...], <<"53">>:=[51 | 101,...], <<"54">>:=[52 | 101,...], <<"55">>:=[53 | 101,...], <<"56">>:=[54 | 101,...], <<"57">>:=[55 | 101,...], <<"58">>:=[56 | 101,...], <<"59">>:=[57 | 101,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100,...]}=>atom() | [1..255,...]} +map_galore.erl:1142:61: The call map_galore:map_guard_sequence_1(#{'seq'=>6, 'val'=>"e"}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq':=1 | 2 | 3 | 4 | 5, 'val':=[97 | 98 | 99 | 100 | 101,...], <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[any(),...], <<"51">>=>[any(),...], <<"52">>=>[any(),...], <<"53">>=>[any(),...], <<"54">>=>[any(),...], <<"55">>=>[any(),...], <<"56">>=>[any(),...], <<"57">>=>[any(),...], <<"58">>=>[any(),...], <<"59">>=>[any(),...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) +map_galore.erl:1143:61: The call map_galore:map_guard_sequence_2(#{'b'=>5}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='gg' | 'kk' | 'sc' | 3 | 4, 'b'=>'other' | 3 | 4 | 5, 'c'=>'sc2', <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[any(),...], <<"51">>=>[any(),...], <<"52">>=>[any(),...], <<"53">>=>[any(),...], <<"54">>=>[any(),...], <<"55">>=>[any(),...], <<"56">>=>[any(),...], <<"57">>=>[any(),...], <<"58">>=>[any(),...], <<"59">>=>[any(),...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) +map_galore.erl:1211:63: The call map_galore:map_guard_sequence_1(#{'seq':=6, 'val':=[101,...], <<"50">>:=[48 | 101,...], <<"51">>:=[49 | 101,...], <<"52">>:=[50 | 101,...], <<"53">>:=[51 | 101,...], <<"54">>:=[52 | 101,...], <<"55">>:=[53 | 101,...], <<"56">>:=[54 | 101,...], <<"57">>:=[55 | 101,...], <<"58">>:=[56 | 101,...], <<"59">>:=[57 | 101,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | 3,...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100,...]}=>atom() | [1..255,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'seq':=1 | 2 | 3 | 4 | 5, 'val':=[97 | 98 | 99 | 100 | 101,...], <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[any(),...], <<"51">>=>[any(),...], <<"52">>=>[any(),...], <<"53">>=>[any(),...], <<"54">>=>[any(),...], <<"55">>=>[any(),...], <<"56">>=>[any(),...], <<"57">>=>[any(),...], <<"58">>=>[any(),...], <<"59">>=>[any(),...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) +map_galore.erl:1212:63: The call map_galore:map_guard_sequence_2(#{'b':=5, <<"50">>:=[48 | 101,...], <<"51">>:=[49 | 101,...], <<"52">>:=[50 | 101,...], <<"53">>:=[51 | 101,...], <<"54">>:=[52 | 101,...], <<"55">>:=[53 | 101,...], <<"56">>:=[54 | 101,...], <<"57">>:=[55 | 101,...], <<"58">>:=[56 | 101,...], <<"59">>:=[57 | 101,...], 10:='a0', 11:='a1', 12:='a2', 13:='a3', 14:='a4', 15:='a5', 16:='a6', 17:='a7', 18:='a8', 19:='a9', 20:='b0', 21:='b1', 22:='b2', 23:='b3', 24:='b4', 25:='b5', 26:='b6', 27:='b7', 28:='b8', 29:='b9', 30:=[48 | 99,...], 31:=[49 | 99,...], 32:=[50 | 99,...], 33:=[51 | 99,...], 34:=[52 | 99,...], 35:=[53 | 99,...], 36:=[54 | 99,...], 37:=[55 | 99,...], 38:=[56 | 99,...], 39:=[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | 3,...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[54 | 99,...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | {[[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...],...]}=>[48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 100,...]}=>atom() | [1..255,...]}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':='gg' | 'kk' | 'sc' | 3 | 4, 'b'=>'other' | 3 | 4 | 5, 'c'=>'sc2', <<"50">>=>[48 | 101,...], <<"51">>=>[49 | 101,...], <<"52">>=>[50 | 101,...], <<"53">>=>[51 | 101,...], <<"54">>=>[52 | 101,...], <<"55">>=>[53 | 101,...], <<"56">>=>[54 | 101,...], <<"57">>=>[55 | 101,...], <<"58">>=>[56 | 101,...], <<"59">>=>[57 | 101,...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[48 | 99,...], 31=>[49 | 99,...], 32=>[50 | 99,...], 33=>[51 | 99,...], 34=>[52 | 99,...], 35=>[53 | 99,...], 36=>[54 | 99,...], 37=>[55 | 99,...], 38=>[56 | 99,...], 39=>[57 | 99,...], [48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57,...] | float() | {[any(),...]} | #{'k16'=>'a6', 'k26'=>'b6', 'k36'=>[any(),...], 'map'=>'key', 'one'=>'small', 'second'=>'small', 'third'=>'small', <<"50">>=>[any(),...], <<"51">>=>[any(),...], <<"52">>=>[any(),...], <<"53">>=>[any(),...], <<"54">>=>[any(),...], <<"55">>=>[any(),...], <<"56">>=>[any(),...], <<"57">>=>[any(),...], <<"58">>=>[any(),...], <<"59">>=>[any(),...], 10=>'a0', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8', 19=>'a9', 20=>'b0', 21=>'b1', 22=>'b2', 23=>'b3', 24=>'b4', 25=>'b5', 26=>'b6', 27=>'b7', 28=>'b8', 29=>'b9', 30=>[any(),...], 31=>[any(),...], 32=>[any(),...], 33=>[any(),...], 34=>[any(),...], 35=>[any(),...], 36=>[any(),...], 37=>[any(),...], 38=>[any(),...], 39=>[any(),...], [any(),...] | {_}=>[any(),...]}=>atom() | [1..255,...]}) +map_galore.erl:1420:84: Fun application with arguments (#{'s'=>'none', 'v'=>'none'}) will never return since it differs in the 1st argument from the success typing arguments: (#{'s':='l' | 't' | 'v', 'v':='none' | <<"hi">> | [<<"hi">>,...] | {<<"hi">>,<<"hi">>}}) map_galore.erl:1493:13: The test #{} =:= #{'a':=1} can never evaluate to 'true' map_galore.erl:1494:13: The test #{'a':=1} =:= #{} can never evaluate to 'true' map_galore.erl:1497:13: The test #{'a':=1} =:= #{'a':=2} can never evaluate to 'true' @@ -17,7 +17,9 @@ map_galore.erl:1500:13: The test #{'a':=1, 'b':=1} =:= #{'a':=1, 'b':=3} can nev map_galore.erl:1764:9: The call maps:get({1, 1},#{{1,float()}=>[101 | 108 | 112 | 116 | 117,...]}) will never return since the success typing arguments are (any(),map()) map_galore.erl:1765:55: The call maps:get('a',#{}) will never return since the success typing arguments are (any(),map()) map_galore.erl:1767:9: The call maps:get('a',#{'b'=>1, 'c'=>2}) will never return since the success typing arguments are (any(),map()) +map_galore.erl:1856:1: Function t_bif_map_merge/1 has no local return map_galore.erl:188:41: The pattern #{'x':=2} can never match the type #{'x':=3} +map_galore.erl:1895:5: The pattern #{10:=10, "10":=10, <<0,0,0,10>>:=10} can never match the type #{<<"\000\000\000\001">>:=1, 1:=1, [any()] | number()=>integer()} map_galore.erl:189:41: The pattern #{'x':=3} can never match the type {'a','b','c'} map_galore.erl:190:41: The pattern #{'x':=3} can never match the type #{'y':=3} map_galore.erl:191:41: The pattern #{'x':=3} can never match the type #{'x':=[101 | 104 | 114 | 116,...]} diff --git a/lib/dialyzer/test/small_SUITE_data/results/bin_type b/lib/dialyzer/test/small_SUITE_data/results/bin_type new file mode 100644 index 000000000000..8db89a0b527f --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/bin_type @@ -0,0 +1,20 @@ +%% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2026. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% diff --git a/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty b/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty index dbfaf63d6e73..f7b33c4ce885 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty +++ b/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty @@ -1,20 +1,40 @@ +%% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2026. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% binary_nonempty.erl:12:1: Function t2/0 has no local return binary_nonempty.erl:13:8: The call binary_nonempty:t2(<<>>) breaks the contract (nonempty_binary()) -> 'foo' binary_nonempty.erl:15:2: Invalid type specification for function binary_nonempty:t2/1. - The success typing is binary_nonempty:t2(<<>>) -> 'foo' + The success typing is binary_nonempty:t2(<<"">>) -> 'foo' But the spec is binary_nonempty:t2(nonempty_binary()) -> 'foo' They do not overlap in the 1st argument binary_nonempty.erl:19:1: Function t3/0 has no local return binary_nonempty.erl:20:8: The call binary_nonempty:t3(<<>>) breaks the contract (<<_:1,_:_*1>>) -> 'foo' binary_nonempty.erl:22:2: Invalid type specification for function binary_nonempty:t3/1. - The success typing is binary_nonempty:t3(<<>>) -> 'foo' + The success typing is binary_nonempty:t3(<<"">>) -> 'foo' But the spec is binary_nonempty:t3(<<_:1,_:_*1>>) -> 'foo' They do not overlap in the 1st argument binary_nonempty.erl:26:1: Function t4/0 has no local return binary_nonempty.erl:27:8: The call binary_nonempty:t4(<<>>) breaks the contract (<<_:8,_:_*8>>) -> 'foo' binary_nonempty.erl:29:2: Invalid type specification for function binary_nonempty:t4/1. - The success typing is binary_nonempty:t4(<<>>) -> 'foo' + The success typing is binary_nonempty:t4(<<"">>) -> 'foo' But the spec is binary_nonempty:t4(<<_:8,_:_*8>>) -> 'foo' They do not overlap in the 1st argument binary_nonempty.erl:33:2: Invalid type specification for function binary_nonempty:t5/1. @@ -22,7 +42,7 @@ binary_nonempty.erl:33:2: Invalid type specification for function binary_nonempt But the spec is binary_nonempty:t5(nonempty_binary()) -> 'foo' They do not overlap in the 1st argument binary_nonempty.erl:38:2: Invalid type specification for function binary_nonempty:t6/1. - The success typing is binary_nonempty:t6(<<_:8>>) -> 'foo' + The success typing is binary_nonempty:t6(<<"f">>) -> 'foo' But the spec is binary_nonempty:t6(<<>>) -> 'foo' They do not overlap in the 1st argument binary_nonempty.erl:43:2: Invalid type specification for function binary_nonempty:t7/1. @@ -32,6 +52,6 @@ binary_nonempty.erl:43:2: Invalid type specification for function binary_nonempt binary_nonempty.erl:5:1: Function t1/0 has no local return binary_nonempty.erl:6:8: The call binary_nonempty:t1(<<>>) breaks the contract (nonempty_bitstring()) -> 'foo' binary_nonempty.erl:8:2: Invalid type specification for function binary_nonempty:t1/1. - The success typing is binary_nonempty:t1(<<>>) -> 'foo' + The success typing is binary_nonempty:t1(<<"">>) -> 'foo' But the spec is binary_nonempty:t1(nonempty_bitstring()) -> 'foo' They do not overlap in the 1st argument diff --git a/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning b/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning index 67d07abce2fa..ac0523e8ed61 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning +++ b/lib/dialyzer/test/small_SUITE_data/results/relevant_record_warning @@ -1,3 +1,23 @@ +%% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2026. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% relevant_record_warning.erl:22:1: Function test/1 has no local return -relevant_record_warning.erl:23:14: Record construction #r{field::<<_:8>>} violates the declared type of field field::'binary' +relevant_record_warning.erl:23:14: Record construction #r{field::<<"*">>} violates the declared type of field field::'binary' diff --git a/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl new file mode 100644 index 000000000000..d972617310ab --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl @@ -0,0 +1,48 @@ +%% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2026. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +-module(bin_type). + +-export([handle/1, status/0, map_key/1, mixed/1, make_msg/1]). + +-record(msg, {tag :: <<"hello">> | <<"world">>, payload :: binary()}). + +-type cmd() :: <<"start">> | <<"stop">>. + +-spec handle(cmd()) -> ok. +handle(<<"start">>) -> ok; +handle(<<"stop">>) -> ok. + +-spec status() -> <<"ok">> | <<"error">>. +status() -> <<"ok">>. + +-spec map_key(#{<<"name">> := binary()}) -> binary(). +map_key(#{<<"name">> := V}) -> V. + +%% Exercises get_modules_mentioned with bin_type + remote type in a union. +-type mixed() :: <<"hello">> | binary:part(). + +-spec mixed(mixed()) -> binary(). +mixed(<<"hello">>) -> <<"greeting">>; +mixed({Pos, Len}) when is_integer(Pos), is_integer(Len) -> <<"part">>. + +-spec make_msg(binary()) -> #msg{}. +make_msg(Payload) -> #msg{tag = <<"hello">>, payload = Payload}. diff --git a/lib/dialyzer/test/user_SUITE_data/results/gh6580 b/lib/dialyzer/test/user_SUITE_data/results/gh6580 index 6060365082ce..7fdf79905815 100644 --- a/lib/dialyzer/test/user_SUITE_data/results/gh6580 +++ b/lib/dialyzer/test/user_SUITE_data/results/gh6580 @@ -1,7 +1,27 @@ +%% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2026. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% -gh6580.erl:11:21: The pattern <[_ | _], _> can never match the type <[],<<>>> +gh6580.erl:11:21: The pattern <[_ | _], _> can never match the type <[],<<"">>> gh6580.erl:5:1: Function f/0 has no local return gh6580.erl:6:5: The created fun has no local return -gh6580.erl:6:5: The pattern <[], _> can never match the type <<<>>,<<>>> -gh6580.erl:6:5: The pattern <[_ | _], _> can never match the type <<<>>,<<>>> -gh6580.erl:9:13: Fun application with arguments (<<>>,<<>>) will never return since it differs in the 1st argument from the success typing arguments: ([],any()) +gh6580.erl:6:5: The pattern <[], _> can never match the type <<<"">>,<<"">>> +gh6580.erl:6:5: The pattern <[_ | _], _> can never match the type <<<"">>,<<"">>> +gh6580.erl:9:13: Fun application with arguments (<<"">>,<<"">>) will never return since it differs in the 1st argument from the success typing arguments: ([],any()) diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 4cff06002893..b5fadeab7c68 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -3895,6 +3895,8 @@ check_type_2({remote_type, A, [{atom, _, Mod}, {atom, _, Name}, Args]}, end; check_type_2({integer, _A, _}, SeenVars, St) -> {SeenVars, St}; check_type_2({atom, _A, _}, SeenVars, St) -> {SeenVars, St}; +check_type_2({bin_type, _A, Value}, SeenVars, St) when is_binary(Value) -> + {SeenVars, St}; check_type_2({var, _A, '_'}, SeenVars, St) -> {SeenVars, St}; check_type_2({var, A, Name}, SeenVars, St) -> NewSeenVars = diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 991406bb6ebe..434af006c8b0 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -262,6 +262,9 @@ binary_type -> '<<' bin_unit_type '>>' : {type, ?anno('$1'),binary, [abstract2(0, ?anno('$1')), '$2']}. binary_type -> '<<' bin_base_type ',' bin_unit_type '>>' : {type, ?anno('$1'), binary, ['$2', '$4']}. +binary_type -> '<<' string opt_bit_type_list '>>' + : build_bin_literal_type(?anno('$1'), '$2', '$3'). +binary_type -> sigil : sigil_to_bin_type('$1'). bin_base_type -> var ':' type : build_bin_type(['$1'], '$3'). @@ -1196,6 +1199,7 @@ processed (see section [Error Information](#module-error-information)). | af_record_type() | af_remote_type() | af_singleton_integer_type() + | af_singleton_binary_type() | af_tuple_type() | af_type_union() | af_type_variable() @@ -1275,6 +1279,8 @@ processed (see section [Error Information](#module-error-information)). | af_unary_op(af_singleton_integer_type()) | af_binary_op(af_singleton_integer_type()). +-type af_singleton_binary_type() :: {'bin_type', anno(), binary()}. + -type af_literal() :: af_atom() | af_character() | af_float() @@ -1587,6 +1593,35 @@ build_bin_type([], Int) -> build_bin_type([{var, Aa, _}|_], _) -> ret_err(Aa, "Bad binary type"). +build_bin_literal_type(Anno, {string, _SAnno, Chars}, default) -> + case lists:all(fun(C) -> C >= 0 andalso C =< 255 end, Chars) of + true -> {bin_type, Anno, list_to_binary(Chars)}; + false -> ret_err(Anno, "binary literal type with characters > 255") + end; +build_bin_literal_type(Anno, {string, _, Chars}, [Enc]) + when Enc =:= utf8; Enc =:= utf16; Enc =:= utf32 -> + case unicode:characters_to_binary(Chars, unicode, Enc) of + Bin when is_binary(Bin) -> + {bin_type, Anno, Bin}; + _ -> + ret_err(Anno, "invalid characters for encoding") + end; +build_bin_literal_type(Anno, {string, _, Chars}, [latin1]) -> + build_bin_literal_type(Anno, {string, unused, Chars}, default); +build_bin_literal_type(Anno, _String, _Other) -> + ret_err(Anno, "unsupported encoding in binary literal type"). + +sigil_to_bin_type({bin, Anno, + [{bin_element, _, {string, _, Chars}, default, [utf8]}]}) -> + case unicode:characters_to_binary(Chars, unicode, utf8) of + Bin when is_binary(Bin) -> + {bin_type, Anno, Bin}; + _ -> + ret_err(Anno, "invalid characters in sigil type") + end; +sigil_to_bin_type(Node) -> + ret_err(element(2, Node), "illegal sigil prefix in type spec"). + build_atom({atom, _Aa, _Name} = Atom) -> Atom; build_atom({var, Aa, Name}) -> {atom, Aa, Name}; build_atom({record, Aa}) -> {atom, Aa, record}; @@ -2436,6 +2471,9 @@ modify_anno1({typed_record_field,Field,Type}, Ac, Mf) -> modify_anno1({Tag,A}, Ac, Mf) -> {A1,Ac1} = Mf(A, Ac), {{Tag,A1},Ac1}; +modify_anno1({bin_type, A, Value}, Ac, Mf) when is_binary(Value) -> + {A1, Ac1} = Mf(A, Ac), + {{bin_type, A1, Value}, Ac1}; modify_anno1({Tag,A,E1}, Ac, Mf) -> {A1,Ac1} = Mf(A, Ac), {E11,Ac2} = modify_anno1(E1, Ac1, Mf), diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 706699a65d43..fc5b688a99f1 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -491,6 +491,8 @@ ltype({type,_Anno,range,[_I1,_I2]=Es}, Prec) -> F = fun(E, Opts) -> lexpr(E, R, Opts) end, E = expr_list(Es, '..', F, options(none)), maybe_paren(P, Prec, E); +ltype({bin_type, _Anno, Bin}, _) -> + leaf(["<<", io_lib:write_string(binary_to_list(Bin), $"), ">>"]); ltype({type,_Anno,binary,[I1,I2]}, _) -> binary_type(I1, I2); % except binary() ltype({type,_Anno,'fun',[]}, _) -> diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index f58e48f6f07f..f918b826d763 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1291,6 +1291,65 @@ types(Config) -> {warnings,[{{1,22},erl_lint, {redefine_builtin_type,{nonempty_bitstring,0}}}]}}, + %% Singleton binary literal types. + {bin_type1, + <<"-type cmd() :: <<\"start\">> | <<\"stop\">>.\n">>, + [nowarn_unused_type], + []}, + + {bin_type2, + <<"-export([f/1]).\n" + "-spec f(<<\"hello\">> | <<\"world\">>) -> ok.\n" + "f(<<\"hello\">>) -> ok;\n" + "f(<<\"world\">>) -> ok.\n">>, + [], + []}, + + {bin_type3, + <<"-callback cb(<<\"x\">>) -> ok.\n">>, + [nowarn_unused_type], + []}, + + {bin_type4, + <<"-type empty() :: <<\"\">>.\n">>, + [nowarn_unused_type], + []}, + + {bin_type5, + <<"-record(msg, {tag :: <<\"hello\">> | <<\"world\">>}).\n" + "-export([f/0]).\n" + "f() -> #msg{tag = <<\"hello\">>}.\n">>, + [], + []}, + + %% Singleton binary literal types with encoding. + {bin_type6, + <<"-type utf8_str() :: <<\"é\"/utf8>>.\n"/utf8>>, + [nowarn_unused_type], + []}, + + {bin_type7, + <<"-type greeting() :: ~\"hello\".\n">>, + [nowarn_unused_type], + []}, + + {bin_type8, + <<"-type t() :: ~\"café\".\n"/utf8>>, + [nowarn_unused_type], + []}, + + {bin_type9, + <<"-type t() :: <<\"hello\"/utf16>>.\n">>, + [nowarn_unused_type], + []}, + + {bin_type10, + <<"-type t() :: <<\"hello\"/integer>>.\n">>, + [nowarn_unused_type], + {errors,[{{1,34},erl_parse, + "unsupported encoding in binary literal type"}], + []}}, + %% Test for bad types. {bad_export_type1, <<"-export_type(no_arity). diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index b16efd1bb386..f3a321411b12 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -930,7 +930,12 @@ type_examples() -> "f17 :: 'undefined'," "f18 :: 1 | 2 | 'undefined'," "f19 = 3 :: integer()|undefined," - "f5 = 3 :: undefined|integer()}). ">>}]. + "f5 = 3 :: undefined|integer()}). ">>}, + {ex_bin_type1,<<"-type cmd() :: <<\"start\">> | <<\"stop\">>. ">>}, + {ex_bin_type2,<<"-type bin_arg() :: <<\"hello\">>. ">>}, + {ex_bin_type3,<<"-type empty() :: <<\"\">>. ">>}, + {ex_bin_type4,<<"-type bin_utf8() :: <<\"hello\"/utf8>>. ">>}, + {ex_bin_type5,<<"-type bin_sigil() :: ~\"hello\". ">>}]. %% OTP_8473. Bugfix abstract type 'fun'. otp_8473(Config) when is_list(Config) -> diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 3af4ab7ea6d9..d62f8dd29c53 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -1137,6 +1137,10 @@ lay_2(Node, Ctxt) -> lay_type_application(Name, Arguments, Ctxt) end; + binary_literal_type -> + text("<<" ++ io_lib:write_string( + binary_to_list(erl_syntax:binary_literal_type_value(Node)), $") ++ ">>"); + bitstring_type -> Ctxt1 = set_prec(Ctxt, max_prec()), M = erl_syntax:bitstring_type_m(Node), diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 63e2381af30a..cd3bfebf36a0 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -147,6 +147,8 @@ trees. binary_generator/2, binary_generator_body/1, binary_generator_pattern/1, + binary_literal_type/1, + binary_literal_type_value/1, bitstring_type/2, bitstring_type_m/1, bitstring_type_n/1, @@ -503,6 +505,7 @@ reason `badarg`. Node types currently defined by this module are: * `attribute` * `binary` * `binary_field` +* `binary_literal_type` * `bitstring_type` * `block_expr` * `case_expr` @@ -577,7 +580,8 @@ Note: The primary constructor functions for a node type should always have the same name as the node type itself. _See also: _`annotated_type/2`, `application/3`, `arity_qualifier/2`, `atom/1`, -`attribute/2`, `binary/1`, `binary_field/2`, `bitstring_type/2`, `block_expr/1`, +`attribute/2`, `binary/1`, `binary_field/2`, `binary_literal_type/1`, +`bitstring_type/2`, `block_expr/1`, `case_expr/2`, `catch_expr/1`, `char/1`, `class_qualifier/2`, `clause/3`, `comment/2`, `conjunction/1`, `constrained_function_type/2`, `constraint/2`, `disjunction/1`, `else_expr/1`, `eof_marker/0`, `error_marker/1`, `float/1`, @@ -610,6 +614,7 @@ type(Node) -> {float, _, _} -> float; {integer, _, _} -> integer; {nil, _} -> nil; + {bin_type, _, V} when is_binary(V) -> binary_literal_type; {string, _, _} -> string; {var, _, Name} -> if Name =:= '_' -> underscore; @@ -695,6 +700,7 @@ Returns `true` if `Node` is a leaf node, otherwise `false`. The currently recognised leaf node types are: * `atom` +* `binary_literal_type` * `char` * `comment` * `eof_marker` @@ -728,6 +734,7 @@ _See also: _`is_literal/1`, `type/1`. is_leaf(Node) -> case type(Node) of atom -> true; + binary_literal_type -> true; char -> true; comment -> true; % nonstandard type eof_marker -> true; @@ -4890,6 +4897,39 @@ bitstring_type_n(Node) -> end. +-doc """ +Creates an abstract singleton binary literal type. + +The result represents `<<"Value">>`. + +_See also: _`binary_literal_type_value/1`. +""". +-spec binary_literal_type(binary()) -> syntaxTree(). + +binary_literal_type(Value) -> + tree(binary_literal_type, Value). + +revert_binary_literal_type(Node) -> + Pos = get_pos(Node), + Value = binary_literal_type_value(Node), + {bin_type, Pos, Value}. + +-doc """ +Returns the binary value of a `binary_literal_type` node. + +_See also: _`binary_literal_type/1`. +""". +-spec binary_literal_type_value(syntaxTree()) -> binary(). + +binary_literal_type_value(Node) -> + case unwrap(Node) of + {bin_type, _, Value} -> + Value; + Node1 -> + data(Node1) + end. + + -record(constrained_function_type, {body :: syntaxTree(), argument :: syntaxTree()}). @@ -7463,6 +7503,8 @@ concrete(Node) -> _ -> erlang:error({badarg, Node}) end; + binary_literal_type -> + binary_literal_type_value(Node); _ -> erlang:error({badarg, Node}) end. @@ -7508,6 +7550,8 @@ is_literal(T) -> end andalso lists:all(fun is_literal_map_field/1, map_expr_fields(T)); binary -> lists:all(fun is_literal_binary_field/1, binary_fields(T)); + binary_literal_type -> + true; _ -> false end. @@ -7607,6 +7651,8 @@ revert_root(Node) -> revert_binary_field(Node); binary_generator -> revert_binary_generator(Node); + binary_literal_type -> + revert_binary_literal_type(Node); bitstring_type -> revert_bitstring_type(Node); block_expr -> diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index aa528c293cc9..1fc8bcaeaac6 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -241,6 +241,7 @@ t_type(Config) when is_list(Config) -> (erl_syntax:integer(1), erl_syntax:integer(2))} ,{"<<_:1,_:_*2>>", erl_syntax:bitstring_type (erl_syntax:integer(1), erl_syntax:integer(2))} + ,{"<<\"hello\">>", erl_syntax:binary_literal_type(<<"hello">>)} ,{"fun()", erl_syntax:fun_type()} ]), @@ -266,6 +267,7 @@ t_type(Config) when is_list(Config) -> ,{"[atom()]", type_application, false} ,{"1..2", integer_range_type, false} ,{"<<_:1,_:_*2>>", bitstring_type, false} + ,{"<<\"hello\">>", binary_literal_type, true} ,{"fun()", fun_type, true} ,{"integer() | atom()", type_union, false} ,{"A :: fun()", annotated_type, false} @@ -273,6 +275,15 @@ t_type(Config) when is_list(Config) -> ,{"fun((integer()) -> atom())", function_type, false} ,{"V", variable, true} ]), + + %% Test concrete/1 and is_literal/1 for binary_literal_type. + BinLitNode = erl_syntax:binary_literal_type(<<"hello">>), + true = erl_syntax:is_literal(BinLitNode), + <<"hello">> = erl_syntax:concrete(BinLitNode), + EmptyBinNode = erl_syntax:binary_literal_type(<<"">>), + true = erl_syntax:is_literal(EmptyBinNode), + <<"">> = erl_syntax:concrete(EmptyBinNode), + ok. validate_basic_type({String, Tree}) -> diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl index b23acdb39e61..d38ad2305d98 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl @@ -1,3 +1,24 @@ +%% +%% %CopyrightBegin% +%% +%% SPDX-License-Identifier: Apache-2.0 +%% +%% Copyright Ericsson AB 2026. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% -module(type_specs). -include_lib("syntax_tools/include/merl.hrl"). @@ -20,7 +41,10 @@ | <<_:_*8>> | <<_:12, _:_*16>> | <<_:16, _:_*(0)>> % same as "<<_:16>>" - | <<_:16, _:_*(+0)>>. + | <<_:16, _:_*(+0)>> + | <<"foo">> + | <<"café"/utf8>> + | ~"hello". -callback cb() -> t(). diff --git a/system/doc/reference_manual/typespec.md b/system/doc/reference_manual/typespec.md index b6a5276be71b..92a9f520ada4 100644 --- a/system/doc/reference_manual/typespec.md +++ b/system/doc/reference_manual/typespec.md @@ -52,8 +52,9 @@ Predefined types represent a typically infinite set of Erlang terms that belong to this type. For example, the type `t:atom/0` denotes the set of all Erlang atoms. -For integers and atoms, it is allowed for singleton types; for example, the -integers `-1` and `42`, or the atoms `'foo'` and `'bar'`. All other types are +For integers, atoms, and binaries, it is allowed for singleton types; for +example, the integers `-1` and `42`, the atoms `'foo'` and `'bar'`, or the +binaries `<<"hello">>` and `<<"ok">>`. All other types are built using unions of either predefined types or singleton types. In a type union between a type and one of its subtypes, the subtype is absorbed by the supertype. Thus, the union is then treated as if the subtype was not a @@ -115,6 +116,13 @@ Bitstring :: <<>> | <<_:M>> %% M is an Integer_Value that evaluates to a positive integer | <<_:_*N>> %% N is an Integer_Value that evaluates to a positive integer | <<_:M, _:_*N>> + | <> %% <<"foo">>, <<"bar">>, ... + | <> %% <<"café"/utf8>>, ... + | Sigil %% ~"hello", ~b"hello", ~B"hello" + +Encoding :: utf8 | utf16 | utf32 | latin1 + +Sigil :: ~"Erlang_String" | ~b"Erlang_String" | ~B"Erlang_String" Fun :: fun() %% any function | fun((...) -> Type) %% any arity, returning Type @@ -169,6 +177,16 @@ segments of `N` bits each, where `k` is also a positive integer). The notations `<<_:_*N>>`, `<<_:M>>`, and `<<>>` are convenient shorthands for the cases that `M` or `N`, or both, are zero. +Singleton binary types denote a specific binary value. The bare form +`<<"hello">>` requires all characters to be in the range 0-255 (Latin-1). +The `<<"café"/utf8>>` form converts the string using the named encoding +(`utf8`, `utf16`, `utf32`, or `latin1`) at parse time. The sigil forms +`~"hello"`, `~b"hello"`, and `~B"hello"` encode the string as UTF-8. +All forms produce the same AST node; encoding is applied at parse time +and not preserved. Only string literals are accepted — binary +construction syntax such as `<<$h, $i>>` is not valid in type +specifications. + Because lists are commonly used, they have shorthand type notations. The types [`list(T)`](`t:list/1`) and [`nonempty_list(T)`](`t:nonempty_list/1`) have the shorthands `[T]` and `[T,...]`, respectively. The only difference between the