diff --git a/bootstrap/src/compiler.rs b/bootstrap/src/compiler.rs index a2c60685c..2342c3519 100644 --- a/bootstrap/src/compiler.rs +++ b/bootstrap/src/compiler.rs @@ -6183,8 +6183,10 @@ impl Compiler { pub fn compile_c(source: &str) -> Result { 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()) @@ -6193,8 +6195,15 @@ impl Compiler { pub fn compile_rust(source: &str) -> Result { 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()) @@ -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 @@ -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() } @@ -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] diff --git a/bootstrap/stage0/FROZEN_HASH b/bootstrap/stage0/FROZEN_HASH index 1e3ad52fc..e37aa95ca 100644 --- a/bootstrap/stage0/FROZEN_HASH +++ b/bootstrap/stage0/FROZEN_HASH @@ -1 +1 @@ -197d6eb5a40bed62ae806cb09c14fbde4d2664ed7cc6991c1e3bfa60983706ff bootstrap/src/compiler.rs +c25b7a6f4e2fca73607b4eb8dac697964b57071b2ba1e2d59997aca82a39d138 bootstrap/src/compiler.rs diff --git a/docs/NOW.md b/docs/NOW.md index dc647be6e..2531d0951 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -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)