// SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ fold::{impl_fold, DoFold}, kw, module::transform_body::{empty_let, with_debug_clone_and_fold, wrap_ty_with_expr, Visitor}, Errors, HdlAttr, PairsIterExt, }; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt}; use std::collections::BTreeSet; use syn::{ fold::{fold_arm, fold_expr_match, fold_local, fold_pat, Fold}, parse::Nothing, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::{Brace, Paren}, Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr, PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath, }; macro_rules! visit_trait { ( $($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)* ) => { trait VisitMatchPat<'a> { $(fn $fn(&mut self, $value: &'a $Value) { $fn(self, $value); })* } $($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)* }; } visit_trait! { fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) { let MatchPatBinding { ident: _ } = v; } fn visit_match_pat_wild(_state: _, v: &MatchPatWild) { let MatchPatWild { underscore_token: _ } = v; } fn visit_match_pat_rest(_state: _, v: &MatchPatRest) { let MatchPatRest { dot2_token: _ } = v; } fn visit_match_pat_paren(state: _, v: &MatchPatParen) { let MatchPatParen { paren_token: _, pat } = v; state.visit_match_pat(pat); } fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen) { let MatchPatParen { paren_token: _, pat } = v; state.visit_match_pat_simple(pat); } fn visit_match_pat_or(state: _, v: &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) { 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) { let MatchPatStructField { field_name: _, colon_token: _, pat } = v; state.visit_match_pat_simple(pat); } fn visit_match_pat_struct(state: _, v: &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) { 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) { let MatchPatEnumVariant {match_span:_, variant_path: _, enum_path: _, variant_name: _, field } = v; if let Some((_, v)) = field { state.visit_match_pat_simple(v); } } fn visit_match_pat_simple(state: _, v: &MatchPatSimple) { match v { MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v), MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v), MatchPatSimple::Binding(v) => state.visit_match_pat_binding(v), MatchPatSimple::Wild(v) => state.visit_match_pat_wild(v), MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v), } } fn visit_match_pat(state: _, v: &MatchPat) { match v { MatchPat::Simple(v) => state.visit_match_pat_simple(v), MatchPat::Or(v) => state.visit_match_pat_or(v), MatchPat::Paren(v) => state.visit_match_pat_paren(v), MatchPat::Struct(v) => state.visit_match_pat_struct(v), MatchPat::Tuple(v) => state.visit_match_pat_tuple(v), MatchPat::EnumVariant(v) => state.visit_match_pat_enum_variant(v), } } } with_debug_clone_and_fold! { struct MatchPatBinding<> { ident: Ident, } } impl ToTokens for MatchPatBinding { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { ident } = self; ident.to_tokens(tokens); } } with_debug_clone_and_fold! { struct MatchPatParen

{ paren_token: Paren, pat: Box

, } } impl ToTokens for MatchPatParen

{ fn to_tokens(&self, tokens: &mut TokenStream) { let Self { paren_token, pat } = self; paren_token.surround(tokens, |tokens| pat.to_tokens(tokens)); } } with_debug_clone_and_fold! { struct MatchPatOr

{ leading_vert: Option, cases: Punctuated, } } impl

MatchPatOr

{ /// returns the first `|` between two patterns fn first_inner_vert(&self) -> Option { let mut pairs = self.cases.pairs(); pairs.next_back(); pairs.next().and_then(|v| v.into_tuple().1.copied()) } } impl ToTokens for MatchPatOr

{ fn to_tokens(&self, tokens: &mut TokenStream) { let Self { leading_vert, cases, } = self; leading_vert.to_tokens(tokens); cases.to_tokens(tokens); } } with_debug_clone_and_fold! { struct MatchPatWild<> { underscore_token: Token![_], } } impl ToTokens for MatchPatWild { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { underscore_token } = self; underscore_token.to_tokens(tokens); } } with_debug_clone_and_fold! { struct MatchPatRest<> { dot2_token: Token![..], } } impl ToTokens for MatchPatRest { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { dot2_token } = self; dot2_token.to_tokens(tokens); } } with_debug_clone_and_fold! { struct MatchPatStructField<> { field_name: Ident, colon_token: Option, pat: MatchPatSimple, } } impl ToTokens for MatchPatStructField { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { field_name, colon_token, pat, } = self; field_name.to_tokens(tokens); if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) { if field_name == ident { return; } } colon_token .unwrap_or_else(|| Token![:](field_name.span())) .to_tokens(tokens); pat.to_tokens(tokens); } } impl MatchPatStructField { fn parse(state: &mut HdlMatchParseState<'_>, field_pat: FieldPat) -> Result { let FieldPat { attrs: _, member, colon_token, pat, } = field_pat; let field_name = if let Member::Named(field_name) = member { field_name } else { state .errors .error(&member, "field name must not be a number"); format_ident!("_{}", member) }; Ok(Self { field_name, colon_token, pat: MatchPatSimple::parse(state, *pat)?, }) } } with_debug_clone_and_fold! { struct MatchPatStruct<> { match_span: Span, path: Path, brace_token: Brace, fields: Punctuated, rest: Option, } } impl ToTokens for MatchPatStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { match_span, path, brace_token, fields, rest, } = self; quote_spanned! {*match_span=> __MatchTy::<#path> } .to_tokens(tokens); brace_token.surround(tokens, |tokens| { fields.to_tokens(tokens); rest.to_tokens(tokens); }) } } with_debug_clone_and_fold! { struct MatchPatTuple<> { paren_token: Paren, fields: Punctuated, } } impl ToTokens for MatchPatTuple { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { paren_token, fields, } = self; paren_token.surround(tokens, |tokens| { fields.to_tokens(tokens); }) } } with_debug_clone_and_fold! { struct MatchPatEnumVariant<> { match_span: Span, variant_path: Path, enum_path: Path, variant_name: Ident, field: Option<(Paren, MatchPatSimple)>, } } impl ToTokens for MatchPatEnumVariant { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { match_span, variant_path: _, enum_path, variant_name, field, } = self; quote_spanned! {*match_span=> __MatchTy::<#enum_path>::#variant_name } .to_tokens(tokens); if let Some((paren_token, field)) = field { paren_token.surround(tokens, |tokens| field.to_tokens(tokens)); } } } #[derive(Debug, Clone)] enum MatchPatSimple { Paren(MatchPatParen), Or(MatchPatOr), Binding(MatchPatBinding), Wild(MatchPatWild), Rest(MatchPatRest), } impl_fold! { enum MatchPatSimple<> { Paren(MatchPatParen), Or(MatchPatOr), Binding(MatchPatBinding), Wild(MatchPatWild), Rest(MatchPatRest), } } impl ToTokens for MatchPatSimple { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Or(v) => v.to_tokens(tokens), Self::Paren(v) => v.to_tokens(tokens), Self::Binding(v) => v.to_tokens(tokens), Self::Wild(v) => v.to_tokens(tokens), Self::Rest(v) => v.to_tokens(tokens), } } } struct EnumPath { variant_path: Path, enum_path: Path, variant_name: Ident, } fn parse_enum_path(variant_path: TypePath) -> Result { let TypePath { qself: None, path: variant_path, } = variant_path else { return Err(variant_path); }; if variant_path.is_ident("HdlNone") || variant_path.is_ident("HdlSome") { let ident = variant_path.get_ident().unwrap(); return Ok(EnumPath { enum_path: parse_quote_spanned! {ident.span()=> ::fayalite::enum_::HdlOption::<_> }, variant_name: ident.clone(), variant_path, }); } if variant_path.segments.len() < 2 { return Err(TypePath { qself: None, path: variant_path, }); } let mut enum_path = variant_path.clone(); let PathSegment { ident: variant_name, arguments, } = enum_path.segments.pop().unwrap().into_value(); if !arguments.is_none() { return Err(TypePath { qself: None, path: variant_path, }); } enum_path.segments.pop_punct(); Ok(EnumPath { variant_path, enum_path, variant_name, }) } fn parse_enum_ident(ident: Ident) -> Result { parse_enum_path(TypePath { qself: None, path: ident.into(), }) .map_err(|p| p.path.segments.into_iter().next().unwrap().ident) } trait ParseMatchPat: Sized { fn simple(v: MatchPatSimple) -> Self; fn or(v: MatchPatOr) -> Self; fn paren(v: MatchPatParen) -> Self; fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result; fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result; fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant) -> Result; fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result { match pat { Pat::Ident(PatIdent { attrs: _, by_ref, mutability, ident, subpat, }) => { if let Some(by_ref) = by_ref { state .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((at_token, _)) = subpat { state .errors .error(at_token, "@ not allowed in #[hdl] patterns"); } match parse_enum_ident(ident) { Ok(EnumPath { variant_path, enum_path, variant_name, }) => Self::enum_variant( state, MatchPatEnumVariant { match_span: state.match_span, variant_path, enum_path, variant_name, field: None, }, ), Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding { ident, }))), } } Pat::Or(PatOr { attrs: _, leading_vert, cases, }) => Ok(Self::or(MatchPatOr { leading_vert, cases: cases .into_pairs() .filter_map_pair_value(|pat| Self::parse(state, pat).ok()) .collect(), })), Pat::Paren(PatParen { attrs: _, paren_token, pat, }) => Ok(Self::paren(MatchPatParen { paren_token, pat: Box::new(Self::parse(state, *pat)?), })), Pat::Path(PatPath { attrs: _, qself, path, }) => { let EnumPath { variant_path, enum_path, variant_name, } = parse_enum_path(TypePath { qself, path }).map_err(|path| { state.errors.error(path, "unsupported enum variant path"); })?; Self::enum_variant( state, MatchPatEnumVariant { match_span: state.match_span, variant_path, enum_path, variant_name, field: None, }, ) } Pat::Struct(PatStruct { attrs: _, qself, path, brace_token, fields, rest, }) => { let fields = fields .into_pairs() .filter_map_pair_value(|field_pat| { MatchPatStructField::parse(state, field_pat).ok() }) .collect(); if qself.is_some() { state .errors .error(TypePath { qself, path }, "unsupported struct path"); return Err(()); } Self::struct_( state, MatchPatStruct { match_span: state.match_span, path, brace_token, fields, rest: rest.map( |PatRest { attrs: _, dot2_token, }| dot2_token, ), }, ) } Pat::TupleStruct(PatTupleStruct { attrs: _, qself, path, paren_token, mut elems, }) => { let EnumPath { variant_path, enum_path, variant_name, } = parse_enum_path(TypePath { qself, path }).map_err(|path| { state.errors.error(path, "unsupported enum variant path"); })?; if elems.is_empty() { let mut tokens = TokenStream::new(); paren_token.surround(&mut tokens, |_| {}); state.errors.error( tokens, "field-less enum variants must not be matched using parenthesis", ); } if elems.len() != 1 { state.errors.error( variant_path, "enum variant pattern must have exactly one field", ); return Err(()); } let field = elems.pop().unwrap().into_value(); let field = if let Pat::Rest(rest) = field { MatchPatSimple::Wild(MatchPatWild { underscore_token: Token![_](rest.dot2_token.span()), }) } else { MatchPatSimple::parse(state, field)? }; Self::enum_variant( state, MatchPatEnumVariant { match_span: state.match_span, variant_path, enum_path, variant_name, field: Some((paren_token, field)), }, ) } Pat::Rest(_) => { state .errors .error(pat, "not allowed here in #[hdl] patterns"); Err(()) } Pat::Wild(PatWild { attrs: _, underscore_token, }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild { underscore_token, }))), Pat::Tuple(PatTuple { attrs: _, paren_token, elems, }) => { let fields = elems .into_pairs() .filter_map_pair_value(|field_pat| { if let Pat::Rest(PatRest { attrs: _, dot2_token, }) = field_pat { Some(MatchPatSimple::Rest(MatchPatRest { dot2_token })) } else { MatchPatSimple::parse(state, field_pat).ok() } }) .collect(); Self::tuple( state, MatchPatTuple { paren_token, fields, }, ) } Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => { state .errors .error(pat, "not yet implemented in #[hdl] patterns"); Err(()) } _ => { state.errors.error(pat, "not allowed in #[hdl] patterns"); Err(()) } } } } impl ParseMatchPat for MatchPatSimple { fn simple(v: MatchPatSimple) -> Self { v } fn or(v: MatchPatOr) -> Self { Self::Or(v) } fn paren(v: MatchPatParen) -> Self { Self::Paren(v) } fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result { state.errors.error( v.path, "matching structs is not yet implemented inside structs/enums in #[hdl] patterns", ); Err(()) } fn tuple(state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result { state.errors.push(syn::Error::new( v.paren_token.span.open(), "matching tuples is not yet implemented inside structs/enums in #[hdl] patterns", )); Err(()) } fn enum_variant( state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant, ) -> Result { state.errors.error( v.variant_path, "matching enum variants is not yet implemented inside structs/enums in #[hdl] patterns", ); Err(()) } } #[derive(Debug, Clone)] enum MatchPat { Simple(MatchPatSimple), Or(MatchPatOr), Paren(MatchPatParen), Struct(MatchPatStruct), Tuple(MatchPatTuple), EnumVariant(MatchPatEnumVariant), } impl_fold! { enum MatchPat<> { Simple(MatchPatSimple), Or(MatchPatOr), Paren(MatchPatParen), Struct(MatchPatStruct), Tuple(MatchPatTuple), EnumVariant(MatchPatEnumVariant), } } impl ParseMatchPat for MatchPat { fn simple(v: MatchPatSimple) -> Self { Self::Simple(v) } fn or(v: MatchPatOr) -> Self { Self::Or(v) } fn paren(v: MatchPatParen) -> Self { Self::Paren(v) } fn struct_(_state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result { Ok(Self::Struct(v)) } fn tuple(_state: &mut HdlMatchParseState<'_>, v: MatchPatTuple) -> Result { Ok(Self::Tuple(v)) } fn enum_variant( _state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant, ) -> Result { Ok(Self::EnumVariant(v)) } } impl ToTokens for MatchPat { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Simple(v) => v.to_tokens(tokens), Self::Or(v) => v.to_tokens(tokens), Self::Paren(v) => v.to_tokens(tokens), Self::Struct(v) => v.to_tokens(tokens), Self::Tuple(v) => v.to_tokens(tokens), Self::EnumVariant(v) => v.to_tokens(tokens), } } } with_debug_clone_and_fold! { struct MatchArm<> { attrs: Vec, pat: MatchPat, fat_arrow_token: Token![=>], body: Box, comma: Option, } } impl MatchArm { fn parse(state: &mut HdlMatchParseState<'_>, arm: Arm) -> Result { let Arm { attrs, pat, guard, fat_arrow_token, body, comma, } = arm; if let Some((if_, _)) = guard { state .errors .error(if_, "#[hdl] match arm if clauses are not implemented"); } Ok(Self { attrs, pat: MatchPat::parse(state, pat)?, fat_arrow_token, body, comma, }) } } impl ToTokens for MatchArm { fn to_tokens(&self, tokens: &mut TokenStream) { let Self { attrs, pat, fat_arrow_token, body, comma, } = self; tokens.append_all(attrs); pat.to_tokens(tokens); fat_arrow_token.to_tokens(tokens); body.to_tokens(tokens); comma.to_tokens(tokens); } } struct RewriteAsCheckMatch { span: Span, } impl Fold for RewriteAsCheckMatch { fn fold_pat(&mut self, pat: Pat) -> Pat { match pat { Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) { Ok(EnumPath { variant_path: _, enum_path, variant_name, }) => parse_quote_spanned! {self.span=> __MatchTy::<#enum_path>::#variant_name {} }, Err(ident) => { pat_ident.ident = ident; Pat::Ident(self.fold_pat_ident(pat_ident)) } }, Pat::Path(PatPath { attrs: _, qself, path, }) => match parse_enum_path(TypePath { qself, path }) { Ok(EnumPath { variant_path: _, enum_path, variant_name, }) => parse_quote_spanned! {self.span=> __MatchTy::<#enum_path>::#variant_name {} }, Err(type_path) => parse_quote_spanned! {self.span=> __MatchTy::<#type_path> {} }, }, Pat::Struct(PatStruct { attrs: _, qself, path, brace_token, fields, rest, }) => { let type_path = TypePath { qself, path }; let path = parse_quote_spanned! {self.span=> __MatchTy::<#type_path> }; let fields = fields.do_fold(self); Pat::Struct(PatStruct { attrs: vec![], qself: None, path, brace_token, fields, rest, }) } Pat::TupleStruct(PatTupleStruct { attrs, qself, path, paren_token, elems, }) => match parse_enum_path(TypePath { qself, path }) { Ok(EnumPath { variant_path: _, enum_path, variant_name, }) => { let path = parse_quote_spanned! {self.span=> __MatchTy::<#enum_path>::#variant_name }; let elems = Punctuated::from_iter( elems.into_pairs().map_pair_value(|p| fold_pat(self, p)), ); Pat::TupleStruct(PatTupleStruct { attrs, qself: None, path, paren_token, elems, }) } Err(TypePath { qself, path }) => { Pat::TupleStruct(self.fold_pat_tuple_struct(PatTupleStruct { attrs, qself, path, paren_token, elems, })) } }, _ => fold_pat(self, pat), } } fn fold_pat_ident(&mut self, mut i: PatIdent) -> PatIdent { i.by_ref = Some(Token![ref](i.ident.span())); i.mutability = None; i } fn fold_arm(&mut self, mut i: Arm) -> Arm { i.body = parse_quote_spanned! {self.span=> match __infallible {} }; i.comma.get_or_insert_with(|| Token![,](self.span)); fold_arm(self, i) } fn fold_expr_match(&mut self, mut i: ExprMatch) -> ExprMatch { i.expr = parse_quote_spanned! {self.span=> __match_value }; fold_expr_match(self, i) } fn fold_expr(&mut self, i: Expr) -> Expr { // don't recurse into expressions i } fn fold_local(&mut self, mut let_stmt: Local) -> Local { if let Some(syn::LocalInit { eq_token, expr: _, diverge, }) = let_stmt.init.take() { let_stmt.init = Some(syn::LocalInit { eq_token, expr: parse_quote_spanned! {self.span=> __match_value }, diverge: diverge.map(|(else_, _expr)| { ( else_, parse_quote_spanned! {self.span=> match __infallible {} }, ) }), }); } fold_local(self, let_stmt) } } struct HdlMatchParseState<'a> { match_span: Span, errors: &'a mut Errors, } struct HdlLetPatVisitState<'a> { errors: &'a mut Errors, bindings: BTreeSet<&'a Ident>, } 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_or(&mut self, v: &'a MatchPatOr) { if let Some(first_inner_vert) = v.first_inner_vert() { self.errors.error( first_inner_vert, "or-patterns are not supported in let statements", ); } visit_match_pat_or(self, v); } fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr) { if let Some(first_inner_vert) = v.first_inner_vert() { self.errors.error( first_inner_vert, "or-patterns are not supported in let statements", ); } visit_match_pat_or_simple(self, v); } fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) { self.errors.error(v, "refutable pattern in let statement"); } } impl Visitor<'_> { pub(crate) fn process_hdl_let_pat( &mut self, _hdl_attr: HdlAttr, mut let_stmt: Local, ) -> Local { let span = let_stmt.let_token.span(); if let Pat::Type(pat) = &mut let_stmt.pat { *pat.ty = wrap_ty_with_expr((*pat.ty).clone()); } let check_let_stmt = RewriteAsCheckMatch { span }.fold_local(let_stmt.clone()); let Local { attrs: _, let_token, pat, init, semi_token, } = let_stmt; self.require_normal_module_or_fn(let_token); let Some(syn::LocalInit { eq_token, expr, diverge, }) = init else { self.errors .error(let_token, "#[hdl] let must be assigned a value"); return empty_let(); }; if let Some((else_, _)) = diverge { // TODO: implement let-else self.errors .error(else_, "#[hdl] let ... else { ... } is not implemented"); return empty_let(); } let Ok(pat) = MatchPat::parse( &mut HdlMatchParseState { match_span: span, errors: &mut self.errors, }, pat, ) else { return empty_let(); }; let mut state = HdlLetPatVisitState { errors: &mut self.errors, bindings: BTreeSet::new(), }; state.visit_match_pat(&pat); let HdlLetPatVisitState { errors: _, bindings, } = state; let retval = parse_quote_spanned! {span=> let (#(#bindings,)* __scope,) = { type __MatchTy = ::MatchVariant; let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { #[allow(unused_variables)] #check_let_stmt match __infallible {} }); let mut __match_iter = ::fayalite::module::match_(__match_expr); let ::fayalite::__std::option::Option::Some(__match_variant) = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { ::fayalite::__std::unreachable!("#[hdl] let with uninhabited type"); }; let ::fayalite::__std::option::Option::None = ::fayalite::__std::iter::Iterator::next(&mut __match_iter) else { ::fayalite::__std::unreachable!("#[hdl] let with refutable pattern"); }; let (__match_variant, __scope) = ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( __match_variant, ); #let_token #pat #eq_token __match_variant #semi_token (#(#bindings,)* __scope,) }; }; match retval { syn::Stmt::Local(retval) => retval, _ => unreachable!(), } } pub(crate) fn process_hdl_match( &mut self, _hdl_attr: HdlAttr, expr_match: ExprMatch, ) -> Expr { let span = expr_match.match_token.span(); let check_match = RewriteAsCheckMatch { span }.fold_expr_match(expr_match.clone()); let ExprMatch { attrs: _, match_token, expr, brace_token: _, arms, } = expr_match; self.require_normal_module_or_fn(match_token); let mut state = HdlMatchParseState { match_span: span, errors: &mut self.errors, }; let arms = Vec::from_iter( arms.into_iter() .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()), ); let expr = quote_spanned! {span=> { type __MatchTy = ::MatchVariant; let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr)); ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| { #[allow(unused_variables)] #check_match }); for __match_variant in ::fayalite::module::match_(__match_expr) { let (__match_variant, __scope) = ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope( __match_variant, ); #match_token __match_variant { #(#arms)* } } } }; syn::parse2(expr).unwrap() } }