forked from libre-chip/fayalite
support Rust's default binding modes when destructuring with #[hdl(sim)] let/match
This commit is contained in:
parent
053c1b2b10
commit
2817cd3d58
6 changed files with 371 additions and 70 deletions
|
|
@ -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<T> {
|
||||
//! a: UInt<8>,
|
||||
//! b: Bool,
|
||||
//! c: T,
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! fn destructure<T: Type>(v: SimValue<MyStruct<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! let MyStruct::<T> {
|
||||
//! a,
|
||||
//! mut b,
|
||||
//! c,
|
||||
//! } = v;
|
||||
//!
|
||||
//! // that gives these types:
|
||||
//! let _: SimValue<UInt<8>> = a;
|
||||
//! let _: SimValue<Bool> = b;
|
||||
//! let _: SimValue<T> = c;
|
||||
//! *b = false; // can modify b since mut was used
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! fn destructure_ref<'a, T: Type>(v: &'a SimValue<MyStruct<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! let MyStruct::<T> {
|
||||
//! a,
|
||||
//! b,
|
||||
//! c,
|
||||
//! } = v;
|
||||
//!
|
||||
//! // that gives these types:
|
||||
//! let _: &'a SimValue<UInt<8>> = a;
|
||||
//! let _: &'a SimValue<Bool> = b;
|
||||
//! let _: &'a SimValue<T> = c;
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue<MyStruct<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! let MyStruct::<T> {
|
||||
//! 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<UInt<8>> = a;
|
||||
//! let _: &'a mut SimValue<Bool> = b;
|
||||
//! let _: &'a mut SimValue<T> = c;
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! fn destructure_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyStruct<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! let MyStruct::<T> {
|
||||
//! a,
|
||||
//! b,
|
||||
//! c,
|
||||
//! } = v;
|
||||
//!
|
||||
//! // that gives these types:
|
||||
//! let _: SimValue<UInt<8>> = a;
|
||||
//! let _: SimValue<Bool> = b;
|
||||
//! let _: SimValue<T> = c;
|
||||
//! }
|
||||
//! ```
|
||||
|
|
|
|||
|
|
@ -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<T> {
|
||||
//! A,
|
||||
//! B(Bool),
|
||||
//! C(T),
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! fn match_move<T: Type>(v: SimValue<MyEnum<T>>) -> String {
|
||||
//! #[hdl(sim)]
|
||||
//! match v {
|
||||
//! MyEnum::<T>::A => String::from("got A"),
|
||||
//! MyEnum::<T>::B(mut b) => {
|
||||
//! let _: SimValue<Bool> = 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<MyEnum<T>>) -> u32 {
|
||||
//! #[hdl(sim)]
|
||||
//! match v {
|
||||
//! MyEnum::<T>::A => 1,
|
||||
//! MyEnum::<T>::B(b) => {
|
||||
//! let _: &'a SimValue<Bool> = b; // b has this type
|
||||
//! println!("got B({b})");
|
||||
//! 5
|
||||
//! }
|
||||
//! _ => 42,
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! fn match_mut<'a, T: Type>(v: &'a mut SimValue<MyEnum<T>>) -> Option<&'a mut SimValue<T>> {
|
||||
//! #[hdl(sim)]
|
||||
//! match v {
|
||||
//! MyEnum::<T>::A => None,
|
||||
//! MyEnum::<T>::B(b) => {
|
||||
//! println!("got B({b})");
|
||||
//! **b = true; // you can modify v by modifying b which borrows from it
|
||||
//! let _: &'a mut SimValue<Bool> = b; // b has this type
|
||||
//! None
|
||||
//! }
|
||||
//! MyEnum::<T>::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<Type = MyEnum<T>>) {
|
||||
//! #[hdl(sim)]
|
||||
//! match v {
|
||||
//! MyEnum::<T>::A => println!("got A"),
|
||||
//! MyEnum::<T>::B(b) => {
|
||||
//! let _: SimValue<Bool> = b; // b has this type
|
||||
//! println!("got B({b})");
|
||||
//! }
|
||||
//! _ => println!("something else"),
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
|
|
|||
|
|
@ -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<T>(Option<T>);
|
||||
|
||||
impl<T> MatchSimValueHelper<T> {
|
||||
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<T: Type> MatchSimValue for MatchSimValueHelper<SimValue<T>> {
|
||||
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<T>> {
|
||||
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<T>> {
|
||||
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 = <MatchSimValueHelper<&'a T> 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 = <MatchSimValueHelper<&'a T> 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 = <MatchSimValueHelper<&'a T> 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 = <MatchSimValueHelper<&'a mut T> 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<T: ToSimValue> MatchSimValueFallback for MatchSimValueHelper<T> {
|
||||
type MatchValue = <T::Type as Type>::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<<Self as ToSimValue>::Type> {
|
||||
type Type: Type;
|
||||
|
||||
|
|
@ -624,7 +735,7 @@ impl<T: Type> ToSimValueWithType<T> for BitSlice {
|
|||
}
|
||||
}
|
||||
|
||||
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
|
||||
impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This {
|
||||
type Type = This::Type;
|
||||
|
||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||
|
|
|
|||
|
|
@ -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<SomeEnum> 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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue