Skip to content

Fix maps:from_keys/2 dedup for boxed terms (tuples, etc.) - #2393

Open
CoderDennis wants to merge 1 commit into
atomvm:mainfrom
CoderDennis:fix-maps-from-keys-boxed-term-dedup
Open

Fix maps:from_keys/2 dedup for boxed terms (tuples, etc.)#2393
CoderDennis wants to merge 1 commit into
atomvm:mainfrom
CoderDennis:fix-maps-from-keys-boxed-term-dedup

Conversation

@CoderDennis

@CoderDennis CoderDennis commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Claude and I hit this bug when building what I thought would be a simple Conway's Game of Life on an ESP32 device with a small touch screen.

Expected behavior

maps:from_keys(List, Value) and sets:from_list(List) should deduplicate List using
structural equality — the same semantics as OTP, and as AtomVM's own term_find_map_pos. Five
separately-allocated {1, 1} tuples should collapse to size 1.

Actual behavior

On AtomVM, five separately-allocated but structurally-identical {1, 1} tuples produce a
map/set of size 5 — no deduplication happens for boxed terms. Immediates (small integers,
atoms) are unaffected.

sort_keys_uniq's dedup phase compared keys with raw pointer != after sorting them with real structural comparison. For immediates (small integers, atoms), the tagged word is the value, so != happened to work. For boxed terms like tuples, the word is a tagged pointer, so two separately allocated but structurally equal keys were never deduplicated, even though the preceding sort placed them adjacent via correct structural comparison.

sets:from_list/1 hits the same bug since it's built on top of maps:from_keys/2.

Fix

Use term_compare/4 in the dedup phase instead, mirroring the existing pattern in term_find_map_pos.

Add a regression test to maps_nifs.erl: build five separately heap-allocated {1, 1} tuples and confirm maps:from_keys/2 collapses them to a map of size 1.

sort_keys_uniq's dedup phase compared keys with raw pointer `!=`
after sorting them with real structural comparison. For immediates
(small integers, atoms) the tagged word *is* the value, so `!=`
happened to work. For boxed terms (tuples, bignums, ...) the word is
a tagged pointer, so two separately-allocated but structurally equal
keys were never deduplicated, even though the preceding sort placed
them adjacent via correct structural comparison.

sets:from_list/1 hits the same bug since it's built on top of
maps:from_keys/2.

Use term_compare/4 in the dedup phase instead, mirroring the existing
pattern in term_find_map_pos.

Add a regression test to maps_nifs.erl: build five separately
heap-allocated {1, 1} tuples and confirm maps:from_keys/2 collapses
them to a map of size 1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016go5qvVW72PrRnvPTnYkq9
Signed-off-by: Dennis Palmer <dodava@gmail.com>
@CoderDennis
CoderDennis force-pushed the fix-maps-from-keys-boxed-term-dedup branch from 5ee856a to 735ff79 Compare September 6, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant