Skip to content
Open
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
68 changes: 54 additions & 14 deletions bootstrap/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6183,8 +6183,10 @@ impl Compiler {
pub fn compile_c(source: &str) -> Result<String, String> {
let lexer = Lexer::new(source);
let mut parser = Parser::new(lexer);
let mut ast = parser.parse()?;
optimize(&mut ast, &OptConfig::default());
let ast = parser.parse()?;
// Emit FAITHFUL C from the AST; the C compiler optimizes downstream. See
// the note on compile_rust: t27c's optimizer drops reassigned locals and
// const-inlines `let`, corrupting the source-level output. Fixes #1455.
let mut codegen = CCodegen::new();
codegen.gen_c(&ast);
Ok(codegen.into_string())
Expand All @@ -6193,8 +6195,15 @@ impl Compiler {
pub fn compile_rust(source: &str) -> Result<String, String> {
let lexer = Lexer::new(source);
let mut parser = Parser::new(lexer);
let mut ast = parser.parse()?;
optimize(&mut ast, &OptConfig::default());
let ast = parser.parse()?;
// Emit FAITHFUL Rust from the AST. Do NOT run t27c's own optimizer on the
// Rust backend: several of its passes (const-propagation and the
// unconditional copy-propagation / dead-store passes) corrupt a
// reassigned mutable local -- e.g. `var x = 0; ... x = a;` loses its
// declaration while its uses remain, producing E0425. rustc/LLVM optimize
// the emitted code far more reliably, so faithful codegen is both correct
// and sufficient here. Verilog/Zig/C backends keep their own pipelines.
// Fixes gHashTag/t27#1455.
let mut codegen = RustCodegen::new();
codegen.gen_rust(&ast);
Ok(codegen.into_string())
Expand Down Expand Up @@ -8070,12 +8079,21 @@ impl RustCodegen {
format!("Vec<{}>", Self::t27_type_to_rust(inner))
}
t if t.starts_with('[') && t.contains(']') => {
// [N]T format - convert to Vec
if let Some(bracket_end) = t.find(']') {
let inner = &t[bracket_end + 1..];
format!("Vec<{}>", Self::t27_type_to_rust(inner))
let end = t.rfind(']').unwrap();
let inside = &t[1..end];
if let Some(semi) = inside.find(';') {
// Rust-style fixed-size array `[T; N]`: element before `;`,
// size after. (Previously only the Zig-style `[N]T` form was
// handled; `[T; N]` took the empty suffix after `]` and emitted
// `Vec<>` -> E0107. #1457.) Array lengths must be `usize`, and
// spec size consts are u32, so cast in the const-expr position.
let elem = inside[..semi].trim();
let size = inside[semi + 1..].trim();
format!("[{}; {} as usize]", Self::t27_type_to_rust(elem), size)
} else {
t.to_string()
// Zig-style `[N]T`: size in brackets, element after `]`.
let after = &t[end + 1..];
format!("Vec<{}>", Self::t27_type_to_rust(after))
}
}
t => t.to_string(), // Custom type name
Expand Down Expand Up @@ -8158,11 +8176,18 @@ impl RustCodegen {
}
NodeKind::ExprIndex => {
if node.children.len() >= 2 {
format!(
"{}[{}]",
Self::expr_to_rust(&node.children[0]),
Self::expr_to_rust(&node.children[1])
)
let base = Self::expr_to_rust(&node.children[0]);
let idx = &node.children[1];
let idx_str = Self::expr_to_rust(idx);
// Array/Vec indices must be `usize`. t27 index expressions are
// u32, so cast non-literal indices. Integer literals already
// infer `usize` in index position, so casting them would trip
// clippy::unnecessary_cast under `-D warnings`. #1457.
if idx.kind == NodeKind::ExprLiteral {
format!("{}[{}]", base, idx_str)
} else {
format!("{}[({}) as usize]", base, idx_str)
}
} else {
"()".to_string()
}
Expand Down Expand Up @@ -22530,6 +22555,21 @@ mod tests_phase40_coverage {
);
}

// #1455 regression: two consecutive reassigned locals must BOTH survive.
// The optimizer used to drop the first declaration (and const-inline `let`),
// leaving `x` undeclared while its uses remained (E0425). Faithful codegen
// on the Rust backend emits both `let mut` bindings.
#[test]
fn test_two_reassigned_locals_survive_1455() {
let code = "module M { pub fn f(a: u32) -> u32 { var x = 0; var y = 1; if (a > x) { x = a; y = 2; } return y; } }";
let out = Compiler::compile_rust(code).expect("compile should succeed");
assert!(
out.contains("let mut x = 0") && out.contains("let mut y = 1"),
"a reassigned local was dropped from Rust output: {}",
out
);
}

// #1401 regression (C emitter shares the same lexer/parser root cause):
// the `let` local must appear as a typed C declaration.
#[test]
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/stage0/FROZEN_HASH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
197d6eb5a40bed62ae806cb09c14fbde4d2664ed7cc6991c1e3bfa60983706ff bootstrap/src/compiler.rs
c25b7a6f4e2fca73607b4eb8dac697964b57071b2ba1e2d59997aca82a39d138 bootstrap/src/compiler.rs
11 changes: 10 additions & 1 deletion docs/NOW.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# NOW — IGLA cycle 1 + Wave Loop 469 context (2026-07-07)

Last updated: 2026-07-07
Last updated: 2026-07-10

## Rust/C codegen fixes (Closes #1455, Refs #1457)

- PR #1456: removed the AST optimizer from the Rust and C source backends
(`compile_rust`/`compile_c`) so they emit faithful code; the optimizer was
dropping reassigned mutable locals (E0425) and const-inlining `let`.
- Array/index codegen: `[T; N]` -> `[T; N as usize]` (was `Vec<>`), non-literal
indices cast to `usize`. t27c suite 1494/1 (pre-existing Verilog HIR fail).
Downstream gHashTag/tri-net regenerates with 2609 E0425 -> 0.

## IGLA cycle 1 — process debt needles (Refs #1438, #1440, #1442, #1444, #1446)

Expand Down
Loading