diff --git a/src/ast/compiler/optimizer/engine.rs b/src/ast/compiler/optimizer/engine.rs index 239a72d..c65a5a8 100644 --- a/src/ast/compiler/optimizer/engine.rs +++ b/src/ast/compiler/optimizer/engine.rs @@ -562,10 +562,6 @@ impl Optimizer { new_exprs.push(opt); } - if !block_changed { - return node_rc; - } - if self.enabled { if new_exprs.is_empty() { return Rc::new(folder.make_nop_node(node)); @@ -574,6 +570,10 @@ impl Optimizer { } } + if !block_changed { + return node_rc; + } + (NodeKind::Block { exprs: new_exprs }, node.ty.clone()) } diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 2b22f81..3b49cca 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -1,5 +1,50 @@ use myc::ast::environment::Environment; +// ── Block simplification ───────────────────────────────────────────────────── + +#[test] +fn test_empty_block_eliminated() { + // (do) is semantically Void/Nop — the optimizer must collapse it, + // even though nothing inside the block changes (block_changed stays false). + let env = Environment::new(); + let dump = env.dump_ast("(do)").unwrap(); + assert!( + !dump.contains("Block"), + "Empty block must be collapsed to Nop. Dump:\n{}", + dump + ); +} + +#[test] +fn test_single_expr_block_unwrapped() { + // (do 42) carries exactly one expression — the Block wrapper is redundant. + // The optimizer must unwrap it to a bare Constant node. + let env = Environment::new(); + let result = env.run_script("(do 42)").unwrap(); + assert_eq!(format!("{}", result), "42"); + let dump = env.dump_ast("(do 42)").unwrap(); + assert!( + !dump.contains("Block"), + "Single-expression block must be unwrapped. Dump:\n{}", + dump + ); +} + +#[test] +fn test_inner_empty_block_eliminated() { + // The inner (do) must be collapsed to Nop during recursive optimization. + // Nop-pruning in the outer block then removes it, leaving just Constant(42). + let env = Environment::new(); + let result = env.run_script("(do (do) 42)").unwrap(); + assert_eq!(format!("{}", result), "42"); + let dump = env.dump_ast("(do (do) 42)").unwrap(); + assert!( + !dump.contains("Block"), + "Inner empty block must be eliminated entirely. Dump:\n{}", + dump + ); +} + #[test] fn test_optimizer_destructuring_inlining_and_mutation() { let env = Environment::new();