From 59cef3f398322efd7d2d6da05a812e97b68c198a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 10 Oct 2024 20:48:09 -0700 Subject: [PATCH] add PhantomData as a hdl bundle --- .../src/hdl_type_common.rs | 192 ++++++++++++++++-- crates/fayalite/src/bundle.rs | 82 ++++++++ crates/fayalite/tests/hdl_types.rs | 3 + 3 files changed, 255 insertions(+), 22 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index b6ab88d..506c153 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1273,6 +1273,130 @@ make_parsed_type_or_const! { } } +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypePhantomData { + pub(crate) phantom_data: known_items::PhantomData, + pub(crate) lt_token: Token![<], + pub(crate) ty: Box, + pub(crate) gt_token: Token![>], +} + +impl_fold! { + struct ParsedTypePhantomData<> { + phantom_data: known_items::PhantomData, + lt_token: Token![<], + ty: Box, + gt_token: Token![>], + } +} + +impl ParsedTypePhantomData { + pub(crate) fn try_from_named( + named: ParsedTypeNamed, + parser: &mut TypesParser<'_>, + ) -> Result, ParseFailed> { + let ParsedTypeNamed { path, args } = named; + let parsed_path = known_items::PhantomData::parse_path(path); + let phantom_data = match parsed_path { + Ok(phantom_data) => phantom_data, + Err(path) => return Ok(Err(ParsedTypeNamed { path, args })), + }; + let Some(ParsedGenericArguments { + colon2_token: _, + lt_token, + args, + gt_token, + }) = args + else { + parser + .errors() + .error(phantom_data, "PhantomData requires generic arguments"); + return Err(ParseFailed); + }; + let args_len = args.len(); + if args_len != 1 { + parser.errors().error( + phantom_data, + format_args!( + "wrong number of generic arguments supplied: got {args_len}, expected 1" + ), + ); + return Err(ParseFailed); + } + let ty = args.into_iter().next().unwrap(); + let ParsedGenericArgument::Type(ty) = ty else { + parser.errors().error(ty, "expected a type"); + return Err(ParseFailed); + }; + Ok(Ok(Self { + phantom_data, + lt_token, + ty: Box::new(ty), + gt_token, + })) + } +} + +impl From for Type { + fn from(value: ParsedTypePhantomData) -> Type { + let ParsedTypePhantomData { + phantom_data, + lt_token, + ty, + gt_token, + } = value; + let path = phantom_data.path; + let mut args = Punctuated::new(); + args.push(GenericArgument::Type(ty.into())); + let args = AngleBracketedGenericArguments { + colon2_token: Some(Token![::](lt_token.span)), + lt_token, + args, + gt_token, + }; + let mut segments = path.segments; + segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(args); + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: path.leading_colon, + segments, + }, + }) + } +} + +impl MakeHdlTypeExpr for ParsedTypePhantomData { + fn make_hdl_type_expr(&self, _context: &MakeHdlTypeExprContext) -> Expr { + let ParsedTypePhantomData { + phantom_data, + lt_token: _, + ty: _, + gt_token: _, + } = self; + Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: phantom_data.path.clone(), + }) + } +} + +impl ToTokens for ParsedTypePhantomData { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + phantom_data, + lt_token, + ty, + gt_token, + } = self; + phantom_data.to_tokens(tokens); + lt_token.to_tokens(tokens); + ty.to_tokens(tokens); + gt_token.to_tokens(tokens); + } +} + #[derive(Debug, Clone)] pub(crate) enum ParsedType { Delimited(ParsedTypeDelimited), @@ -1280,6 +1404,7 @@ pub(crate) enum ParsedType { NamedParam(ParsedTypeNamedParam), Tuple(ParsedTypeTuple), ConstUsize(ParsedTypeConstUsize), + PhantomData(ParsedTypePhantomData), Array(ParsedTypeArray), UInt(ParsedTypeUInt), SInt(ParsedTypeSInt), @@ -1294,6 +1419,7 @@ impl_fold! { NamedParam(ParsedTypeNamedParam), Tuple(ParsedTypeTuple), ConstUsize(ParsedTypeConstUsize), + PhantomData(ParsedTypePhantomData), Array(ParsedTypeArray), UInt(ParsedTypeUInt), SInt(ParsedTypeSInt), @@ -1310,6 +1436,7 @@ impl From for Type { ParsedType::NamedParam(v) => v.into(), ParsedType::Tuple(v) => v.into(), ParsedType::ConstUsize(v) => v.into(), + ParsedType::PhantomData(v) => v.into(), ParsedType::Array(v) => v.into(), ParsedType::UInt(v) => v.into(), ParsedType::SInt(v) => v.into(), @@ -1360,6 +1487,7 @@ impl ToTokens for ParsedType { ParsedType::Named(ty) => ty.to_tokens(tokens), ParsedType::Tuple(ty) => ty.to_tokens(tokens), ParsedType::ConstUsize(ty) => ty.to_tokens(tokens), + ParsedType::PhantomData(ty) => ty.to_tokens(tokens), ParsedType::Array(ty) => ty.to_tokens(tokens), ParsedType::UInt(ty) => ty.to_tokens(tokens), ParsedType::SInt(ty) => ty.to_tokens(tokens), @@ -1467,6 +1595,10 @@ impl ParseTypes for ParsedType { Ok(v) => return Ok(Self::ConstUsize(v)), Err(named) => named, }; + let named = match ParsedTypePhantomData::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::PhantomData(v)), + Err(named) => named, + }; let named = match ParsedTypeUInt::try_from_named(named, parser)? { Ok(v) => return Ok(Self::UInt(v)), Err(named) => named, @@ -1784,7 +1916,7 @@ pub(crate) mod known_items { #[allow(non_snake_case, dead_code)] pub(crate) fn $known_item(span: Span) -> $known_item { - let segments = $known_item::PATH_SEGMENTS.iter() + let segments = $known_item::PATH_SEGMENTS[0].iter() .copied() .map(|seg| PathSegment::from(Ident::new(seg, span))) .collect(); @@ -1810,20 +1942,21 @@ pub(crate) mod known_items { return Ok(Self { span: ident.span(), path }); } } - if path.segments.len() == Self::PATH_SEGMENTS.len() - && path - .segments - .iter() - .zip(Self::PATH_SEGMENTS) - .all(|(seg, expected)| { - matches!(seg.arguments, PathArguments::None) - && seg.ident == *expected - }) - { - Ok(Self { span: path.segments.last().unwrap().ident.span(), path }) - } else { - Err(path) + for &path_segments in Self::PATH_SEGMENTS.iter() { + if path.segments.len() == path_segments.len() + && path + .segments + .iter() + .zip(path_segments) + .all(|(seg, expected)| { + matches!(seg.arguments, PathArguments::None) + && seg.ident == *expected + }) + { + return Ok(Self { span: path.segments.last().unwrap().ident.span(), path }); + } } + Err(path) } #[allow(dead_code)] pub(crate) fn parse_path_with_arguments(mut path: Path) -> Result<(Self, PathArguments), Path> { @@ -1878,19 +2011,24 @@ pub(crate) mod known_items { } macro_rules! impl_known_item { - ($([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => { - impl_known_item!([$($(::$head)*)? ::$next] $(::$tail)+); - }; - ([$(::$seg:ident)+] ::$known_item:ident) => { + ($(#[alias = $(::$alias:ident)+])* [$(::$seg:ident)+] ::$known_item:ident) => { impl_known_item_body!($known_item); impl $known_item { - pub(crate) const PATH_SEGMENTS: &'static [&'static str] = &[ - $(stringify!($seg),)+ - stringify!($known_item), + pub(crate) const PATH_SEGMENTS: &'static [&'static [&'static str]] = &[ + &[ + $(stringify!($seg),)+ + stringify!($known_item), + ], + $(&[ + $(stringify!($alias),)+ + ],)* ]; } }; + ($(#[alias = $(::$alias:ident)+])* $([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => { + impl_known_item!($(#[alias = $(::$alias)+])* [$($(::$head)*)? ::$next] $(::$tail)+); + }; } impl_known_item!(::fayalite::array::Array); @@ -1911,7 +2049,16 @@ pub(crate) mod known_items { impl_known_item!(::fayalite::ty::Type); impl_known_item!(::fayalite::ty::Type::MaskType); impl_known_item!(::fayalite::util::ConstUsize); - impl_known_item!(::fayalite::__std::primitive::usize); + impl_known_item!( + #[alias = ::std::primitive::usize] + #[alias = ::core::primitive::usize] + ::fayalite::__std::primitive::usize + ); + impl_known_item!( + #[alias = ::std::marker::PhantomData] + #[alias = ::core::marker::PhantomData] + ::fayalite::__std::marker::PhantomData + ); } macro_rules! impl_bounds { @@ -3969,6 +4116,7 @@ impl MakeHdlTypeExpr for ParsedType { Self::NamedParam(v) => v.make_hdl_type_expr(context), Self::Tuple(v) => v.make_hdl_type_expr(context), Self::ConstUsize(v) => v.make_hdl_type_expr(context), + Self::PhantomData(v) => v.make_hdl_type_expr(context), Self::Array(v) => v.make_hdl_type_expr(context), Self::UInt(v) => v.make_hdl_type_expr(context), Self::SInt(v) => v.make_hdl_type_expr(context), diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index b338322..843eb6c 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -446,3 +446,85 @@ impl_tuples! { {#[num = 11, field = field_11] v11: T11} ] } + +impl Type for PhantomData { + type BaseType = Bundle; + type MaskType = (); + type MatchVariant = PhantomData; + type MatchActiveScope = (); + type MatchVariantAndInactiveScope = MatchVariantWithoutScope; + type MatchVariantsIter = std::iter::Once; + fn match_variants( + this: Expr, + source_location: SourceLocation, + ) -> Self::MatchVariantsIter { + let _ = this; + let _ = source_location; + std::iter::once(MatchVariantWithoutScope(PhantomData)) + } + fn mask_type(&self) -> Self::MaskType { + () + } + fn canonical(&self) -> CanonicalType { + Bundle::new(self.fields()).canonical() + } + #[track_caller] + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Bundle(bundle) = canonical_type else { + panic!("expected bundle"); + }; + assert!( + bundle.fields().is_empty(), + "bundle has wrong number of fields" + ); + PhantomData + } + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } +} + +pub struct PhantomDataBuilder(PhantomData); + +impl Default for PhantomDataBuilder { + fn default() -> Self { + Self(PhantomData) + } +} + +impl ToExpr for PhantomDataBuilder { + type Type = PhantomData; + + fn to_expr(&self) -> Expr { + PhantomData.to_expr() + } +} + +impl BundleType for PhantomData { + type Builder = PhantomDataBuilder; + type FilledBuilder = PhantomDataBuilder; + fn fields(&self) -> Interned<[BundleField]> { + Interned::default() + } +} + +impl TypeWithDeref for PhantomData { + fn expr_deref(_this: &Expr) -> &Self::MatchVariant { + &PhantomData + } +} + +impl StaticType for PhantomData { + const TYPE: Self = PhantomData; + const MASK_TYPE: Self::MaskType = (); + const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; + const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; +} + +impl ToExpr for PhantomData { + type Type = PhantomData; + + fn to_expr(&self) -> Expr { + BundleLiteral::new(PhantomData, Interned::default()).to_expr() + } +} diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 3f11de3..bd7c0fd 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::prelude::*; +use std::marker::PhantomData; #[hdl(outline_generated)] pub struct S { @@ -8,6 +9,8 @@ pub struct S { b: UInt<3>, pub(crate) c: ArrayType, Len>, pub d: T2, + pub e: PhantomData, + pub f: PhantomData, } #[hdl(outline_generated)]