From dfc8d4de2bae54e667455363f2e0b1465bcc7a81 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sat, 7 Mar 2026 19:25:54 -0300 Subject: [PATCH 01/13] Add singleton binary literal types to type specifications Erlang's type system supports singleton types for atoms ('foo') and integers (42), but not for binaries. This adds Phase 1 support for binary literal types like <<"hello">> in -type, -spec, and -callback declarations. In this phase, Dialyzer treats <<"foo">> structurally as <<_:24>> (by bit size). Full value tracking is planned for Phase 2. Changes: - Parser: grammar rule for `<< string >>` producing {bin_type, Anno, Binary} - Linter: accept {bin_type, _, Value} in type positions - Pretty printer: render bin_type back as <<"...">> - Dialyzer: from_form maps to t_bitstr(0, BitSize), t_form_to_string - Syntax tools: binary_literal_type node type with constructor/accessor/revert - Documentation: abstract format and type spec reference updated - Tests: erl_lint, erl_pp, syntax_tools, and dialyzer test data --- erts/doc/guides/absform.md | 2 + lib/dialyzer/src/erl_types.erl | 4 ++ .../test/small_SUITE_data/results/bin_type | 2 + .../test/small_SUITE_data/src/bin_type.erl | 12 +++++ lib/stdlib/src/erl_lint.erl | 2 + lib/stdlib/src/erl_parse.yrl | 13 ++++++ lib/stdlib/src/erl_pp.erl | 2 + lib/stdlib/test/erl_lint_SUITE.erl | 19 ++++++++ lib/stdlib/test/erl_pp_SUITE.erl | 4 +- lib/syntax_tools/src/erl_syntax.erl | 44 ++++++++++++++++++- lib/syntax_tools/test/syntax_tools_SUITE.erl | 2 + .../syntax_tools_SUITE_data/type_specs.erl | 3 +- system/doc/reference_manual/typespec.md | 6 ++- 13 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 lib/dialyzer/test/small_SUITE_data/results/bin_type create mode 100644 lib/dialyzer/test/small_SUITE_data/src/bin_type.erl 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..efd98c112c5d 100644 --- a/lib/dialyzer/src/erl_types.erl +++ b/lib/dialyzer/src/erl_types.erl @@ -4391,6 +4391,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_bitstr(0, bit_size(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) -> @@ -5115,6 +5117,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 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..ab54797b0c6d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/bin_type @@ -0,0 +1,2 @@ +%% SPDX-License-Identifier: Apache-2.0 +%% SPDX-FileCopyrightText: 2026 Erlang/OTP and its contributors 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..d5d5be8fba59 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl @@ -0,0 +1,12 @@ +-module(bin_type). + +-export([handle/1, status/0]). + +-type cmd() :: <<"start">> | <<"stop">>. + +-spec handle(cmd()) -> ok. +handle(<<"start">>) -> ok; +handle(<<"stop">>) -> ok. + +-spec status() -> <<"ok">> | <<"error">>. +status() -> <<"ok">>. 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..0a9e9321351f 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -262,6 +262,7 @@ 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 '>>' : build_bin_literal_type(?anno('$1'), '$2'). bin_base_type -> var ':' type : build_bin_type(['$1'], '$3'). @@ -1196,6 +1197,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 +1277,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 +1591,12 @@ build_bin_type([], Int) -> build_bin_type([{var, Aa, _}|_], _) -> ret_err(Aa, "Bad binary type"). +build_bin_literal_type(Anno, {string, _SAnno, Chars}) -> + 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_atom({atom, _Aa, _Name} = Atom) -> Atom; build_atom({var, Aa, Name}) -> {atom, Aa, Name}; build_atom({record, Aa}) -> {atom, Aa, record}; @@ -2436,6 +2446,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..9d9114d1c190 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1291,6 +1291,25 @@ 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], + []}, + %% 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..cf8eb3d5ff51 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -930,7 +930,9 @@ 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,<<"-spec f(<<\"hello\">>) -> ok. ">>}]. %% OTP_8473. Bugfix abstract type 'fun'. otp_8473(Config) when is_list(Config) -> diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 63e2381af30a..9ce0f5a86d13 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()}). @@ -7607,6 +7647,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..c5bcf5b5cbb2 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} 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..42551bb493b9 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 @@ -20,7 +20,8 @@ | <<_:_*8>> | <<_:12, _:_*16>> | <<_:16, _:_*(0)>> % same as "<<_:16>>" - | <<_:16, _:_*(+0)>>. + | <<_:16, _:_*(+0)>> + | <<"foo">>. -callback cb() -> t(). diff --git a/system/doc/reference_manual/typespec.md b/system/doc/reference_manual/typespec.md index b6a5276be71b..07f6310bee60 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,7 @@ 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>> + | Erlang_Binary_String %% <<"foo">>, <<"bar">>, ... Fun :: fun() %% any function | fun((...) -> Type) %% any arity, returning Type From 4dfb1937e455c9cecda8c4c64abcdc0aebe77e52 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sat, 7 Mar 2026 19:58:31 -0300 Subject: [PATCH 02/13] Add Dialyzer value tracking for singleton binary literal types Extend Dialyzer to track binary literal values, not just bit sizes. <<"foo">> is now a distinct tracked value rather than just <<_:24>>, enabling type narrowing, map key tracking, and set operations. Uses the qualifier field of #c{} (unused for binary types) to store an ordset of known binary values, following the same pattern as atom and integer singleton sets. Changes in erl_types.erl: - ?bitstr_vals macro and t_binary_val/1 constructor - from_form uses t_binary_val for value-level precision - t_from_term tracks binary values (not just bitstring sizes) - t_sup_aux: union of binary value sets with SET_LIMIT widening - t_inf_aux: intersection with value sets and structural types - t_subtract_aux: element-wise subtraction of value sets - is_singleton_type: recognizes single-value binary sets - separate_key: expands binary value sets for map key tracking - t_to_string/t_elements: render and expand value sets --- lib/dialyzer/src/erl_types.erl | 66 ++++++++++++++++++- .../test/small_SUITE_data/src/bin_type.erl | 5 +- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/lib/dialyzer/src/erl_types.erl b/lib/dialyzer/src/erl_types.erl index efd98c112c5d..56d994597df1 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 -> ?bitstr_vals(0, bit_size(hd(NewVals)), 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; + _ -> ?bitstr_vals(0, bit_size(hd(Filtered)), 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,13 @@ 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. + %%----------------------------------------------------------------------------- %% Substitution of variables %% @@ -3403,6 +3447,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 +4023,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) -> @@ -4392,7 +4445,7 @@ 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_bitstr(0, bit_size(Bin)), 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) -> @@ -4877,6 +4930,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) -> @@ -5401,6 +5456,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. @@ -5474,6 +5531,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)). @@ -5618,6 +5678,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/small_SUITE_data/src/bin_type.erl b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl index d5d5be8fba59..ac1db2966880 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl @@ -1,6 +1,6 @@ -module(bin_type). --export([handle/1, status/0]). +-export([handle/1, status/0, map_key/1]). -type cmd() :: <<"start">> | <<"stop">>. @@ -10,3 +10,6 @@ handle(<<"stop">>) -> ok. -spec status() -> <<"ok">> | <<"error">>. status() -> <<"ok">>. + +-spec map_key(#{<<"name">> := binary()}) -> binary(). +map_key(#{<<"name">> := V}) -> V. From 8afabe71b323b9a8cff1e7a428d63d57179eb10c Mon Sep 17 00:00:00 2001 From: williamthome Date: Sat, 7 Mar 2026 21:13:47 -0300 Subject: [PATCH 03/13] Add tests for bin_type handling in get_modules_mentioned Add a unit test in erl_types_SUITE that directly exercises type_form_to_remote_modules with {bin_type, _, _} forms, verifying that binary literal types are correctly treated as leaf types with no module dependencies. Also extend the small_SUITE bin_type integration test with a type that combines a binary literal and a remote type reference (<<"hello">> | binary:part()), exercising the full Dialyzer pipeline through get_modules_mentioned. --- lib/dialyzer/test/erl_types_SUITE.erl | 25 +++++++++++++++++++ .../test/small_SUITE_data/src/bin_type.erl | 9 ++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/dialyzer/test/erl_types_SUITE.erl b/lib/dialyzer/test/erl_types_SUITE.erl index 5674ffd1cbdf..cba5f35b979e 100644 --- a/lib/dialyzer/test/erl_types_SUITE.erl +++ b/lib/dialyzer/test/erl_types_SUITE.erl @@ -16,6 +16,7 @@ -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, absorption/1,associativity/1,commutativity/1,idempotence/1, identity/1,limit/1 ]). @@ -29,6 +30,7 @@ all() -> [consistency_and_to_string, misc, map_multiple_representations, + bin_type_modules_mentioned, {group,property_tests} ]. @@ -357,6 +359,29 @@ 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. + 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/small_SUITE_data/src/bin_type.erl b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl index ac1db2966880..2303da9b4a68 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl @@ -1,6 +1,6 @@ -module(bin_type). --export([handle/1, status/0, map_key/1]). +-export([handle/1, status/0, map_key/1, mixed/1]). -type cmd() :: <<"start">> | <<"stop">>. @@ -13,3 +13,10 @@ 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">>. From 7a705f37694dcd4ff43653208d5df7845a91be74 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sat, 7 Mar 2026 21:51:54 -0300 Subject: [PATCH 04/13] Fix syntax_tools bugs and add tests for singleton binary literal types Fix missing binary_literal_type clauses in erl_prettypr:lay_2/2, erl_syntax:concrete/1, and erl_syntax:is_literal/1 that would crash or return wrong results for binary literal type nodes. Add comprehensive tests for binary val type operations in erl_types, empty binary edge cases in erl_pp and erl_lint, and concrete/is_literal coverage in syntax_tools. --- lib/dialyzer/test/erl_types_SUITE.erl | 54 +++++++++++++++++++- lib/stdlib/test/erl_lint_SUITE.erl | 5 ++ lib/stdlib/test/erl_pp_SUITE.erl | 3 +- lib/syntax_tools/src/erl_prettypr.erl | 4 ++ lib/syntax_tools/src/erl_syntax.erl | 4 ++ lib/syntax_tools/test/syntax_tools_SUITE.erl | 9 ++++ 6 files changed, 77 insertions(+), 2 deletions(-) diff --git a/lib/dialyzer/test/erl_types_SUITE.erl b/lib/dialyzer/test/erl_types_SUITE.erl index cba5f35b979e..6be5b2c14683 100644 --- a/lib/dialyzer/test/erl_types_SUITE.erl +++ b/lib/dialyzer/test/erl_types_SUITE.erl @@ -16,7 +16,7 @@ -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_modules_mentioned/1,bin_type_operations/1, absorption/1,associativity/1,commutativity/1,idempotence/1, identity/1,limit/1 ]). @@ -31,6 +31,7 @@ all() -> misc, map_multiple_representations, bin_type_modules_mentioned, + bin_type_operations, {group,property_tests} ]. @@ -382,6 +383,57 @@ bin_type_modules_mentioned(_Config) -> 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), + + 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/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 9d9114d1c190..7730a0703ff0 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1310,6 +1310,11 @@ types(Config) -> [nowarn_unused_type], []}, + {bin_type4, + <<"-type empty() :: <<\"\">>.\n">>, + [nowarn_unused_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 cf8eb3d5ff51..cd4eeb6fd36d 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -932,7 +932,8 @@ type_examples() -> "f19 = 3 :: integer()|undefined," "f5 = 3 :: undefined|integer()}). ">>}, {ex_bin_type1,<<"-type cmd() :: <<\"start\">> | <<\"stop\">>. ">>}, - {ex_bin_type2,<<"-spec f(<<\"hello\">>) -> ok. ">>}]. + {ex_bin_type2,<<"-spec f(<<\"hello\">>) -> ok. ">>}, + {ex_bin_type3,<<"-type empty() :: <<\"\">>. ">>}]. %% 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 9ce0f5a86d13..892b9fa5201d 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -7503,6 +7503,8 @@ concrete(Node) -> _ -> erlang:error({badarg, Node}) end; + binary_literal_type -> + binary_literal_type_value(Node); _ -> erlang:error({badarg, Node}) end. @@ -7548,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. diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl index c5bcf5b5cbb2..1fc8bcaeaac6 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl @@ -275,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}) -> From ce9ef5c2b6599f9e38304d4ded52ee1f37239e41 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sat, 7 Mar 2026 22:37:31 -0300 Subject: [PATCH 05/13] Fix check_record_fields crash for bin_type in record field types Dialyzer's check_record_fields/3 was missing a clause for {bin_type, _, _} forms, causing a function_clause crash when analyzing records with binary literal type fields (e.g., -record(msg, {tag :: <<"hello">>})). --- lib/dialyzer/src/erl_types.erl | 1 + lib/dialyzer/test/small_SUITE_data/src/bin_type.erl | 7 ++++++- lib/stdlib/test/erl_lint_SUITE.erl | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/dialyzer/src/erl_types.erl b/lib/dialyzer/src/erl_types.erl index 56d994597df1..31448fee33b3 100644 --- a/lib/dialyzer/src/erl_types.erl +++ b/lib/dialyzer/src/erl_types.erl @@ -5029,6 +5029,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; diff --git a/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl index 2303da9b4a68..8275597c8c46 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl @@ -1,6 +1,8 @@ -module(bin_type). --export([handle/1, status/0, map_key/1, mixed/1]). +-export([handle/1, status/0, map_key/1, mixed/1, make_msg/1]). + +-record(msg, {tag :: <<"hello">> | <<"world">>, payload :: binary()}). -type cmd() :: <<"start">> | <<"stop">>. @@ -20,3 +22,6 @@ map_key(#{<<"name">> := V}) -> V. -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/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 7730a0703ff0..66aa84ddd78f 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1315,6 +1315,13 @@ types(Config) -> [nowarn_unused_type], []}, + {bin_type5, + <<"-record(msg, {tag :: <<\"hello\">> | <<\"world\">>}).\n" + "-export([f/0]).\n" + "f() -> #msg{tag = <<\"hello\">>}.\n">>, + [], + []}, + %% Test for bad types. {bad_export_type1, <<"-export_type(no_arity). From e7038191e04476f9a134a3de95335def9f60a554 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sat, 7 Mar 2026 23:49:42 -0300 Subject: [PATCH 06/13] Add UTF-8 encoding and sigil support for singleton binary literal types Support <<"..."/utf8>>, <<"..."/utf16>>, <<"..."/utf32>>, <<"..."/latin1>>, and ~"..." sigil syntax in type specifications. Only the parser needed changes since {bin_type, Anno, Bin} stores the final binary value. --- lib/dialyzer/test/erl_types_SUITE.erl | 5 ++++ lib/stdlib/src/erl_parse.yrl | 39 +++++++++++++++++++++++++++ lib/stdlib/test/erl_lint_SUITE.erl | 28 +++++++++++++++++++ lib/stdlib/test/erl_pp_SUITE.erl | 5 +++- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/dialyzer/test/erl_types_SUITE.erl b/lib/dialyzer/test/erl_types_SUITE.erl index 6be5b2c14683..0dad4986a0a0 100644 --- a/lib/dialyzer/test/erl_types_SUITE.erl +++ b/lib/dialyzer/test/erl_types_SUITE.erl @@ -432,6 +432,11 @@ bin_type_operations(_Config) -> [_] = ?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) -> diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 0a9e9321351f..cf8221edf473 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -263,6 +263,11 @@ binary_type -> '<<' bin_unit_type '>>' : {type, ?anno('$1'),binary, binary_type -> '<<' bin_base_type ',' bin_unit_type '>>' : {type, ?anno('$1'), binary, ['$2', '$4']}. binary_type -> '<<' string '>>' : build_bin_literal_type(?anno('$1'), '$2'). +binary_type -> '<<' string '/' atom '>>' + : build_bin_literal_type(?anno('$1'), '$2', '$4'). +binary_type -> sigil_prefix string sigil_suffix + : build_bin_literal_type_sigil(?anno('$1'), + '$1', '$2', '$3'). bin_base_type -> var ':' type : build_bin_type(['$1'], '$3'). @@ -1597,6 +1602,40 @@ build_bin_literal_type(Anno, {string, _SAnno, Chars}) -> false -> ret_err(Anno, "binary literal type with characters > 255") end. +build_bin_literal_type(Anno, {string, _, Chars}, {atom, _, 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}, {atom, _, latin1}) -> + build_bin_literal_type(Anno, {string, unused, Chars}); +build_bin_literal_type(Anno, _String, {atom, _, _}) -> + ret_err(Anno, "unsupported encoding in binary literal type"). + +build_bin_literal_type_sigil(Anno, SigilPrefix, {string, _, Chars}, + SigilSuffix) -> + Type = element(3, SigilPrefix), + Suffix = element(3, SigilSuffix), + if + (Type =:= '' orelse Type =:= 'b' orelse Type =:= 'B'), + Suffix =:= "" -> + 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; + Type =:= '' orelse Type =:= 'b' orelse Type =:= 'B' -> + ret_err(element(2, SigilSuffix), + "illegal sigil suffix in type spec"); + true -> + ret_err(element(2, SigilPrefix), + "illegal sigil prefix in type spec") + end. + build_atom({atom, _Aa, _Name} = Atom) -> Atom; build_atom({var, Aa, Name}) -> {atom, Aa, Name}; build_atom({record, Aa}) -> {atom, Aa, record}; diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 66aa84ddd78f..f918b826d763 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1322,6 +1322,34 @@ types(Config) -> [], []}, + %% 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 cd4eeb6fd36d..3a6e0f108994 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -933,7 +933,10 @@ type_examples() -> "f5 = 3 :: undefined|integer()}). ">>}, {ex_bin_type1,<<"-type cmd() :: <<\"start\">> | <<\"stop\">>. ">>}, {ex_bin_type2,<<"-spec f(<<\"hello\">>) -> ok. ">>}, - {ex_bin_type3,<<"-type empty() :: <<\"\">>. ">>}]. + {ex_bin_type3,<<"-type empty() :: <<\"\">>. ">>}, + {ex_bin_type4,<<"-type t() :: <<\"hello\"/utf8>>. ">>}, + {ex_bin_type5,<<"-type t() :: ~\"hello\". ">>}, + {ex_bin_type6,<<"-type t() :: ~\"caf", 233, "\". ">>}]. %% OTP_8473. Bugfix abstract type 'fun'. otp_8473(Config) when is_list(Config) -> From 9e47709e822a3495c73cacbb42c079823be65b6e Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 00:28:01 -0300 Subject: [PATCH 07/13] Refactor parser grammar for singleton binary literal types Reuse existing non-terminals (opt_bit_type_list, sigil) instead of duplicating grammar structure. Reduces 3 rules to 2 and unifies builder functions, while producing identical AST output. --- lib/stdlib/src/erl_parse.yrl | 52 +++++++++++++----------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index cf8221edf473..434af006c8b0 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -262,12 +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 '>>' : build_bin_literal_type(?anno('$1'), '$2'). -binary_type -> '<<' string '/' atom '>>' - : build_bin_literal_type(?anno('$1'), '$2', '$4'). -binary_type -> sigil_prefix string sigil_suffix - : build_bin_literal_type_sigil(?anno('$1'), - '$1', '$2', '$3'). +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'). @@ -1596,13 +1593,12 @@ build_bin_type([], Int) -> build_bin_type([{var, Aa, _}|_], _) -> ret_err(Aa, "Bad binary type"). -build_bin_literal_type(Anno, {string, _SAnno, Chars}) -> +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}, {atom, _, Enc}) + 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) -> @@ -1610,31 +1606,21 @@ build_bin_literal_type(Anno, {string, _, Chars}, {atom, _, Enc}) _ -> ret_err(Anno, "invalid characters for encoding") end; -build_bin_literal_type(Anno, {string, _, Chars}, {atom, _, latin1}) -> - build_bin_literal_type(Anno, {string, unused, Chars}); -build_bin_literal_type(Anno, _String, {atom, _, _}) -> +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"). -build_bin_literal_type_sigil(Anno, SigilPrefix, {string, _, Chars}, - SigilSuffix) -> - Type = element(3, SigilPrefix), - Suffix = element(3, SigilSuffix), - if - (Type =:= '' orelse Type =:= 'b' orelse Type =:= 'B'), - Suffix =:= "" -> - 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; - Type =:= '' orelse Type =:= 'b' orelse Type =:= 'B' -> - ret_err(element(2, SigilSuffix), - "illegal sigil suffix in type spec"); - true -> - ret_err(element(2, SigilPrefix), - "illegal sigil prefix in type spec") - end. +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}; From 8ded6b987ca3ed8cc301ca86e26ac765bbe7d3d6 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 01:33:11 -0300 Subject: [PATCH 08/13] Update docs and test data for encoding and sigil binary literal types --- .../syntax_tools_SUITE_data/type_specs.erl | 4 +++- system/doc/reference_manual/typespec.md | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) 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 42551bb493b9..5f673f516015 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 @@ -21,7 +21,9 @@ | <<_:12, _:_*16>> | <<_:16, _:_*(0)>> % same as "<<_:16>>" | <<_:16, _:_*(+0)>> - | <<"foo">>. + | <<"foo">> + | <<"café"/utf8>> + | ~"hello". -callback cb() -> t(). diff --git a/system/doc/reference_manual/typespec.md b/system/doc/reference_manual/typespec.md index 07f6310bee60..92a9f520ada4 100644 --- a/system/doc/reference_manual/typespec.md +++ b/system/doc/reference_manual/typespec.md @@ -116,7 +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>> - | Erlang_Binary_String %% <<"foo">>, <<"bar">>, ... + | <> %% <<"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 @@ -171,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 From 06817a170b21cbcb630f8f0a4781ec4bfca537e1 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 01:43:00 -0300 Subject: [PATCH 09/13] Add license headers to files missing them --- lib/dialyzer/test/erl_types_SUITE.erl | 8 +++++++ .../test/small_SUITE_data/results/bin_type | 20 +++++++++++++++++- .../test/small_SUITE_data/src/bin_type.erl | 21 +++++++++++++++++++ .../syntax_tools_SUITE_data/type_specs.erl | 21 +++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/lib/dialyzer/test/erl_types_SUITE.erl b/lib/dialyzer/test/erl_types_SUITE.erl index 0dad4986a0a0..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,6 +18,8 @@ %% 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, diff --git a/lib/dialyzer/test/small_SUITE_data/results/bin_type b/lib/dialyzer/test/small_SUITE_data/results/bin_type index ab54797b0c6d..8db89a0b527f 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/bin_type +++ b/lib/dialyzer/test/small_SUITE_data/results/bin_type @@ -1,2 +1,20 @@ +%% +%% %CopyrightBegin% +%% %% SPDX-License-Identifier: Apache-2.0 -%% SPDX-FileCopyrightText: 2026 Erlang/OTP and its contributors +%% +%% 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/src/bin_type.erl b/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl index 8275597c8c46..d972617310ab 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/bin_type.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/bin_type.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(bin_type). -export([handle/1, status/0, map_key/1, mixed/1, make_msg/1]). 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 5f673f516015..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"). From d626d65bc9b3c5d5c3e28a787c83e64c348185fe Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 01:51:35 -0300 Subject: [PATCH 10/13] Fix tab indentation to spaces in added lines --- lib/dialyzer/src/erl_types.erl | 2 +- lib/syntax_tools/src/erl_syntax.erl | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/dialyzer/src/erl_types.erl b/lib/dialyzer/src/erl_types.erl index 31448fee33b3..331d47c1f756 100644 --- a/lib/dialyzer/src/erl_types.erl +++ b/lib/dialyzer/src/erl_types.erl @@ -51,7 +51,7 @@ t_atoms/1, t_atom_vals/1, t_binary/0, - t_binary_val/1, + t_binary_val/1, t_bitstr/0, t_bitstr/2, t_bitstr_base/1, diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 892b9fa5201d..cd3bfebf36a0 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -147,8 +147,8 @@ trees. binary_generator/2, binary_generator_body/1, binary_generator_pattern/1, - binary_literal_type/1, - binary_literal_type_value/1, + binary_literal_type/1, + binary_literal_type_value/1, bitstring_type/2, bitstring_type_m/1, bitstring_type_n/1, @@ -614,7 +614,7 @@ type(Node) -> {float, _, _} -> float; {integer, _, _} -> integer; {nil, _} -> nil; - {bin_type, _, V} when is_binary(V) -> binary_literal_type; + {bin_type, _, V} when is_binary(V) -> binary_literal_type; {string, _, _} -> string; {var, _, Name} -> if Name =:= '_' -> underscore; @@ -734,7 +734,7 @@ _See also: _`is_literal/1`, `type/1`. is_leaf(Node) -> case type(Node) of atom -> true; - binary_literal_type -> true; + binary_literal_type -> true; char -> true; comment -> true; % nonstandard type eof_marker -> true; @@ -7550,8 +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; + binary_literal_type -> + true; _ -> false end. From 3c4f1c4c5f9bce64d1711f11a1dfa98fb4d03b9b Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 09:16:15 -0300 Subject: [PATCH 11/13] Update Dialyzer test expected results for singleton binary literal types The t_from_term change now tracks exact binary values (like atoms), producing more precise type names in Dialyzer output: - <<>> becomes <<"">> - <<_:8>> becomes <<"f">> or <<"*">> for known values - <<_:16>> becomes <<"hi">> for known values - Binary map keys show as singleton values (e.g., <<"50">>) Also fix make_bitstr_vals to properly compute Unit/Base from filtered values after intersection, rather than assuming uniform sizes. --- lib/dialyzer/src/erl_types.erl | 10 +- .../test/indent_SUITE_data/results/map_galore | 208 +++++++++++++++--- .../test/map_SUITE_data/results/map_galore | 16 +- .../small_SUITE_data/results/binary_nonempty | 10 +- .../results/relevant_record_warning | 2 +- .../test/user_SUITE_data/results/gh6580 | 8 +- 6 files changed, 205 insertions(+), 49 deletions(-) diff --git a/lib/dialyzer/src/erl_types.erl b/lib/dialyzer/src/erl_types.erl index 331d47c1f756..5dc1416f57ce 100644 --- a/lib/dialyzer/src/erl_types.erl +++ b/lib/dialyzer/src/erl_types.erl @@ -2711,14 +2711,14 @@ 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 -> ?bitstr_vals(0, bit_size(hd(NewVals)), NewVals) + 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; - _ -> ?bitstr_vals(0, bit_size(hd(Filtered)), Filtered) + _ -> make_bitstr_vals(Filtered) end; t_inf_aux(?bitstr(U1, B1), ?bitstr_vals(U2, B2, Vals)) when is_list(Vals) -> @@ -3193,6 +3193,12 @@ bitstr_val_matches(Bin, Unit, 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 %% 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/binary_nonempty b/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty index dbfaf63d6e73..ab7018c00e7f 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty +++ b/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty @@ -2,19 +2,19 @@ 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 +22,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 +32,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..c72e33a992a0 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,3 @@ 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/user_SUITE_data/results/gh6580 b/lib/dialyzer/test/user_SUITE_data/results/gh6580 index 6060365082ce..38c3fe9d059e 100644 --- a/lib/dialyzer/test/user_SUITE_data/results/gh6580 +++ b/lib/dialyzer/test/user_SUITE_data/results/gh6580 @@ -1,7 +1,7 @@ -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()) From 8851eef45d78de82e89f39dd24a1a8a2c67f4111 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 09:26:22 -0300 Subject: [PATCH 12/13] Add license headers to Dialyzer test results files The CI license-header check requires headers on modified files. The test comparator skips %-prefixed lines, so headers are safe. --- .../small_SUITE_data/results/binary_nonempty | 20 +++++++++++++++++++ .../results/relevant_record_warning | 20 +++++++++++++++++++ .../test/user_SUITE_data/results/gh6580 | 20 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty b/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty index ab7018c00e7f..f7b33c4ce885 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty +++ b/lib/dialyzer/test/small_SUITE_data/results/binary_nonempty @@ -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% 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' 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 c72e33a992a0..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::<<"*">>} violates the declared type of field field::'binary' diff --git a/lib/dialyzer/test/user_SUITE_data/results/gh6580 b/lib/dialyzer/test/user_SUITE_data/results/gh6580 index 38c3fe9d059e..7fdf79905815 100644 --- a/lib/dialyzer/test/user_SUITE_data/results/gh6580 +++ b/lib/dialyzer/test/user_SUITE_data/results/gh6580 @@ -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% gh6580.erl:11:21: The pattern <[_ | _], _> can never match the type <[],<<"">>> gh6580.erl:5:1: Function f/0 has no local return From eeaacb50ea88d199f812acbf8061176b9436a1d2 Mon Sep 17 00:00:00 2001 From: williamthome Date: Sun, 8 Mar 2026 11:05:51 -0300 Subject: [PATCH 13/13] Fix type_examples in erl_pp_SUITE for singleton binary literal types --- lib/stdlib/test/erl_pp_SUITE.erl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 3a6e0f108994..f3a321411b12 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -932,11 +932,10 @@ type_examples() -> "f19 = 3 :: integer()|undefined," "f5 = 3 :: undefined|integer()}). ">>}, {ex_bin_type1,<<"-type cmd() :: <<\"start\">> | <<\"stop\">>. ">>}, - {ex_bin_type2,<<"-spec f(<<\"hello\">>) -> ok. ">>}, + {ex_bin_type2,<<"-type bin_arg() :: <<\"hello\">>. ">>}, {ex_bin_type3,<<"-type empty() :: <<\"\">>. ">>}, - {ex_bin_type4,<<"-type t() :: <<\"hello\"/utf8>>. ">>}, - {ex_bin_type5,<<"-type t() :: ~\"hello\". ">>}, - {ex_bin_type6,<<"-type t() :: ~\"caf", 233, "\". ">>}]. + {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) ->