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
|
|
@ -88,6 +88,9 @@ impl Visitor<'_> {
|
||||||
field.expr = parse_quote_spanned! {field.member.span()=>
|
field.expr = parse_quote_spanned! {field.member.span()=>
|
||||||
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
|
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
|
||||||
};
|
};
|
||||||
|
field
|
||||||
|
.colon_token
|
||||||
|
.get_or_insert(Token));
|
||||||
}
|
}
|
||||||
return parse_quote_spanned! {name_span=>
|
return parse_quote_spanned! {name_span=>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
|
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeMap;
|
||||||
use syn::{
|
use syn::{
|
||||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
|
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
|
||||||
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
|
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
|
||||||
|
|
@ -24,65 +24,65 @@ use syn::{
|
||||||
|
|
||||||
macro_rules! visit_trait {
|
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> {
|
trait VisitMatchPat<'a> {
|
||||||
$(fn $fn(&mut self, $value: &'a $Value) {
|
$(fn $fn(&mut self, $value: &'a mut $Value) {
|
||||||
$fn(self, $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! {
|
visit_trait! {
|
||||||
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
|
fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) {
|
||||||
let MatchPatBinding { ident: _ } = v;
|
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;
|
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;
|
let MatchPatRest { dot2_token: _ } = v;
|
||||||
}
|
}
|
||||||
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
|
fn visit_match_pat_paren(state: _, v: &mut MatchPatParen<MatchPat>) {
|
||||||
let MatchPatParen { paren_token: _, pat } = v;
|
let MatchPatParen { paren_token: _, pat } = v;
|
||||||
state.visit_match_pat(pat);
|
state.visit_match_pat(pat);
|
||||||
}
|
}
|
||||||
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
|
fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen<MatchPatSimple>) {
|
||||||
let MatchPatParen { paren_token: _, pat } = v;
|
let MatchPatParen { paren_token: _, pat } = v;
|
||||||
state.visit_match_pat_simple(pat);
|
state.visit_match_pat_simple(pat);
|
||||||
}
|
}
|
||||||
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
|
fn visit_match_pat_or(state: _, v: &mut MatchPatOr<MatchPat>) {
|
||||||
let MatchPatOr { leading_vert: _, cases } = v;
|
let MatchPatOr { leading_vert: _, cases } = v;
|
||||||
for v in cases {
|
for v in cases {
|
||||||
state.visit_match_pat(v);
|
state.visit_match_pat(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
|
fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr<MatchPatSimple>) {
|
||||||
let MatchPatOr { leading_vert: _, cases } = v;
|
let MatchPatOr { leading_vert: _, cases } = v;
|
||||||
for v in cases {
|
for v in cases {
|
||||||
state.visit_match_pat_simple(v);
|
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;
|
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
|
||||||
state.visit_match_pat_simple(pat);
|
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;
|
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
|
||||||
for v in fields {
|
for v in fields {
|
||||||
state.visit_match_pat_struct_field(v);
|
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;
|
let MatchPatTuple { paren_token: _, fields } = v;
|
||||||
for v in fields {
|
for v in fields {
|
||||||
state.visit_match_pat_simple(v);
|
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 {
|
let MatchPatEnumVariant {
|
||||||
match_span:_,
|
match_span:_,
|
||||||
sim:_,
|
sim:_,
|
||||||
|
|
@ -95,7 +95,7 @@ visit_trait! {
|
||||||
state.visit_match_pat_simple(v);
|
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 {
|
match v {
|
||||||
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
|
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
|
||||||
MatchPatSimple::Or(v) => state.visit_match_pat_or_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),
|
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 {
|
match v {
|
||||||
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
|
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
|
||||||
MatchPat::Or(v) => state.visit_match_pat_or(v),
|
MatchPat::Or(v) => state.visit_match_pat_or(v),
|
||||||
|
|
@ -118,13 +118,15 @@ visit_trait! {
|
||||||
|
|
||||||
with_debug_clone_and_fold! {
|
with_debug_clone_and_fold! {
|
||||||
struct MatchPatBinding<> {
|
struct MatchPatBinding<> {
|
||||||
|
mutability: Option<Token![mut]>,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for MatchPatBinding {
|
impl ToTokens for MatchPatBinding {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
let Self { ident } = self;
|
let Self { mutability, ident } = self;
|
||||||
|
mutability.to_tokens(tokens);
|
||||||
ident.to_tokens(tokens);
|
ident.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,12 +213,20 @@ impl ToTokens for MatchPatStructField {
|
||||||
colon_token,
|
colon_token,
|
||||||
pat,
|
pat,
|
||||||
} = self;
|
} = self;
|
||||||
field_name.to_tokens(tokens);
|
if let (
|
||||||
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
|
None,
|
||||||
|
MatchPatSimple::Binding(MatchPatBinding {
|
||||||
|
mutability: _,
|
||||||
|
ident,
|
||||||
|
}),
|
||||||
|
) = (colon_token, pat)
|
||||||
|
{
|
||||||
if field_name == ident {
|
if field_name == ident {
|
||||||
|
pat.to_tokens(tokens);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
field_name.to_tokens(tokens);
|
||||||
colon_token
|
colon_token
|
||||||
.unwrap_or_else(|| Token))
|
.unwrap_or_else(|| Token))
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
|
|
@ -450,7 +460,7 @@ trait ParseMatchPat: Sized {
|
||||||
Pat::Ident(PatIdent {
|
Pat::Ident(PatIdent {
|
||||||
attrs: _,
|
attrs: _,
|
||||||
by_ref,
|
by_ref,
|
||||||
mutability,
|
mut mutability,
|
||||||
ident,
|
ident,
|
||||||
subpat,
|
subpat,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -459,10 +469,13 @@ trait ParseMatchPat: Sized {
|
||||||
.errors
|
.errors
|
||||||
.error(by_ref, "ref not allowed in #[hdl] patterns");
|
.error(by_ref, "ref not allowed in #[hdl] patterns");
|
||||||
}
|
}
|
||||||
if let Some(mutability) = mutability {
|
if let Some(mut_token) = mutability {
|
||||||
state
|
if state.sim.is_none() {
|
||||||
.errors
|
state
|
||||||
.error(mutability, "mut not allowed in #[hdl] patterns");
|
.errors
|
||||||
|
.error(mut_token, "mut not allowed in #[hdl] patterns");
|
||||||
|
mutability = None; // avoid duplicate errors
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some((at_token, _)) = subpat {
|
if let Some((at_token, _)) = subpat {
|
||||||
state
|
state
|
||||||
|
|
@ -474,18 +487,26 @@ trait ParseMatchPat: Sized {
|
||||||
variant_path,
|
variant_path,
|
||||||
enum_path,
|
enum_path,
|
||||||
variant_name,
|
variant_name,
|
||||||
}) => Self::enum_variant(
|
}) => {
|
||||||
state,
|
if let Some(mut_token) = mutability {
|
||||||
MatchPatEnumVariant {
|
state
|
||||||
match_span: state.match_span,
|
.errors
|
||||||
sim: state.sim,
|
.error(mut_token, "mut not allowed on unit variants");
|
||||||
variant_path,
|
}
|
||||||
enum_path,
|
Self::enum_variant(
|
||||||
variant_name,
|
state,
|
||||||
field: None,
|
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 {
|
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
||||||
|
mutability,
|
||||||
ident,
|
ident,
|
||||||
}))),
|
}))),
|
||||||
}
|
}
|
||||||
|
|
@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> {
|
||||||
|
|
||||||
struct HdlLetPatVisitState<'a> {
|
struct HdlLetPatVisitState<'a> {
|
||||||
errors: &'a mut Errors,
|
errors: &'a mut Errors,
|
||||||
bindings: BTreeSet<&'a Ident>,
|
bindings: BTreeMap<Ident, MatchPatBinding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||||
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
|
fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) {
|
||||||
self.bindings.insert(&v.ident);
|
self.bindings.insert(v.ident.clone(), v.clone());
|
||||||
|
v.mutability = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
|
fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr<MatchPat>) {
|
||||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||||
self.errors.error(
|
self.errors.error(
|
||||||
first_inner_vert,
|
first_inner_vert,
|
||||||
|
|
@ -998,7 +1020,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||||
visit_match_pat_or(self, v);
|
visit_match_pat_or(self, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
|
fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr<MatchPatSimple>) {
|
||||||
if let Some(first_inner_vert) = v.first_inner_vert() {
|
if let Some(first_inner_vert) = v.first_inner_vert() {
|
||||||
self.errors.error(
|
self.errors.error(
|
||||||
first_inner_vert,
|
first_inner_vert,
|
||||||
|
|
@ -1008,7 +1030,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
|
||||||
visit_match_pat_or_simple(self, v);
|
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");
|
self.errors.error(v, "refutable pattern in let statement");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1048,7 +1070,7 @@ impl Visitor<'_> {
|
||||||
.error(else_, "#[hdl] let ... else { ... } is not implemented");
|
.error(else_, "#[hdl] let ... else { ... } is not implemented");
|
||||||
return empty_let();
|
return empty_let();
|
||||||
}
|
}
|
||||||
let Ok(pat) = MatchPat::parse(
|
let Ok(mut pat) = MatchPat::parse(
|
||||||
&mut HdlMatchParseState {
|
&mut HdlMatchParseState {
|
||||||
sim,
|
sim,
|
||||||
match_span: span,
|
match_span: span,
|
||||||
|
|
@ -1060,20 +1082,27 @@ impl Visitor<'_> {
|
||||||
};
|
};
|
||||||
let mut state = HdlLetPatVisitState {
|
let mut state = HdlLetPatVisitState {
|
||||||
errors: &mut self.errors,
|
errors: &mut self.errors,
|
||||||
bindings: BTreeSet::new(),
|
bindings: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
state.visit_match_pat(&pat);
|
state.visit_match_pat(&mut pat);
|
||||||
let HdlLetPatVisitState {
|
let HdlLetPatVisitState {
|
||||||
errors: _,
|
errors: _,
|
||||||
bindings,
|
bindings,
|
||||||
} = state;
|
} = state;
|
||||||
|
let bindings_idents = bindings.keys();
|
||||||
|
let bindings = bindings.values();
|
||||||
let retval = if sim.is_some() {
|
let retval = if sim.is_some() {
|
||||||
parse_quote_spanned! {span=>
|
parse_quote_spanned! {span=>
|
||||||
let (#(#bindings,)*) = {
|
let (#(#bindings,)*) = {
|
||||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
||||||
let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
|
let __match_value = #expr;
|
||||||
#let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
|
let __match_value = {
|
||||||
(#(#bindings,)*)
|
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 {
|
} else {
|
||||||
|
|
@ -1105,7 +1134,7 @@ impl Visitor<'_> {
|
||||||
__match_variant,
|
__match_variant,
|
||||||
);
|
);
|
||||||
#let_token #pat #eq_token __match_variant #semi_token
|
#let_token #pat #eq_token __match_variant #semi_token
|
||||||
(#(#bindings,)* __scope,)
|
(#(#bindings_idents,)* __scope,)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1142,8 +1171,13 @@ impl Visitor<'_> {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
{
|
{
|
||||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
|
||||||
let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
|
let __match_value = #expr;
|
||||||
#match_token ::fayalite::sim::value::SimValue::into_value(__match_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)*
|
#(#arms)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
//! `#[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(_))`.
|
//! 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> {
|
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
|
||||||
type Type: 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;
|
type Type = This::Type;
|
||||||
|
|
||||||
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
||||||
|
|
|
||||||
|
|
@ -506,10 +506,10 @@ fn test_enums() {
|
||||||
data_out: _,
|
data_out: _,
|
||||||
b_out: _,
|
b_out: _,
|
||||||
b2_out: _,
|
b2_out: _,
|
||||||
} = expected;
|
} = &expected;
|
||||||
sim.write(sim.io().en, &en);
|
sim.write(sim.io().en, en);
|
||||||
sim.write(sim.io().which_in, &which_in);
|
sim.write(sim.io().which_in, which_in);
|
||||||
sim.write(sim.io().data_in, &data_in);
|
sim.write(sim.io().data_in, data_in);
|
||||||
let io = #[hdl(sim)]
|
let io = #[hdl(sim)]
|
||||||
IO::<_> {
|
IO::<_> {
|
||||||
en,
|
en,
|
||||||
|
|
@ -528,7 +528,7 @@ fn test_enums() {
|
||||||
);
|
);
|
||||||
// make sure matching on SimValue<SomeEnum> works
|
// make sure matching on SimValue<SomeEnum> works
|
||||||
#[hdl(sim)]
|
#[hdl(sim)]
|
||||||
match io.b_out {
|
match &io.b_out {
|
||||||
HdlNone => println!("io.b_out is HdlNone"),
|
HdlNone => println!("io.b_out is HdlNone"),
|
||||||
HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1),
|
HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1),
|
||||||
}
|
}
|
||||||
|
|
@ -706,13 +706,13 @@ fn test_memories() {
|
||||||
w_en,
|
w_en,
|
||||||
w_data,
|
w_data,
|
||||||
w_mask,
|
w_mask,
|
||||||
} = expected;
|
} = &expected;
|
||||||
sim.write(sim.io().r.addr, &r_addr);
|
sim.write(sim.io().r.addr, r_addr);
|
||||||
sim.write(sim.io().r.en, &r_en);
|
sim.write(sim.io().r.en, r_en);
|
||||||
sim.write(sim.io().w.addr, &w_addr);
|
sim.write(sim.io().w.addr, w_addr);
|
||||||
sim.write(sim.io().w.en, &w_en);
|
sim.write(sim.io().w.en, w_en);
|
||||||
sim.write(sim.io().w.data, &w_data);
|
sim.write(sim.io().w.data, w_data);
|
||||||
sim.write(sim.io().w.mask, &w_mask);
|
sim.write(sim.io().w.mask, w_mask);
|
||||||
let io = #[hdl(sim)]
|
let io = #[hdl(sim)]
|
||||||
IO {
|
IO {
|
||||||
r_addr,
|
r_addr,
|
||||||
|
|
@ -1505,7 +1505,7 @@ fn test_many_memories() {
|
||||||
w_en,
|
w_en,
|
||||||
w_data,
|
w_data,
|
||||||
w_mask,
|
w_mask,
|
||||||
} = expected;
|
} = &expected;
|
||||||
for (((r, w), w_data), w_mask) in sim
|
for (((r, w), w_data), w_mask) in sim
|
||||||
.io()
|
.io()
|
||||||
.r
|
.r
|
||||||
|
|
@ -1514,10 +1514,10 @@ fn test_many_memories() {
|
||||||
.zip(w_data.iter())
|
.zip(w_data.iter())
|
||||||
.zip(w_mask.iter())
|
.zip(w_mask.iter())
|
||||||
{
|
{
|
||||||
sim.write(r.addr, &r_addr);
|
sim.write(r.addr, r_addr);
|
||||||
sim.write(r.en, &r_en);
|
sim.write(r.en, r_en);
|
||||||
sim.write(w.addr, &w_addr);
|
sim.write(w.addr, w_addr);
|
||||||
sim.write(w.en, &w_en);
|
sim.write(w.en, w_en);
|
||||||
sim.write(w.data, w_data);
|
sim.write(w.data, w_data);
|
||||||
sim.write(w.mask, w_mask);
|
sim.write(w.mask, w_mask);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue