Repository navigation
t27core: the coercions typecheck lets through and Zig refuses (Closes #8080) - #8085
Merged
Merged
Conversation
…8080) P3 slice 2 of epic #5980. t27c's typecheck only warns about a narrowing initializer, argument or return, and about an assignment to a module const, and does not look at conditions, operands or indexes (see #8075). gen-c then writes C that converts the value silently. The core now refuses these with E_UNSOUND (17), read on typecheck's types and on the precise type of a cast, field, element, struct literal or enum member: - a value that does not coerce, as Zig coerces, to its declared type (initializer, argument, return, field, item, assignment, module init); - a condition, or an operand of !, and, or, assert, that is not a bool; - arithmetic over mixed signs or a bool; unary - on an unsigned; a signed index; an assignment to a module const; - a comparison C makes on converted values (see #8076). A comptime integer (a literal, a module const, a local const with such an initializer) coerces by its value, as in Zig, and is never a bool. A local const is marked in the scope table (Sym.ct) for that. Fixtures: one accept fixture that runs on all three routes (the local const form is a core test, see #8079), 23 refuse fixtures, 9 core tests. The slice-1 tests' first feed lines are indented. t27core_ddc.t27 restates the widened Sym and is repinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Zig takes them (Closes #8080) core_selfhost.rs's inline fixtures run only through gen-c and cc, and two of them used what P3 slice 2 now refuses with E_UNSOUND: `i32 + u64` arithmetic in flow's ops, and a u8 operand of && returned as a u8 in the comment fixture. Both keep their shape with one type changed: b is an i64, and f takes and returns a bool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…8080) A scratch fuzzer of the Zig route found `9 == !true` accepted: the comparison rule left a comptime integer alone on either side. Zig compares a comptime integer with an integer only, and C compares 9 with 0 or 1. check_eq_types now refuses one against a bool, a struct or an enum (E_UNSOUND, see #8075). t27core_ddc.t27 is repinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gHashTag
enabled auto-merge (squash)
October 9, 2026 08:04
4 of 6 tasks
This was referenced Oct 9, 2026
Open
…erand (Closes #8080) The Zig-route fuzzer found three more holes in the slice's coercion rules: - (v as i64) & x, with x an i16, is an i64 in Zig; - G & x, with const G: i64, is an i64 too; - usize & u32 is a usize, where typecheck's promotion, which ranks usize 0, said u32. A value Zig knows whole at compile time still coerces by its value. Where it meets a runtime operand, only a number, a char and a local const whose value is one number (gen-zig inlines it) take that operand's type. A module const, a cast, and a local const computed otherwise keep their own, as probed on the Zig route. Sym.ct now tells the inlined local const (3) from the typed one (1). Comparisons and indexes keep reading a compile-time value as untyped: C and Zig agree on it unless it is negative, which is the literal-widths slice. Fixtures e17_cast_keeps_its_type, e17_typed_const_in_mixed_operation and e17_usize_operand_widens; an accept case for a const index and a const compared with a u64; 2 core tests. t27core_ddc.t27 is repinned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 9, 2026
Merged
Contributor
This was referenced Oct 9, 2026
This was referenced Oct 9, 2026
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.
Closes #8080
P3 slice 2 of epic #5980 (see #5980). Slice 1 (#8039) brought t27c's typecheck into the core as it is. This slice refuses what typecheck lets through and the Zig route refuses, where gen-c's C converts the value silently (see #8075).
Accepts
var a: u8 = CA_LIMIT;withconst CA_LIMIT: i64 = 200;. Where such a value meets a runtime operand, only a number, a char, or a local const whose value is one number (gen-zig inlines it) takes the operand's type. A module const or a cast keeps its own:G & xwithconst G: i64andx: i16is an i64, as on the Zig route. A compile-time index or comparison operand is read by its value./and%: gen-zig writes@divTruncand@rem, and C truncates too.Refuses (E_UNSOUND, 17)
!,and,or,assert, that is not a bool; a comptime integer where a bool is declared-on an unsigned; a signed indexThe local const is marked in the scope table (
Sym.ct) so that it can coerce by value.Fixtures
accept/coercion_accepts.t27: every accepted form above, run on gen-c+cc, Zig and t27b. The local-const form is a core test only, because t27b refuses it (see t27b: a local const known at compile time coerces by value, as Zig does (var b: u8 = local, with const local: i64 = 7) #8079).refuse/e17_*.t27. Each wrong shape is BLOCKED on the Zig route when called from a test, except the C-only comparison. typecheck accepts all of them, so gen-c generates them, and none joinstools/specs_generate_baseline.txt.t27core.t27. The first feed lines of slice 1's tests are indented.core_selfhost.rs: two inline C-only fixtures used shapes this slice refuses.flow'sopshadi32 + u64; nowbis an i64. The comment fixture had a u8 operand of&&returned as a u8;fnow takes and returns a bool.Lines by file
Sym, repinned data)Nothing generated.
bootstrap/src/compiler.rsis untouched. Master'scheck_budgetandcheck_all(gen/c/policy/own_language.c, with master'stools/policy/foreign-exceptions.txt) both exit 0 on this diff's numstat and name-status.Lab evidence
Base: master 8eb0ff4.
t27c lab:
cargo test --release -p t27c --test core_selfhost: ok;DDC:
t27core_ddc1 passed,coercion_accepts1 passed (7 runtime asserts);t27c test-report):t27core_ddcpass 1,coercion_acceptspass 1.The DDC data is S sha256 9eb0a9fd..., 218931 bytes, and E sha256 5af32e15..., 191487 bytes.
Two scratch fuzzers ran on the lab against the same t27c.
Typecheck parity, 2700 programs: HOLE 0. The core refuses everything typecheck refuses, and raises E_CHECK only where typecheck does.
The Zig route, with every fn called from a test, in rounds: 1000 programs, then 600, then 500 and 300 after the typed-operand fixes:
The core refuses and Zig compiles in 4 + 2 programs. Each is one of:
if (false)branch, a comptimefalse and 3ortrue or ..., a dead store;@intCasttaking the destination's type.The core stays strict there.
Zig refuses and the core accepts in 56 + 35 programs:
0/1(the program is well-typed);Holes the rounds found are fixed here:
9 == !true(e17_compare_literal_with_bool);e17_typed_const_in_mixed_operation,e17_cast_keeps_its_type);e17_usize_operand_widens).The last round has FALSE_POS 0, and its gaps are t27c gen (Zig): constant folding turns a comparison into 0 or 1, which Zig refuses where a bool is expected #8077 and literal widths only.
Defects filed: #8075 (typecheck), #8076 (gen-c comparison), #8077 (gen-zig folding), #8079 (t27b local const).
🤖 Generated with Claude Code