Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .trinity/seals/compiler_IntTypeNames.json
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
{
"built_by": "t27c-bootstrap@0.5.2+ea1bccd",
"built_by": "t27c-bootstrap@0.5.2+ee3da59",
"closure": [
{
"spec": "specs/tri/t27b/type_text.t27",
"spec_hash": "sha256:f474fe76cd84a302b46401c9a1b7db0b6a7282c0f0c8a462e4a9493415d0d915"
"spec_hash": "sha256:0c2ea71bc30d0865c2fb2206de7a0499e836bba7da6d1340bb1bef116823f473"
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:1740064d2823e46c3785fbf7c43b442b0c15d0c4aecdc0012d19b7865f1b5b83",
"gen_hash_rust": "sha256:64c77221dd68b3e9c3e79bf611e5c9426b298faf7a08fb10b6e3926a92e076f3",
"gen_hash_verilog": "sha256:57571437b59aac3631bc9f2f850fe6dc4e98583987304cd2c0c639e39b3eb898",
"gen_hash_zig": "sha256:907e2194aa712faa772ed63a88b511c8567f09e2a923dd8a2eca840a8e7f8300",
"gen_hash_c": "sha256:1171f7cf9a3ba01dd60e34f57012d097d601729ec447efd62327f6695cf22513",
"gen_hash_rust": "sha256:bd09f357f64b7eab3a5c587dbf069c6c1561ec44f6c479e0acf0beb3667a2107",
"gen_hash_verilog": "sha256:057d18d09c0a9b8ba81e95f118b75f7a1de579312fce1ccfc57bb1fffd155645",
"gen_hash_zig": "sha256:b3608b162eef8df3ae7b6cdece1b868009fba678624a6cdd3e5b63fd06cdd051",
"module": "IntTypeNames",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-11T13:03:09Z",
"sealed_at": "2026-10-17T18:22:57Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:5f7c1268c8c7a377d710513790be27dd4bd3c85fb2a6445af99a58211bb1e84d",
"spec_hash": "sha256:21600d9f4c201e54953fd0c27f059993b0939041dd4750944e51eb85ddc11275",
"spec_path": "specs/compiler/int_type_names.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 4,
"total": 4,
"passed": 6,
"total": 6,
"vacuous": 0
},
"toolchain": {
Expand Down
6 changes: 3 additions & 3 deletions .trinity/seals/conformance_T27bConformanceOddWidthInts.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"built_by": "t27c-bootstrap@0.5.2+cc19d1fcd",
"built_by": "t27c-bootstrap@0.5.2+ee3da59",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:4e37f84a8bb97a92df3c8b4cb25f6c44e882cf89fc9324e83065f81e426a8713",
Expand All @@ -9,9 +9,9 @@
"module": "T27bConformanceOddWidthInts",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-11T10:29:43Z",
"sealed_at": "2026-10-17T18:23:00Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:e0ae99328f30f3c92eb64fc81fbc94ac4a730692dc40e53a941ce97fb7d65310",
"spec_hash": "sha256:1e679330ea9b8074f0399a50a7a1f108d44afa7b9de046a68eac353c699a149f",
"spec_path": "specs/tri/t27b/conformance/odd_width_int.t27",
"tests": {
"failed": 0,
Expand Down
28 changes: 28 additions & 0 deletions .trinity/seals/conformance_T27bConformanceVerilogOddCast.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"built_by": "t27c-bootstrap@0.5.2+ee3da59",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:7aeec07814506d88186d27ed7c4c09888b396c27ebd50872922fd05606bb27b0",
"gen_hash_rust": "sha256:b5e5134185aef3f5e1c35f31e37e7bf7bef596627f4091a2c30ffeb52bc3db64",
"gen_hash_verilog": "sha256:c239e304111bb7da816ebe1005f352146bac9b3d01f079eeef3546e1739d878b",
"gen_hash_zig": "sha256:481bb9ba2b3a3317acd7d52c8332c795c3a720df9f3e03810f92695d64cf29a4",
"module": "T27bConformanceVerilogOddCast",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-17T18:22:59Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:1b073e86e2f4ec29ecd7693ac67cf1c2fadf77682afc25f798bdf7ef9a63f540",
"spec_path": "specs/tri/t27b/conformance/verilog_odd_cast.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 4,
"total": 4,
"vacuous": 0
},
"toolchain": {
"test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d",
"zig": "0.16.0"
}
}
53 changes: 37 additions & 16 deletions bootstrap/gen/rust/compiler/int_type_names.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
// Generated from .t27 spec
// DO NOT EDIT — generated by t27c
// NOT LOWERED BY THIS BACKEND: 4 test(s), 0 invariant(s), 0 function(s) with no body.
// NOT LOWERED BY THIS BACKEND: 6 test(s), 0 invariant(s), 0 function(s) with no body.
// This backend emits declarations only. The spec's checks live in
// the Zig and Verilog outputs; do not read this file as verified.

pub fn uint_bits(ty: &[u8]) -> Option<u32> {
let lo: usize = trim_lo(ty, 0, ty.len());
let hi: usize = trim_hi(ty, lo, ty.len());
if (((hi - lo) < 2) || ((hi - lo) > 3)) {
return None;
}
if (ty[(lo) as usize] != 117) {
return None;
}
let p1: usize = (lo + 1);
if (ty[(p1) as usize] == 48) {
return None;
}
let mut w: u32 = 0;
let mut j: usize = p1;
while (j < hi) {
if !(digit(ty[(j) as usize])) {
return None;
}
let dv: u32 = ((ty[(j) as usize] - 48) as _);
w = ((w * 10) + dv);
j = (j + 1);
}
if ((w < 1) || (w > 64)) {
return None;
}
return Some(w);
}

pub fn zig_uint_bits(ty: &[u8]) -> Option<u32> {
let lo: usize = trim_lo(ty, 0, ty.len());
let t = &ty[(lo) as usize..(trim_hi(ty, lo, ty.len())) as usize];
if same(t, "u8") {
return Some(8);
if same(t, "usize") {
return Some(64);
} else {
if same(t, "u16") {
return Some(16);
} else {
if same(t, "u32") {
return Some(32);
} else {
if (same(t, "u64") || same(t, "usize")) {
return Some(64);
} else {
return None;
}
}
}
return uint_bits(t);
}
}

Expand Down Expand Up @@ -108,6 +125,10 @@ pub const B_SPACE: u8 = 32;

pub const B_TAB: u8 = 9;

pub fn digit(c: u8) -> bool {
return ((c >= 48) && (c <= 57));
}

pub fn same(s: &[u8], w: &'static str) -> bool {
if (s.len() != w.len()) {
return false;
Expand Down
6 changes: 3 additions & 3 deletions bootstrap/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5020,7 +5020,7 @@ the parser used to read it as `{}` followed by a negation",
/// Consume the type that follows an `as` cast, e.g. `u32`, `i8`, or an
/// array form like `u8[4]`. Returns the textual type for diagnostics.
/// The base type name is validated against the known scalar type set so
/// that a typo like `x as u3` or `x as banana` is rejected at parse time
/// that `x as u0` or `x as banana` (uN: specs/compiler/int_type_names.t27) is rejected at parse time
/// instead of silently lowering to a 32-bit default.
fn parse_cast_target_type(&mut self) -> Result<String, String> {
if self.current.kind != TokenKind::Ident {
Expand All @@ -5046,7 +5046,7 @@ the parser used to read it as `{}` followed by a negation",
"bool", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize",
"f32", "f64",
];
if !VALID_CAST_TYPES.contains(&base.as_str()) {
if !VALID_CAST_TYPES.contains(&base.as_str()) && int_type_names::uint_bits(base.as_bytes()).is_none() {
// f32/f64 parse in DECLARATIONS, so "unknown type" would be a lie here.
// The truth is narrower: no backend lowers float arithmetic -- the C
// generator emits the literal token `f32`, which is not a C type, and
Expand All @@ -5063,7 +5063,7 @@ the parser used to read it as `{}` followed by a negation",
));
}
return Err(format!(
"unknown cast target type `{}`; expected one of {:?}",
"unknown cast target type `{}`; expected one of {:?} or uN, 1 <= N <= 64",
base, VALID_CAST_TYPES
));
}
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/stage0/FROZEN_HASH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
e769ef0217136b9bfdcb3dd783f02d8a8e23e91b70367416d02cdf61af4727e8 bootstrap/src/compiler.rs
3b53d48ddb5201c33b594b583beace6cc8e6a5a1dadee345a2b37dd38476e2be bootstrap/src/compiler.rs
1 change: 1 addition & 0 deletions docs/reports/t27b_expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,7 @@
{"path": "specs/tri/t27b/conformance/value_ignored.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/conformance/value_params.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/conformance/verilog_lib.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/conformance/verilog_odd_cast.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/conformance/verilog_prim.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/conformance/verilog_use.t27", "t27b": "pass"},
{"path": "specs/tri/t27b/conformance/verilog_widths.t27", "t27b": "pass"},
Expand Down
79 changes: 70 additions & 9 deletions specs/compiler/int_type_names.t27
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
; trim_lo / trim_hi (Rust's White_Space trim).
; - t27o compares with tri::mutate::plant's same_text, which gen-rust lowers to (&'static str, &'static str)
; while a sliced text here is &[u8]; tri::t27b::type_text's `same` has the slice signature.
; uint_bits (#10072) is written here, not converted. The parser's cast whitelist consults it, and
; zig_uint_bits defers to it past `usize`.
; Nothing here is written twice.
; --
; The tests call each function by name. Their expected values are what the hand Rust returned on the lab
Expand All @@ -26,22 +28,41 @@ module IntTypeNames;

use tri::t27b::type_text;

/// The width of an unsigned type name `uN`, 1 <= N <= 64, or null (#10072). These are the widths `t27b
/// verilog` lowers (specs/tri/t27b/verilog_plan.t27, "Widths"), so they are the cast targets the parser
/// accepts beyond its standard names: `x as u3`, `avg as u12`. A name, not a number: no leading zero
/// (`u012`), no sign, no `usize`. u0 holds nothing and u65 is past the 64 bits a `t27b verilog` value
/// carries, so both stay refused, and so does a typo (`u3x`).
pub fn uint_bits(ty: []const u8) -> ?u32 {
const lo : usize = type_text::trim_lo(ty, 0, ty.len);
const hi : usize = type_text::trim_hi(ty, lo, ty.len);
if (hi - lo < 2 or hi - lo > 3) { return null; }
if (ty[lo] != 117) { return null; }
const p1 : usize = lo + 1;
if (ty[p1] == 48) { return null; }
var w : u32 = 0;
var j : usize = p1;
while (j < hi) {
if (!type_text::digit(ty[j])) { return null; }
const dv : u32 = @intCast(ty[j] - 48);
w = w * 10 + dv;
j = j + 1;
}
if (w < 1 or w > 64) { return null; }
return w;
}

/// W566: bit-width of a t27 UNSIGNED integer type, or null otherwise. Restricted to unsigned because
/// Zig's `@truncate` only lowers a narrowing cast cleanly when both sides are unsigned; signed narrowing
/// stays on the checked `@intCast` path.
/// stays on the checked `@intCast` path. Every `uN` counts (#10072): `x as u3` keeps the low three bits
/// here as `t27_u3` does in `t27b verilog`, where `@intCast` would panic on 13.
pub fn zig_uint_bits(ty: []const u8) -> ?u32 {
const lo : usize = type_text::trim_lo(ty, 0, ty.len);
const t = ty[lo .. type_text::trim_hi(ty, lo, ty.len)];
if (type_text::same(t, "u8")) {
return 8;
} else if (type_text::same(t, "u16")) {
return 16;
} else if (type_text::same(t, "u32")) {
return 32;
} else if (type_text::same(t, "u64") or type_text::same(t, "usize")) {
if (type_text::same(t, "usize")) {
return 64;
} else {
return null;
return uint_bits(t);
}
}

Expand Down Expand Up @@ -130,6 +151,46 @@ test zig_uint_bits_reads_unsigned_widths_only {
assert zig_uint_bits("") == null;
assert zig_uint_bits("u") == null;
assert zig_uint_bits("i") == null;
assert zig_uint_bits("u3").? == 3;
assert zig_uint_bits(" u12 ").? == 12;
assert zig_uint_bits("u0") == null;
assert zig_uint_bits("u65") == null;
assert zig_uint_bits("i3") == null;
}

test uint_bits_names_every_unsigned_width {
assert uint_bits("u1").? == 1;
assert uint_bits("u3").? == 3;
assert uint_bits("u8").? == 8;
assert uint_bits("u12").? == 12;
assert uint_bits("u21").? == 21;
assert uint_bits("u63").? == 63;
assert uint_bits("u64").? == 64;
assert uint_bits(" u12").? == 12;
assert uint_bits("u3\n").? == 3;
assert uint_bits("\tu9 ").? == 9;
}

test uint_bits_refuses_what_is_not_a_width {
assert uint_bits("u0") == null;
assert uint_bits("u65") == null;
assert uint_bits("u99") == null;
assert uint_bits("u128") == null;
assert uint_bits("u012") == null;
assert uint_bits("u03") == null;
assert uint_bits("u00") == null;
assert uint_bits("u") == null;
assert uint_bits("") == null;
assert uint_bits(" ") == null;
assert uint_bits("i3") == null;
assert uint_bits("U3") == null;
assert uint_bits("u3x") == null;
assert uint_bits("x3") == null;
assert uint_bits("u 3") == null;
assert uint_bits("u-3") == null;
assert uint_bits("usize") == null;
assert uint_bits("bool") == null;
assert uint_bits("banana") == null;
}

test int_value_bits_counts_value_bits {
Expand Down
2 changes: 1 addition & 1 deletion specs/tri/t27b/conformance/odd_width_int.t27
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
; operators, `~` on a narrow unsigned type, shifts that drop the bits that
; leave the type, products that pass 2^32 before they are checked, widening
; into wider types, `as` casts out of an odd width, and struct fields of odd
; widths. (t27c's `as` names only the standard widths as a target.)
; widths. Casts into an odd width are verilog_odd_cast.t27 (#10072).
; phi^2 + phi^-2 = 3 | TRINITY

module T27bConformanceOddWidthInts;
Expand Down
70 changes: 70 additions & 0 deletions specs/tri/t27b/conformance/verilog_odd_cast.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
; specs/tri/t27b/conformance/verilog_odd_cast.t27 -- a cast into an odd unsigned width (gHashTag/t27#10072).
; t27c's parser refused `x as u3` and `x as u12` until uN, 1 <= N <= 64, was decided in
; specs/compiler/int_type_names.t27. `t27b verilog` lowers a narrowing cast through t27_uW and a widening one
; as a zero fill (verilog_plan.t27, "Widths"); the reference truncates with Zig's @truncate. Each test below
; passes in the reference, in t27b and in the generated test bench:
; - the 8-sample moving average the Build-A-Chip-Bench proxy run needed (epic #10066): u12 samples, a sum
; formed in 32 bits, and `as u12` back to the sample width;
; - `as u3` keeps the low three bits of a byte, `as u1` the low bit;
; - a cast out of u12 into u16 is a zero fill, and one into u8 keeps the low byte;
; - the same narrowing inside on_clock, in the module.
; phi^2 + 1/phi^2 = 3 | TRINITY

module T27bConformanceVerilogOddCast;

pub fn avg8(a: u12, b: u12, c: u12, d: u12, e: u12, f: u12, g: u12, h: u12) -> u12 {
return (((a as u32) + (b as u32) + (c as u32) + (d as u32) + (e as u32) + (f as u32) + (g as u32) + (h as u32)) >> 3) as u12;
}

pub fn low3(x: u8) -> u3 {
return x as u3;
}

pub fn low_bit(x: u8) -> u1 {
return x as u1;
}

pub fn wide(x: u12) -> u16 {
return x as u16;
}

pub fn low_byte(x: u12) -> u8 {
return x as u8;
}

pub var out : u12 = 0;

fn on_clock(x: u16) {
out = (x >> 2) as u12;
}

test the_average_comes_back_as_u12 {
assert_eq(avg8(8, 8, 8, 8, 8, 8, 8, 8), 8);
assert_eq(avg8(4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095), 4095);
assert_eq(avg8(16, 0, 0, 0, 0, 0, 0, 0), 2);
assert_eq(avg8(1, 2, 3, 4, 5, 6, 7, 8), 4);
}

test a_narrowing_keeps_the_low_bits {
assert_eq(low3(13), 5);
assert_eq(low3(255), 7);
assert_eq(low3(8), 0);
assert_eq(low_bit(6), 0);
assert_eq(low_bit(7), 1);
assert_eq(low_byte(4095), 255);
assert_eq(low_byte(300), 44);
}

test a_widening_is_a_zero_fill {
assert_eq(wide(4095), 4095);
assert_eq(wide(0), 0);
}

test the_module_narrows_at_its_width {
out = 0;
on_clock(65535);
assert_eq(out, 4095);
on_clock(400);
assert_eq(out, 100);
}
Loading