[fix] atom: reject cells as dict/set keys and enforce freeze on mutation#170
Conversation
An atom cell was both mutable and hashable, and Freeze() set a flag no method
checked — two contract violations reachable from any pure script:
- Key corruption: Hash() tracked the live value, so after
a = new_int(1); d = {a: 'x'}; a.set(2) the entry was stranded under the
stale hash and (a in d) turned False. Cells are now unhashable
("unhashable type: atom_int"), like starlark's other mutable values; key
by x.get() for a snapshot key. The vestigial dict-key lines in the default
test cases (retrieval already commented out — the very symptom) are removed,
and the dead hash helpers with them.
- Freeze bypass: every mutating method (set/cas/add/sub/inc/dec) stored
through a frozen cell and returned success. They now error with
"cannot <method> frozen atom_*"; get, truthiness, and comparisons still
work on a frozen cell.
The frozen flag is an atomic.Bool: it is written by Freeze and read by every
mutator guard, so a plain bool would be a data race against the module's
documented concurrency-safety (a race-detector regression test is added).
README updated to state the unhashable and freeze contracts (it previously
documented the stale-hash footgun as expected behavior).
Behavior changes: using a cell as a dict/set key errors instead of
corrupting; mutating a frozen cell errors instead of silently updating.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Up to standards ✅🟢 Issues
|
| Category | Results |
|---|---|
| CodeStyle | 1 minor |
🟢 Metrics 20 complexity · 33 duplication
Metric Results Complexity 20 Duplication 33
🟢 Coverage 100.00% diff coverage · +0.02% coverage variation
Metric Results Coverage variation ✅ +0.02% coverage variation (-1.00%) Diff coverage ✅ 100.00% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (5d4c419) Report Missing Report Missing Report Missing Head commit (037815f) 7779 (+6) 7363 (+7) 94.65% (+0.02%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#170) 47 47 100.00% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%1 Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #170 +/- ##
==========================================
+ Coverage 93.42% 93.45% +0.02%
==========================================
Files 50 50
Lines 6222 6233 +11
==========================================
+ Hits 5813 5825 +12
+ Misses 261 259 -2
- Partials 148 149 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
What
An
atomcell (new_int/new_float/new_string) was both mutable and hashable, andFreeze()set a flag no method checked — two contract violations reachable from any pure script.Hash()tracked the live value, so aftera = new_int(1); d = {a: 'x'}; a.set(2)the entry was stranded under the stale hash and(a in d)turned False. Cells are now unhashable (unhashable type: atom_int), like starlark's other mutable values; key byx.get()for a snapshot key.set/cas/add/sub/inc/dec) stored through a frozen cell and returned success. They now errorcannot <method> frozen atom_*;get, truthiness, and comparisons still work on a frozen cell.The
frozenflag is anatomic.Bool(written byFreeze, read by every guard), so it is race-free under the module's documented concurrency-safety. The dead hash helpers and the vestigial dict-key test lines (with their retrieval already commented out — the very symptom) are removed. README updated.Behavior change
Using a cell as a dict/set key errors instead of corrupting; mutating a frozen cell errors instead of silently updating.
Tests
Unhashable dict/set cases, a
cannot … frozencase per mutator, a "frozen cells still read/compare" case, and a white-box-racefreeze/mutate regression test.go test -race -count=2 ./...,go vet,gofmt -l, Docker go1.19 clean.