Skip to content

fix(emit): a list renders through str(…) and a hole, so assert xs == ys reports both sides - #363

Merged
cmj0121 merged 4 commits into
mainfrom
fix/359-assert-list-eq
Oct 14, 2026
Merged

cmj0121 merged 4 commits into
mainfrom
fix/359-assert-list-eq

Conversation

@cmj0121

@cmj0121 cmj0121 commented Oct 14, 2026

Copy link
Copy Markdown
Owner

Closes #359.

What

assert xs == ys over two lists builds, and a failing one reports both sides:

AssertionError: a.zg:11  assert words(1) == words(2)
  words(1) = ["1", "a, b"]
  words(2) = ["2", "a, b"]

The claim was never the problem. An assert's message is an f-string with a hole per operand, a hole lowers
through str(…), and str(…) refused any list that is not bytes or code points (E4011, "a composite has no
rendering of its own"). Every composite has rendered since the structural rendering was built — print xs and
xs.display() used it — and that arm was the one place still turning a list away. It now keeps its two
bridges and hands every other list to the same renderer.

So this is wider than the issue's title: str(xs) and f"{xs}" render a list as [1, 2], which is what
docs/runtime/format.md already said all four spellings do. E4011 retires.

Decisions made here

  • Fixed at the cause rather than at assert. The issue offered a narrower way out (keep the refusal, reword
    it for assert). The chapter says a list renders through a hole, so the refusal was the deviation.
  • The two bridges are unchanged. str(bs) and f"{bs}" over a list[byte] or list[rune] are still the
    text, while print bs shows [104, 105]. That difference predates this change; format.md now says so.

Found on the way, not fixed

  • A channel INSIDE a composite renders as its address: print [ch], f"{m}" for a map holding one, a tuple.
    Top level is E4076. This is on main today through print; this change adds the list-in-a-hole spelling to
    the ways of reaching it. Not filed yet.
  • c_renders_as_text still answers "no" for a list that does not bridge, as it does for a map, so the
    xs.format("04d") refusal says there is no rendering to fall back on when there is one.

Verified

  • Local gates, one at a time, each read: build fixpoint lint fmt-self error-codes-check seed-gaps refuse reject
    mem-check oracle corpus sanitize-corpus mem-peak examples docs-mirror docs-links docs-zerg docs-refused
    docs-repeat marker-codes chapter-codes deviation-check refusal-cites method-gaps gates treesitter dead-code
    stdlib-test compiler-test check-equal reject-fuzz fmt-corpus behaviour conformance productions suites desugar.
    refuse and mem-check also under /bin/bash.
  • The new mem-check case was red with the renderer's give-back skipped for a list (2730 per round), green
    after.
  • The corpus case leaks the message of an Err a guard caught, the class assert_claim is already listed
    for; it is listed beside it under the same ?. With the guarded asserts removed it reports no leak.

Not verified

🤖 Generated with Claude Code

https://claude.ai/code/session_01N8jd1oK2orM6diHxhvWhC7

…print`

    `str(xs)` and `f"{xs}"` over a list that is not bytes or code points
    were refused as E4011, "a composite has no rendering of its own". It
    has had one since the structural rendering was built, and `print xs`
    and `xs.display()` used it; only the `str(…)` arm still turned a list
    away, before it reached the renderer every other composite takes.

    The arm keeps its two bridges and hands every other list on. That is
    what `assert xs == ys` was refused for: its message is an f-string
    with a hole per operand, so the claim compared and could not be
    reported.

    E4011 retires: its rule, its table row (en and zh-TW) and its refuse
    case go together.

    Closes #359.
    Bumps test-data for codegen/list_in_a_hole: the four spellings over
    one list, the two bridges that stay text, and asserts over lists that
    hold and that report both sides.
…made

    A list a call answered is rendered and released, the text is the
    caller's, and a held list is read afterwards; an assert over two
    lists binds both sides. Shown red with the renderer's give-back
    skipped for a list.

    The split tests' header no longer cites the refusal that was their
    reason for going through `join`.
    package.md said a claim over two lists had to be parenthesized. It
    is claimed like any other form now. format.md gains the one place
    the four spellings differ: `str(…)` over bytes or code points is the
    way back to text and not a rendering.
@cmj0121
cmj0121 merged commit 0f79d11 into main Oct 14, 2026
8 checks passed
@cmj0121
cmj0121 deleted the fix/359-assert-list-eq branch October 14, 2026 03:21
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.

assert xs == ys over two lists is refused as E4011, a str(…) the program never wrote

1 participant