Skip to content

fix: fmt operand map literal, T: Ord over what < orders, channel buffer drop - #376

Merged
cmj0121 merged 6 commits into
mainfrom
fix/four-minor
Oct 20, 2026
Merged

cmj0121 merged 6 commits into
mainfrom
fix/four-minor

Conversation

@cmj0121

@cmj0121 cmj0121 commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Three of the four issues picked for this branch. #320 is left out; see below.

Fixes

  • zerg fmt opens a one-line map literal across three lines after print and == #374 zerg fmt: a map literal on the right of an operator, or after print, assert and the other
    keywords that take an expression, stays on one line. fmt_takes_operand states the rule by GRAMMAR's operator
    groups, asked of the parser's own predicates (moved to token.zg), so the two cannot drift. A block in the same
    positions keeps a block's layout.
  • a tuple orders under < but does not meet the bound T: Ord #369 T: Ord is met by what < orders: int, every integer width, str, and a tuple or array whose parts
    order. The bound and the < lowering ask the same c_ord_legal. A strong typedef over an integer orders as its
    integer does. A float part is still E4010 at the call.
  • a value left in a channel's buffer is never dropped #334 a channel freed with values still buffered gives them back: zrt_chan_new takes the element's drop
    (NULL for plain data) and chan_free runs it on what the ring holds. A send moves an owned copy in, so this
    is not a double free.

Tests

  • test-data (fix/four-minor): fmt/map-literal-operand.zg, codegen/generic_ord_scalar.zg,
    codegen/generic_ord_tuple.zg (oracle-skipped: the seed has no tuple <).
  • reject-check.sh: a tuple with a float part does not meet T: Ord. It is RED with the bound answering true.
  • mem-check.sh chan_left and runtime TestChannelDropsWhatItsBufferHolds. Both are RED before the fix.

All CI gates were run locally and are green.

Not in this PR

#320 stays open. An Err never owns its message: a copy is a plain struct, and runtime Errs carry C literals.
Releasing at ?? alone would turn the leak into a use-after-free (wrap(e) ?? 0; print e.msg). It needs an
ownership model for Err, which is larger than this branch.

Closes #374
Closes #369
Closes #334

🤖 Generated with Claude Code

https://claude.ai/code/session_01N8jd1oK2orM6diHxhvWhC7

    fmt_wants_value named a few value positions one token at a time, so
    `print {"x": 1}`, `print {"x": 1}.len()` and `m == {"x": 1}` had the
    literal opened across three lines like a body (#374).

    - fmt_takes_operand states the rule instead: every binary and prefix
      operator by GRAMMAR's groups, and each keyword whose production goes
      on to an expression (`print`, `assert`, `raise`, `return`, ...).
    - the groups are the parser's own predicates, moved to token.zg as
      is_add_op, is_mul_op, is_cmp_op and is_unary_op, so an operator the
      parser learns is one the formatter knows.
    - a type position (`is`, `as`, `->`), a postfix (`!`, `?`) and a body
      keyword stay off, and fmt_brace_is_map still decides by content, so
      a block after `print` or `+` keeps a block's layout.
    A send moves an owned copy into the buffer, but zrt_chan_new knew only
    the element's size, so chan_free freed the ring's bytes and kept every
    str or list a buffered value pointed at (#334).

    - zrt_chan_new takes the element's drop (a zrt_drop_fn, NULL for plain
      data) and chan_free runs it on each element still in the ring,
      oldest first from head
    - c_chan_new passes c_elem_drop_fn of the element, the same thunk a
      list element and a held send already use
    - the runtime suite's drivers pass NULL for their long channels
    The bound asked only for a declared `Ord` impl, so `least[T: Ord]` refused int, every
    integer width, `str`, and the tuples and arrays #360 taught `<` to order. `T: Eq`
    already asked `c_eq_legal`, the predicate `==` lowers by; `T: Ord` now asks
    `c_ord_legal`, the whole-type form of #360's part rule.

    - A part that orders reads through a strong typedef to its scalar, so a
      `type Age = int` orders as its int does: inside a tuple as it already did alone,
      per docs/core/types.md's "an operator `Y` has, `X` has".
    - A `float`, a list and a tuple holding either are still E4010 at the call.
    The submodule takes the fmt case for #374 and the two codegen cases for #369.
    The bound widened to what `<` orders, and this case holds the edge of it: shown RED with
    the `Ord` bound answering true, when the call built and the body's `<` reported E3044
    in a template the caller never wrote.
    - mem-check's chan_left leaves a called str, a named str's copy and a
      list in buffered channels each round, beside an int channel that owes
      nothing; it counted three live allocations a round before the fix
    - the runtime suite frees a channel whose ring has wrapped and asserts
      each buffered element is dropped once, oldest first
@cmj0121
cmj0121 merged commit f144770 into main Oct 20, 2026
8 checks passed
@cmj0121
cmj0121 deleted the fix/four-minor branch October 20, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant