Repository navigation
self-host: gen-c passes leading-zero, bad-binary-digit and misplaced-underscore literals to C #7319
Description
Profile (read first)
You are working as Gamma (Compiler Core) on epic #5980: rewrite t27c in t27 itself.
The self-hosted core (specs/compiler/core/t27core.t27) must write exactly the bytes
t27c gen-c writes, so every gen-c defect is a defect the core has to refuse instead of learn.
Rules for this task:
- Change the Rust lexer only inside the Boundary below.
gen/is generated: never hand-edit it. - Build with
cargo build --release -p t27cand use./target/release/t27cfrom the repository
root, not at27con PATH. - Write the regression test first and watch it fail on the unfixed compiler. Then fix, and
revert the fix once to watch the test go red again. Say in the PR that you did both. - Any spec that stops compiling must be listed in the PR by path with its fix. A silent corpus
change is a failed task. - The PR body says
Closes #plus this issue's number andPart of #5980. ASCII only in source files.
Context
gen-c's number scan takes any run of digits, x X b B _ and (after 0x) hex letters, and writes
what it read to C. Several malformed literals therefore pass with exit 0:
- a decimal with a leading zero reaches C as an OCTAL constant:
010and0_10are written as
010, which C reads as 8. A test assertingD == 10compiles and traps at run time.08
does not compile at all (invalid digit "8" in octal constant); 0b102is written as0b102(invalid digit "2" in binary constant);1_,1__0and0x_1Fare accepted and quietly normalized to1,10and0x1F.
The Zig reference refuses every one of these (std.zig.number_literal failures leading_zero,
invalid_digit, trailing_underscore, repeated_underscore, invalid_underscore_after_special),
so the fix is a lexer error, not a rewrite. 0o17 becoming 0 is the separate #2645.
The core (P1 slice 2) refuses all of these with E_LEX.
Reproduce
cargo build --release -p t27c
for v in 010 0_10 08 0b102 1_ 1__0 0x_1F; do printf 'module m;\nconst D: i64 = %s;\n' "$v" > /tmp/lit.t27; ./target/release/t27c gen-c /tmp/lit.t27 >/dev/null 2>&1; echo "$v exit $?"; done
Today every line ends in exit 0.
Acceptance criteria
-
- The Reproduce loop prints a non-zero exit for each of the seven literals (today
exit 0for all seven)
- The Reproduce loop prints a non-zero exit for each of the seven literals (today
-
- The same loop over
9 10 1_000 0x1_F 0b1_0 0 0x00printsexit 0for each (today too)
- The same loop over
-
cargo test --release -p t27c --test genc_number_literalspasses
Boundary
bootstrap/src/compiler.rsbootstrap/tests/genc_number_literals.rs
User Scenarios & Testing
- Given a spec with
const D: i64 = 010;,
when./target/release/t27c gen-cruns on it,
then it exits non-zero with an error naming the literal and its line, instead of writing an octal 8.
Requirements
- FR-001: the lexer MUST refuse a decimal literal with a leading zero and more digits, a binary digit other than 0 or 1, and a
_that is not between two digits of the literal's radix. - FR-900:
cargo test --release -p t27c --test genc_number_literalsMUST pass, and MUST fail with the fix reverted. - FR-901:
cargo test --release -p t27c --test core_selfhostand the corpus suite (./target/release/t27c suite --repo-root .) MUST still pass.
Activity
t27core: numeric literal forms and pub var, byte-identical to gen-c (C…
fix(t27c): refuse a malformed number literal by name, read 0o as octal (
gHashTag commentedon Oct 7, 2026
The fix in #7421 is parked as a draft: it is over the foreign-line budget (#7371). compiler.rs +159, tests +231, 390 in total, against limits of 40 per file and 80 per PR. Details are on #7421.