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
8 changes: 8 additions & 0 deletions NOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Last updated: 2026-08-08

## fix(gen-c): [T;N] params, test-block bindings, t27_assert macro (Refs #1919)

- Rust-style `[T; N]` parameter types now lower to `T*` (previously emitted a bare `* name` -- no base type)
- Test-block bindings are declared on first assignment (`uint64_t b0 = ...;` -- the C twin of #1894); tuple targets bind the struct return once via GNU `__auto_type` and peel `.f0/.f1`
- Two-argument `assert(cond, "msg")` lowers to a self-contained `t27_assert` macro with the message RE-QUOTED (bare unquoted words previously hit C's one-arg assert and leaked non-ASCII into identifiers)
- tri-net gcc -fsyntax-only sweep: 64/68 -> 8/68 invalid; the eight distinct long-tails are enumerated in #1919
- Unit suite 1537/1537; other backends untouched; FROZEN_HASH resealed

## fix(gen-zig): array-literal text lowering, CSE hoist, undefined init, shadow-aware dead-local scan (Closes #1910)

- ExprArrayLiteral carries its ELEMENT TEXT in extra_size with no children; Zig now emits `.{ e1, e2 }` / `.{ v } ** n` (paren-aware top-level comma split), which coerce to the typed array target
Expand Down
85 changes: 83 additions & 2 deletions bootstrap/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9565,6 +9565,11 @@ impl CCodegen {
let has_tests = ast.children.iter().any(|d| d.kind == NodeKind::TestBlock);
if has_tests {
self.write_line("#include <assert.h>");
// t27's two-argument assert(cond, "msg") is not C's assert; lower it
// to a self-contained macro (message kept for readability, unused).
self.write_line(
"#define t27_assert(c, m) do { if (!(c)) { __builtin_trap(); } } while (0)",
);
}
self.write_line("");

Expand Down Expand Up @@ -9950,8 +9955,55 @@ impl CCodegen {
self.write_line(&format!("void {}(void) {{", fn_name));
self.indent();

// Test-block bindings ('b0 = f(...);') parse as StmtAssign, not
// StmtLocal -- the same defect fixed for Verilog (#1894) and Zig.
// Declare the FIRST assignment to each plain identifier (uint64_t holds
// every t27 scalar); a tuple target binds the struct return once via
// GNU __auto_type (gcc and clang) and peels .f0/.f1/...
let mut bound: std::collections::HashSet<String> = std::collections::HashSet::new();
let mut tuple_ctr = 0u32;
for stmt in &node.children {
self.gen_c_stmt(stmt);
let fresh = stmt.kind == NodeKind::StmtAssign
&& stmt.children.len() >= 2
&& stmt.children[0].kind == NodeKind::ExprIdentifier
&& !stmt.children[0].name.is_empty()
&& bound.insert(stmt.children[0].name.clone());
let tuple = stmt.kind == NodeKind::StmtAssign
&& stmt.children.len() >= 2
&& stmt.children[0].kind == NodeKind::ExprTuple
&& stmt.children[0]
.children
.iter()
.all(|e| e.kind == NodeKind::ExprIdentifier && !e.name.is_empty());
if fresh {
self.write_indent();
self.write(&format!("uint64_t {} = ", stmt.children[0].name));
self.gen_c_expr(&stmt.children[1]);
self.write(";");
self.write_line("");
self.write_indent();
self.write(&format!("(void){};", stmt.children[0].name));
self.write_line("");
} else if tuple {
tuple_ctr += 1;
let tmp = format!("__t27_tup{}", tuple_ctr);
self.write_indent();
self.write(&format!("__auto_type {} = ", tmp));
self.gen_c_expr(&stmt.children[1]);
self.write(";");
self.write_line("");
for (fi, e) in stmt.children[0].children.iter().enumerate() {
bound.insert(e.name.clone());
self.write_indent();
self.write(&format!("__auto_type {} = {}.f{};", e.name, tmp, fi));
self.write_line("");
self.write_indent();
self.write(&format!("(void){};", e.name));
self.write_line("");
}
} else {
self.gen_c_stmt(stmt);
}
}

if node.children.is_empty() {
Expand Down Expand Up @@ -10310,6 +10362,21 @@ impl CCodegen {
};
return format!("{}*", c_inner);
}
// Rust-style array types: [T; N] → T* (pointer in param position).
if ty.starts_with('[') && ty.contains(';') {
if let Some(bracket_end) = ty.rfind(']') {
let inner = &ty[1..bracket_end];
if let Some(semi) = inner.find(';') {
let elem = inner[..semi].trim();
let c_elem = if Self::is_primitive(elem) {
Self::type_to_c(elem).to_string()
} else {
elem.to_string()
};
return format!("{}*", c_elem);
}
}
}
// Array types: [SIZE]Type → Type* (pointer in param position)
if ty.starts_with('[') {
if let Some(bracket_end) = ty.find(']') {
Expand Down Expand Up @@ -10366,7 +10433,21 @@ impl CCodegen {
}
NodeKind::ExprCall => {
let fname = &node.name;
if fname == "@compileAssert" {
if fname == "assert" && node.children.len() == 2 {
// t27 assert(cond, "msg"): C's assert macro takes ONE argument
// and the message literal must be re-quoted (the parser stores
// string contents unquoted).
self.write("t27_assert(");
self.gen_c_expr(&node.children[0]);
let msg = node.children[1]
.value
.trim_matches('"')
.replace('\\', "\\\\")
.replace('"', "\\\"");
self.write(", \"");
self.write(&msg);
self.write("\")");
} else if fname == "@compileAssert" {
self.write("_Static_assert(");
if !node.children.is_empty() {
self.gen_c_expr(&node.children[0]);
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/stage0/FROZEN_HASH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ed9e8b98b73d75673c2416383daffc74aeea0c3f5c8b97156aa5825464857323
409ab06144271d348b7ffdb0332a032c13928984f21c8fc0f838df5ecd6caa28
10 changes: 9 additions & 1 deletion docs/NOW.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# NOW — fix(gen-zig): campaign complete, 68/68 valid (2026-08-08)
# NOW — fix(gen-c): params, test bindings, assert macro (2026-08-08)

Last updated: 2026-08-08

## fix(gen-c): [T;N] params, test-block bindings, t27_assert macro (Refs #1919)

- Rust-style `[T; N]` parameter types now lower to `T*` (previously emitted a bare `* name` -- no base type)
- Test-block bindings are declared on first assignment (`uint64_t b0 = ...;` -- the C twin of #1894); tuple targets bind the struct return once via GNU `__auto_type` and peel `.f0/.f1`
- Two-argument `assert(cond, "msg")` lowers to a self-contained `t27_assert` macro with the message RE-QUOTED (bare unquoted words previously hit C's one-arg assert and leaked non-ASCII into identifiers)
- tri-net gcc -fsyntax-only sweep: 64/68 -> 8/68 invalid; the eight distinct long-tails are enumerated in #1919
- Unit suite 1537/1537; other backends untouched; FROZEN_HASH resealed

## fix(gen-zig): array-literal text lowering, CSE hoist, undefined init, shadow-aware dead-local scan (Closes #1910)

- ExprArrayLiteral carries its ELEMENT TEXT in extra_size with no children; Zig now emits `.{ e1, e2 }` / `.{ v } ** n` (paren-aware top-level comma split), which coerce to the typed array target
Expand Down
Loading