From 2817cd3d58c0cd783e5a4261b0e768b028cc7f3d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 13 Nov 2025 23:42:37 -0800 Subject: [PATCH] support Rust's default binding modes when destructuring with #[hdl(sim)] let/match --- .../expand_aggregate_literals.rs | 3 + .../src/module/transform_body/expand_match.rs | 138 +++++++++++------- .../hdl_let_statements/destructuring.rs | 78 ++++++++++ .../module_bodies/hdl_match_statements.rs | 75 ++++++++++ crates/fayalite/src/sim/value.rs | 113 +++++++++++++- crates/fayalite/tests/sim.rs | 34 ++--- 6 files changed, 371 insertions(+), 70 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs index 1aabb19e..e30eb0fa 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs @@ -88,6 +88,9 @@ impl Visitor<'_> { field.expr = parse_quote_spanned! {field.member.span()=> ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)) }; + field + .colon_token + .get_or_insert(Token![:](field.member.span())); } return parse_quote_spanned! {name_span=> { diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index 069f00d2..ca06c0b1 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -10,7 +10,7 @@ use crate::{ }; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned}; -use std::collections::BTreeSet; +use std::collections::BTreeMap; use syn::{ Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, @@ -24,65 +24,65 @@ use syn::{ macro_rules! visit_trait { ( - $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)* + $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)* ) => { trait VisitMatchPat<'a> { - $(fn $fn(&mut self, $value: &'a $Value) { + $(fn $fn(&mut self, $value: &'a mut $Value) { $fn(self, $value); })* } - $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)* + $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)* }; } visit_trait! { - fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) { - let MatchPatBinding { ident: _ } = v; + fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) { + let MatchPatBinding { mutability: _, ident: _ } = v; } - fn visit_match_pat_wild(_state: _, v: &MatchPatWild) { + fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) { let MatchPatWild { underscore_token: _ } = v; } - fn visit_match_pat_rest(_state: _, v: &MatchPatRest) { + fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) { let MatchPatRest { dot2_token: _ } = v; } - fn visit_match_pat_paren(state: _, v: &MatchPatParen) { + fn visit_match_pat_paren(state: _, v: &mut MatchPatParen) { let MatchPatParen { paren_token: _, pat } = v; state.visit_match_pat(pat); } - fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen) { + fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen) { let MatchPatParen { paren_token: _, pat } = v; state.visit_match_pat_simple(pat); } - fn visit_match_pat_or(state: _, v: &MatchPatOr) { + fn visit_match_pat_or(state: _, v: &mut MatchPatOr) { let MatchPatOr { leading_vert: _, cases } = v; for v in cases { state.visit_match_pat(v); } } - fn visit_match_pat_or_simple(state: _, v: &MatchPatOr) { + fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr) { let MatchPatOr { leading_vert: _, cases } = v; for v in cases { state.visit_match_pat_simple(v); } } - fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) { + fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) { let MatchPatStructField { field_name: _, colon_token: _, pat } = v; state.visit_match_pat_simple(pat); } - fn visit_match_pat_struct(state: _, v: &MatchPatStruct) { + fn visit_match_pat_struct(state: _, v: &mut MatchPatStruct) { let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v; for v in fields { state.visit_match_pat_struct_field(v); } } - fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) { + fn visit_match_pat_tuple(state: _, v: &mut MatchPatTuple) { let MatchPatTuple { paren_token: _, fields } = v; for v in fields { state.visit_match_pat_simple(v); } } - fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) { + fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) { let MatchPatEnumVariant { match_span:_, sim:_, @@ -95,7 +95,7 @@ visit_trait! { state.visit_match_pat_simple(v); } } - fn visit_match_pat_simple(state: _, v: &MatchPatSimple) { + fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) { match v { MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v), @@ -104,7 +104,7 @@ visit_trait! { MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v), } } - fn visit_match_pat(state: _, v: &MatchPat) { + fn visit_match_pat(state: _, v: &mut MatchPat) { match v { MatchPat::Simple(v) => state.visit_match_pat_simple(v), MatchPat::Or(v) => state.visit_match_pat_or(v), @@ -118,13 +118,15 @@ visit_trait! { with_debug_clone_and_fold! { struct MatchPatBinding<> { + mutability: Option, ident: Ident, } } impl ToTokens for MatchPatBinding { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { ident } = self; + let Self { mutability, ident } = self; + mutability.to_tokens(tokens); ident.to_tokens(tokens); } } @@ -211,12 +213,20 @@ impl ToTokens for MatchPatStructField { colon_token, pat, } = self; - field_name.to_tokens(tokens); - if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) { + if let ( + None, + MatchPatSimple::Binding(MatchPatBinding { + mutability: _, + ident, + }), + ) = (colon_token, pat) + { if field_name == ident { + pat.to_tokens(tokens); return; } } + field_name.to_tokens(tokens); colon_token .unwrap_or_else(|| Token![:](field_name.span())) .to_tokens(tokens); @@ -450,7 +460,7 @@ trait ParseMatchPat: Sized { Pat::Ident(PatIdent { attrs: _, by_ref, - mutability, + mut mutability, ident, subpat, }) => { @@ -459,10 +469,13 @@ trait ParseMatchPat: Sized { .errors .error(by_ref, "ref not allowed in #[hdl] patterns"); } - if let Some(mutability) = mutability { - state - .errors - .error(mutability, "mut not allowed in #[hdl] patterns"); + if let Some(mut_token) = mutability { + if state.sim.is_none() { + state + .errors + .error(mut_token, "mut not allowed in #[hdl] patterns"); + mutability = None; // avoid duplicate errors + } } if let Some((at_token, _)) = subpat { state @@ -474,18 +487,26 @@ trait ParseMatchPat: Sized { variant_path, enum_path, variant_name, - }) => Self::enum_variant( - state, - MatchPatEnumVariant { - match_span: state.match_span, - sim: state.sim, - variant_path, - enum_path, - variant_name, - field: None, - }, - ), + }) => { + if let Some(mut_token) = mutability { + state + .errors + .error(mut_token, "mut not allowed on unit variants"); + } + Self::enum_variant( + state, + MatchPatEnumVariant { + match_span: state.match_span, + sim: state.sim, + variant_path, + enum_path, + variant_name, + field: None, + }, + ) + } Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding { + mutability, ident, }))), } @@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> { struct HdlLetPatVisitState<'a> { errors: &'a mut Errors, - bindings: BTreeSet<&'a Ident>, + bindings: BTreeMap, } impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { - fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) { - self.bindings.insert(&v.ident); + fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) { + self.bindings.insert(v.ident.clone(), v.clone()); + v.mutability = None; } - fn visit_match_pat_or(&mut self, v: &'a MatchPatOr) { + fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr) { if let Some(first_inner_vert) = v.first_inner_vert() { self.errors.error( first_inner_vert, @@ -998,7 +1020,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { visit_match_pat_or(self, v); } - fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr) { + fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr) { if let Some(first_inner_vert) = v.first_inner_vert() { self.errors.error( first_inner_vert, @@ -1008,7 +1030,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> { visit_match_pat_or_simple(self, v); } - fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) { + fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) { self.errors.error(v, "refutable pattern in let statement"); } } @@ -1048,7 +1070,7 @@ impl Visitor<'_> { .error(else_, "#[hdl] let ... else { ... } is not implemented"); return empty_let(); } - let Ok(pat) = MatchPat::parse( + let Ok(mut pat) = MatchPat::parse( &mut HdlMatchParseState { sim, match_span: span, @@ -1060,20 +1082,27 @@ impl Visitor<'_> { }; let mut state = HdlLetPatVisitState { errors: &mut self.errors, - bindings: BTreeSet::new(), + bindings: BTreeMap::new(), }; - state.visit_match_pat(&pat); + state.visit_match_pat(&mut pat); let HdlLetPatVisitState { errors: _, bindings, } = state; + let bindings_idents = bindings.keys(); + let bindings = bindings.values(); let retval = if sim.is_some() { parse_quote_spanned! {span=> let (#(#bindings,)*) = { type __MatchTy = ::SimValue; - let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); - #let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token - (#(#bindings,)*) + let __match_value = #expr; + let __match_value = { + use ::fayalite::sim::value::match_sim_value::*; + // use method syntax to deduce the correct trait to call + ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value() + }; + #let_token #pat #eq_token __match_value #semi_token + (#(#bindings_idents,)*) }; } } else { @@ -1105,7 +1134,7 @@ impl Visitor<'_> { __match_variant, ); #let_token #pat #eq_token __match_variant #semi_token - (#(#bindings,)* __scope,) + (#(#bindings_idents,)* __scope,) }; } }; @@ -1142,8 +1171,13 @@ impl Visitor<'_> { quote_spanned! {span=> { type __MatchTy = ::SimValue; - let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr)); - #match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) { + let __match_value = #expr; + let __match_value = { + use ::fayalite::sim::value::match_sim_value::*; + // use method syntax to deduce the correct trait to call + ::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value() + }; + #match_token __match_value { #(#arms)* } } diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs index 1fc4705c..8d70d21c 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/destructuring.rs @@ -31,3 +31,81 @@ //! } //! } //! ``` +//! +//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]). +//! +//! [`SimValue`]: crate::sim::value::SimValue +//! [`ToSimValue`]: crate::sim::value::ToSimValue +//! +//! ``` +//! # use fayalite::prelude::*; +//! #[hdl] +//! struct MyStruct { +//! a: UInt<8>, +//! b: Bool, +//! c: T, +//! } +//! +//! #[hdl] +//! fn destructure(v: SimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! mut b, +//! c, +//! } = v; +//! +//! // that gives these types: +//! let _: SimValue> = a; +//! let _: SimValue = b; +//! let _: SimValue = c; +//! *b = false; // can modify b since mut was used +//! } +//! +//! #[hdl] +//! fn destructure_ref<'a, T: Type>(v: &'a SimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! b, +//! c, +//! } = v; +//! +//! // that gives these types: +//! let _: &'a SimValue> = a; +//! let _: &'a SimValue = b; +//! let _: &'a SimValue = c; +//! } +//! +//! #[hdl] +//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! b, +//! c, +//! } = v; +//! +//! **b = true; // you can modify v by modifying b which borrows from it +//! +//! // that gives these types: +//! let _: &'a mut SimValue> = a; +//! let _: &'a mut SimValue = b; +//! let _: &'a mut SimValue = c; +//! } +//! +//! #[hdl] +//! fn destructure_to_sim_value<'a, T: Type>(v: impl ToSimValue>) { +//! #[hdl(sim)] +//! let MyStruct:: { +//! a, +//! b, +//! c, +//! } = v; +//! +//! // that gives these types: +//! let _: SimValue> = a; +//! let _: SimValue = b; +//! let _: SimValue = c; +//! } +//! ``` diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs index 6df70f1a..accd3d78 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs @@ -9,3 +9,78 @@ //! //! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now, //! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`. +//! +//! You can also use `#[hdl(sim)] match` to match [`SimValue`]s (or anything that implements [`ToSimValue`]). +//! +//! `#[hdl(sim)] match` statements' bodies may have any type, unlike `#[hdl] match`. +//! +//! [`SimValue`]: crate::sim::value::SimValue +//! [`ToSimValue`]: crate::sim::value::ToSimValue +//! +//! ``` +//! # use fayalite::prelude::*; +//! #[hdl] +//! enum MyEnum { +//! A, +//! B(Bool), +//! C(T), +//! } +//! +//! #[hdl] +//! fn match_move(v: SimValue>) -> String { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => String::from("got A"), +//! MyEnum::::B(mut b) => { +//! let _: SimValue = b; // b has this type +//! let text = format!("got B({b})"); +//! *b = true; // can modify b since mut was used +//! text +//! } +//! _ => String::from("something else"), +//! } +//! } +//! +//! #[hdl] +//! fn match_ref<'a, T: Type>(v: &'a SimValue>) -> u32 { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => 1, +//! MyEnum::::B(b) => { +//! let _: &'a SimValue = b; // b has this type +//! println!("got B({b})"); +//! 5 +//! } +//! _ => 42, +//! } +//! } +//! +//! #[hdl] +//! fn match_mut<'a, T: Type>(v: &'a mut SimValue>) -> Option<&'a mut SimValue> { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => None, +//! MyEnum::::B(b) => { +//! println!("got B({b})"); +//! **b = true; // you can modify v by modifying b which borrows from it +//! let _: &'a mut SimValue = b; // b has this type +//! None +//! } +//! MyEnum::::C(v) => Some(v), // you can return matched values +//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm +//! } +//! } +//! +//! #[hdl] +//! fn match_to_sim_value<'a, T: Type>(v: impl ToSimValue>) { +//! #[hdl(sim)] +//! match v { +//! MyEnum::::A => println!("got A"), +//! MyEnum::::B(b) => { +//! let _: SimValue = b; // b has this type +//! println!("got B({b})"); +//! } +//! _ => println!("something else"), +//! } +//! } +//! ``` diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index f1fc40c0..ff836d52 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -492,6 +492,117 @@ impl SimValuePartialEq for AsyncReset { } } +#[doc(hidden)] +pub mod match_sim_value { + use crate::{ + sim::value::{SimValue, ToSimValue}, + ty::Type, + }; + + #[doc(hidden)] + pub struct MatchSimValueHelper(Option); + + impl MatchSimValueHelper { + pub fn new(v: T) -> Self { + Self(Some(v)) + } + } + + #[doc(hidden)] + pub trait MatchSimValue { + type MatchValue; + + /// use `self` so it comes first in the method resolution order + fn __fayalite_match_sim_value(self) -> Self::MatchValue + where + Self: Sized; + } + + impl MatchSimValue for MatchSimValueHelper> { + type MatchValue = T::SimValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + SimValue::into_value(self.0.expect("should be Some")) + } + } + + impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a SimValue> { + type MatchValue = &'a T::SimValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + SimValue::value(self.0.expect("should be Some")) + } + } + + impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a mut SimValue> { + type MatchValue = &'a mut T::SimValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + SimValue::value_mut(self.0.expect("should be Some")) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ &'a T> + where + MatchSimValueHelper<&'a T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v))) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ mut &'a T> + where + MatchSimValueHelper<&'a T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v))) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a &'_ mut T> + where + MatchSimValueHelper<&'a T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &**v))) + } + } + + impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a mut &'_ mut T> + where + MatchSimValueHelper<&'a mut T>: MatchSimValue, + { + type MatchValue = as MatchSimValue>::MatchValue; + + fn __fayalite_match_sim_value(self) -> Self::MatchValue { + MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &mut **v))) + } + } + + #[doc(hidden)] + pub trait MatchSimValueFallback { + type MatchValue; + + /// use `&mut self` so it comes later in the method resolution order than MatchSimValue + fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue; + } + + impl MatchSimValueFallback for MatchSimValueHelper { + type MatchValue = ::SimValue; + + fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue { + SimValue::into_value(self.0.take().expect("should be Some").into_sim_value()) + } + } +} + pub trait ToSimValue: ToSimValueWithType<::Type> { type Type: Type; @@ -624,7 +735,7 @@ impl ToSimValueWithType for BitSlice { } } -impl ToSimValue for &'_ This { +impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This { type Type = This::Type; fn to_sim_value(&self) -> SimValue { diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index b4169c68..be4e2071 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -506,10 +506,10 @@ fn test_enums() { data_out: _, b_out: _, b2_out: _, - } = expected; - sim.write(sim.io().en, &en); - sim.write(sim.io().which_in, &which_in); - sim.write(sim.io().data_in, &data_in); + } = &expected; + sim.write(sim.io().en, en); + sim.write(sim.io().which_in, which_in); + sim.write(sim.io().data_in, data_in); let io = #[hdl(sim)] IO::<_> { en, @@ -528,7 +528,7 @@ fn test_enums() { ); // make sure matching on SimValue works #[hdl(sim)] - match io.b_out { + match &io.b_out { HdlNone => println!("io.b_out is HdlNone"), HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1), } @@ -706,13 +706,13 @@ fn test_memories() { w_en, w_data, w_mask, - } = expected; - sim.write(sim.io().r.addr, &r_addr); - sim.write(sim.io().r.en, &r_en); - sim.write(sim.io().w.addr, &w_addr); - sim.write(sim.io().w.en, &w_en); - sim.write(sim.io().w.data, &w_data); - sim.write(sim.io().w.mask, &w_mask); + } = &expected; + sim.write(sim.io().r.addr, r_addr); + sim.write(sim.io().r.en, r_en); + sim.write(sim.io().w.addr, w_addr); + sim.write(sim.io().w.en, w_en); + sim.write(sim.io().w.data, w_data); + sim.write(sim.io().w.mask, w_mask); let io = #[hdl(sim)] IO { r_addr, @@ -1505,7 +1505,7 @@ fn test_many_memories() { w_en, w_data, w_mask, - } = expected; + } = &expected; for (((r, w), w_data), w_mask) in sim .io() .r @@ -1514,10 +1514,10 @@ fn test_many_memories() { .zip(w_data.iter()) .zip(w_mask.iter()) { - sim.write(r.addr, &r_addr); - sim.write(r.en, &r_en); - sim.write(w.addr, &w_addr); - sim.write(w.en, &w_en); + sim.write(r.addr, r_addr); + sim.write(r.en, r_en); + sim.write(w.addr, w_addr); + sim.write(w.en, w_en); sim.write(w.data, w_data); sim.write(w.mask, w_mask); }