Skip to content

[fix] atom: reject cells as dict/set keys and enforce freeze on mutation#170

Merged
vt128 merged 1 commit into
masterfrom
fix/atom-frozen-guards-unhashable
Jul 13, 2026
Merged

[fix] atom: reject cells as dict/set keys and enforce freeze on mutation#170
vt128 merged 1 commit into
masterfrom
fix/atom-frozen-guards-unhashable

Conversation

@vt128

@vt128 vt128 commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

An atom cell (new_int/new_float/new_string) 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.
  • Freeze bypass. Every mutating method (set/cas/add/sub/inc/dec) stored through a frozen cell and returned success. They now error cannot <method> frozen atom_*; get, truthiness, and comparisons still work on a frozen cell.

The frozen flag is an atomic.Bool (written by Freeze, 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 … frozen case per mutator, a "frozen cells still read/compare" case, and a white-box -race freeze/mutate regression test. go test -race -count=2 ./..., go vet, gofmt -l, Docker go1.19 clean.

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>
@codacy-production

codacy-production Bot commented Jul 13, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 1 minor

Results:
1 new issue

Category Results
CodeStyle 1 minor

View in Codacy

🟢 Metrics 20 complexity · 33 duplication

Metric Results
Complexity 20
Duplication 33

View in Codacy

🟢 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

View coverage diff in Codacy

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

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.45%. Comparing base (5d4c419) to head (037815f).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vt128
vt128 merged commit 2cf4e51 into master Jul 13, 2026
21 checks passed
@vt128
vt128 deleted the fix/atom-frozen-guards-unhashable branch July 13, 2026 01:27
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