Skip to content

fix: seven small issues (#322, #323, #321, #324, #341, #356, #261) - #375

Merged
cmj0121 merged 8 commits into
mainfrom
fix/seven-minor
Oct 18, 2026
Merged

cmj0121 merged 8 commits into
mainfrom
fix/seven-minor

Conversation

@cmj0121

@cmj0121 cmj0121 commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Closes #322. Closes #323. Closes #321. Closes #324. Closes #341. Closes #356. Closes #261.

Seven small fixes on one branch, at the owner's request. Each issue has its own fix (or doc) commit.

Issue Change
#322 / #323 c_map_index holds an owned map and an owned key across the lookup, then gives them back. The lookup aborts on a missing key, so the hold matters. The value is taken out before the map is dropped. c_map_owned recurses, so mkn(7)["a"]["b"] owns the inner map as well.
#321 for k in mkm(1) now takes the map it was handed instead of copying it and losing the original.
#324 int(s), uint(s) and float(s) hold an owned source across the parse and release it afterwards.
#261 zrt_chan_send and zrt_chan_recv read w.done under the channel lock. sched.c's "only select" note now says that a plain op can still be woken by a token a select left standing.
#356 The tree-sitter map_literal accepts a newline after {, after each , and before }. One new conflict, which the generator asks for.
#341 memory.md (en + zh-TW) no longer says produced strings never leak. A [deviation] names #320, re-measured at one leak per discard. The README table (en + zh-TW) drops the stale eager-init() row and gains the #320 row.

Measured

  • mem-check: three new cases, map_index_owned, map_walk_owned and parse_owned. All three leak every round on a compiler built from main's emit.zg (LEAK … per round unfreed) and are clean with the fix.
  • Probes under ASan and UBSan: 11 map-index shapes, 5 for-in shapes, the parse shapes, and the abort paths under guard. All clean, with correct output, and leaks --atExit reports 0 leaks.
  • treesitter: the new corpus case codegen/map_literal_opens_a_line is RED on the old grammar (ERROR [9, 5]). With the change, all 917 files read.
  • chan: a plain send or receive reads its waiter's done flag without the channel lock #261: not reproducible, as the issue says. make -C src/runtime test, sanitize-conc and sanitize-corpus are green.
  • Builds: every intermediate fix(emit) commit builds.
  • Gates run one at a time, all OK: about 55 of them, covering every gate in ci.yml except the Linux-only ones.

Split out

#356's third Done-when item is the formatter opening print {"x": 1} across three lines. It is filed as #374.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N8jd1oK2orM6diHxhvWhC7

    `mkm(1)["a"]` read one value out of a map nothing else names and never
    dropped the map (#322), and `m[key(1)]` never released an owned `str`
    key (#323). c_map_index now holds an owned map and an owned key across
    the lookup, which aborts on a missing key. The value is taken out
    (c_taken) before the map is dropped. c_map_owned answers which maps are
    this expression's own, and it recurses, so a map read out of an owned map
    is owned too. c_index asks it before treating a map-read list as a borrow.
    c_formap copied the map it walks and gave back the copy, so the map that
    `for k in mkm(1)` was handed had no owner. An owned map (c_map_owned) is
    now taken as the loop's own; one somebody else holds is still copied.
    (#321)
    The str→number conversions passed an owned operand straight to the
    runtime parse, which keeps nothing, so `int(digits(7))` leaked the str.
    An owned source is now held across the parse, which raises on a
    malformed number, and released after it. (#324)
…l lock

    zrt_chan_send and zrt_chan_recv read `w.done` right after the park
    returned, without the lock the counterparty writes it under. A park can
    end on a wake token an earlier select left standing, while a sender on
    another worker is between taking the waiter and setting `done`. A
    lockless false then went round the loop, and a receive could overwrite
    the value just handed into *out. Both now take the lock first. sched.c's
    note that "only select" is woken early now says what it covers: only a
    select sets a token for a channel, but a plain op can still be woken by
    one. Not reproduced, as #261 says; the fix follows the reading. (#261)
    A newline after `{` is offered as a separator token because a block may
    open there, so the grammar committed to the block and `{⏎ "x": 1 ⏎}`,
    which `zerg fmt` writes, was an ERROR at the `:`. map_literal now accepts
    the same token after the `{`, after each `,` and before the `}`. The new
    conflict against _statement_separator is the one the generator asks for.
    (#356)
    The corpus gains codegen/map_literal_opens_a_line (#356), an oracle skip
    for it, and drops the index_rvalue line from sanitize-leaks, which #322
    fixed.
    Three mem-check cases. map_index_owned covers an owned map, a map read out
    of one, an owned key and a missing key under guard (#322, #323).
    map_walk_owned covers an owned map walked, broken out of, and a held map
    walked (#321). parse_owned covers an owned str parsed as int and uint, a
    malformed one under guard, and a held one (#324). All three leaked every
    round on the compiler without the fixes.
    memory.md (en and zh-TW) said produced strings no longer leak. It now
    says what is true, with a [deviation] naming #320, re-measured: the
    message of an `Err` built from a run-time str leaks once per discard. The
    README's silent-deviation table (en and zh-TW) drops the eager `init()`
    row, which its own chapter calls the rule, and gains the #320 row.
    (#341)
@cmj0121
cmj0121 merged commit dcf9487 into main Oct 18, 2026
8 checks passed
@cmj0121
cmj0121 deleted the fix/seven-minor branch October 18, 2026 02:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment