Conversation
The lexer strips the surrounding quotes and stores the raw text, tagging
the node `extra_kind == "string"`. Zig reads that tag, Verilog reads it,
the typechecker reads it. The C and Rust emitters never did:
let s = "hello world" -> __auto_type s = hello world;
-> let s = hello world;
const NAME : str = "trinity" -> #define NAME trinity
-> pub const NAME: String = trinity;
The #define is the worst of the three, because it is VALID C. A macro
whose body is a bare word lands in the output, the file compiles past it,
and it fails wherever the name is used -- or expands to something else.
The other two are honest compile errors.
Two call sites in C, and the second is what a partial fix leaves behind:
`c_literal` takes a &str and so cannot see a tag that lives on the node,
so repairing the expression arm alone leaves every `#define` wrong. Both
are fixed; the second has a test that fails on a tree where only the
first is.
Measured per spec, both directions, over a 665-spec snapshot, 589
generating, 0 timeouts:
cc -fsyntax-only -std=gnu11 268 -> 290 +22 regressions 0
rustc --emit=metadata 223 -> 224 +1 regressions 0
The C number was derived independently twice -- by an adversarial sweep
that patched the emitter and regenerated, and by this change -- and both
say 22. Nobody had measured the Rust half.
The population was snapshotted into a per-run file rather than read from
the shared /tmp/specs.txt, which a background agent rewrote mid-session
earlier today (skill 401).
Four mutants, all killed. M4 is the one worth having: quoting EVERY
literal fixes strings and breaks numbers, and must also leave intact the
`_`-separator stripping the C arm already did (100_000 -> 100000, since C
reads the separator as a suffix) -- an early return for strings must not
skip it.
The escape test was rewritten after failing on CORRECT output: its first
version tried to find "the line with the escape" and found the wrong one.
The emitters were right and the test was not. It asserts the exact
expected literal now, in all three backends.
Not touched: `expr_to_string` has the same shape and is not an emitter --
it builds description fields where quoting may well be wrong. Named
rather than changed on the strength of a pattern match.
Closes #2970
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
gHashTag
enabled auto-merge (squash)
August 30, 2026 18:17
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
This was referenced Aug 30, 2026
Contributor
PR DashboardGenerated at: 2026-08-30 18:27:08 UTC
Summary
Seal Status
|
Contributor
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
This was referenced Aug 30, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two backends wrote every string literal as a bare identifier
The lexer strips the surrounding quotes and stores the raw text, tagging the node
extra_kind == "string". Zig reads that tag (compiler.rs:9560), Verilog reads it(
:16519), the typechecker reads it (:22684). The C and Rust emitters never did.The
#defineis the worst of the three. A macro whose body is a bare word is validC: it lands in the output, the file compiles past it, and it fails wherever the name is
used — or expands to something else entirely. The other two are honest compile errors.
Two call sites in C, and the second is the one a partial fix leaves behind
gen_c_expr's literal arm is the obvious site. Fixing it alone leaves#definestillwrong, because that path goes through
c_literal(v: &str)— which takes a string, andso cannot see a tag that lives on the node. Both are fixed here; the second has its own
test, which fails on a tree where only the first is repaired.
Measurement — per spec, both directions, over a 665-spec snapshot
cc -fsyntax-only -std=gnu11 -ferror-limit=0rustc --edition 2021 --crate-type lib --emit=metadata -A warnings589 of the 665 generate; timeouts 0 in every run.
The C number was derived independently twice — by an adversarial sweep that reached it
by patching the emitter and regenerating, and by this change — and both say 22. The Rust
half nobody had measured; it is worth +1 on the lib column, and the reason it is small is
that Rust's remaining blockers are elsewhere.
The population was snapshotted into a per-run file rather than read from the shared
/tmp/specs.txt, because that file was rewritten by a background agent mid-sessionearlier today (skill 401).
Mutation testing
c_keeps_the_quotes_on_a_local,escapes_are_written_back_escaped#definec_keeps_the_quotes_on_a_definerust_keeps_the_quotes,escapes_are_written_back_escapeda_number_is_not_quoted,the_underscore_separator_rule_still_appliesM4 is the one worth having: a rule that quoted every literal would fix strings and break
numbers, and it also has to leave intact the
_-separator stripping the C arm already did(
100_000→100000, since C reads the separator as a suffix). An earlyreturnforstrings must not skip it.
The escape test was rewritten after failing on correct output: its first version tried
to locate "the line with the escape" and found the wrong one. The emitters were right and
the test was not. It now asserts the exact expected literal — the lexer unescapes as it
reads, so a real newline and a real quote arrive at the emitter and must be written back as
\nand\"in all three backends.Not touched
expr_to_string(compiler.rs:29609) has the same shape and is not an emitter — itbuilds
target/valuedescription fields, where quoting may well be wrong. Left alone andnamed here rather than changed on the strength of a pattern match.
Found by the adversarial sweep of 2026-08-31; the Rust half is new.
Closes #2970
🤖 Generated with Claude Code