diff --git a/NOW.md b/NOW.md index e38cc2f7e..d0bf13f07 100644 --- a/NOW.md +++ b/NOW.md @@ -2,6 +2,13 @@ Last updated: 2026-08-08 +## gen-verilog: `_` discard in tuple destructure gets a throwaway reg (Refs #1948) + +- `(link1, _) = create_2node_mesh(50)` emitted `{_, link1} = call()` -- Verilog has no `_` wildcard, so iverilog could not find the variable +- Each `_` position now declares a throwaway reg keyed by (line, index) at its element width; the LHS concat references it +- tri-net corpus: icarus 94 -> 95 (mesh_node_sim) +- FROZEN_HASH resealed + ## test(gen-verilog): tuple contract expects width-cast operands (Refs #1948) - Follow-up to the tuple width-cast (#1973): the phase40 contract test's exact-string expectation updated to `{32'((a-b)), 32'((a+b))}` diff --git a/bootstrap/src/compiler.rs b/bootstrap/src/compiler.rs index bef447788..4ac4b6dee 100644 --- a/bootstrap/src/compiler.rs +++ b/bootstrap/src/compiler.rs @@ -8420,24 +8420,28 @@ impl VerilogCodegen { }) .unwrap_or_default(); for (idx, e) in target.children.iter().enumerate() { - if e.kind != NodeKind::ExprIdentifier - || e.name.is_empty() - || e.name == "_" - { + if e.kind != NodeKind::ExprIdentifier || e.name.is_empty() { continue; } - if declared.insert(e.name.clone()) { - let (w, signed) = elem_types - .get(idx) - .map(|t| { - (self.packed_width(t).max(1), self.packed_signed(t)) - }) - .unwrap_or((64, false)); + let (w, signed) = elem_types + .get(idx) + .map(|t| (self.packed_width(t).max(1), self.packed_signed(t))) + .unwrap_or((64, false)); + // A `_` element is a discard: Verilog has no + // wildcard, so give the position a throwaway reg + // keyed by (line, index) so it is stable across + // the decl pass and the assignment (t27#1948). + let decl_name = if e.name == "_" { + format!("__t27_disc_{}_{}", target.line, idx) + } else { + e.name.clone() + }; + if declared.insert(decl_name.clone()) { self.write_indent(); self.write_line(&format!( "{} {}; // t27#1948 tuple binding", reg_decl(w, signed), - Self::verilog_safe_identifier(&e.name) + Self::verilog_safe_identifier(&decl_name) )); } } @@ -9881,6 +9885,15 @@ impl VerilogCodegen { if i != last { self.write(", "); } + // LHS `_` discard: reference the throwaway reg declared for + // this (line, index) position (t27#1948). + if self.in_lvalue + && child.kind == NodeKind::ExprIdentifier + && child.name == "_" + { + self.write(&format!("__t27_disc_{}_{}", node.line, i)); + continue; + } let cast = elem_types .as_ref() .filter(|ts| ts.len() == node.children.len()) diff --git a/bootstrap/stage0/FROZEN_HASH b/bootstrap/stage0/FROZEN_HASH index 5f70d83f1..43c3dc2c4 100644 --- a/bootstrap/stage0/FROZEN_HASH +++ b/bootstrap/stage0/FROZEN_HASH @@ -1 +1 @@ -cf9e471fbec493af043262efe2c2098c04255994e64567ce04a9e734239d5742 +fbbe3e1568e833b001e94be2d4cb612011d41d1e38fb5b57bca42e91ca510fb4 diff --git a/docs/NOW.md b/docs/NOW.md index 1b7075b67..107fef24a 100644 --- a/docs/NOW.md +++ b/docs/NOW.md @@ -2,6 +2,13 @@ Last updated: 2026-08-08 +## gen-verilog: `_` discard in tuple destructure gets a throwaway reg (Refs #1948) + +- `(link1, _) = create_2node_mesh(50)` emitted `{_, link1} = call()` -- Verilog has no `_` wildcard, so iverilog could not find the variable +- Each `_` position now declares a throwaway reg keyed by (line, index) at its element width; the LHS concat references it +- tri-net corpus: icarus 94 -> 95 (mesh_node_sim) +- FROZEN_HASH resealed + ## test(gen-verilog): tuple contract expects width-cast operands (Refs #1948) - Follow-up to the tuple width-cast (#1973): the phase40 contract test's exact-string expectation updated to `{32'((a-b)), 32'((a+b))}`