Skip to content

Implement singleton binary literal types#10816

Open
williamthome wants to merge 13 commits intoerlang:masterfrom
williamthome:singleton-binary-literal-types
Open

Implement singleton binary literal types#10816
williamthome wants to merge 13 commits intoerlang:masterfrom
williamthome:singleton-binary-literal-types

Conversation

@williamthome
Copy link
Contributor

This is an implementation of singleton binary literal types as described in erlang/eep#84.

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
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
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.
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.
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">>})).
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.
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.
@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

CT Test Results

    6 files    257 suites   2h 8m 27s ⏱️
4 202 tests 4 101 ✅ 101 💤 0 ❌
5 371 runs  5 245 ✅ 126 💤 0 ❌

Results for commit eeaacb5.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

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.
The CI license-header check requires headers on modified files.
The test comparator skips %-prefixed lines, so headers are safe.
@IngelaAndin IngelaAndin added the team:VM Assigned to OTP team VM label Mar 9, 2026
@bjorng bjorng added the team:LG Assigned to OTP language group label Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team:LG Assigned to OTP language group team:VM Assigned to OTP team VM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants