diff --git a/crates/fayalite/src/generics.rs b/crates/fayalite/src/generics.rs new file mode 100644 index 0000000..ac5d88a --- /dev/null +++ b/crates/fayalite/src/generics.rs @@ -0,0 +1,368 @@ +use crate::{ + intern::{Intern, Interned}, + source_location::SourceLocation, + ty::{DynCanonicalType, Type, Value}, + util::DebugAsDisplay, +}; +use std::{fmt, hash::Hash}; + +mod sealed { + pub trait GenericArgsSealed {} + pub trait ToGenericArgSealed {} +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct GenericParamName { + pub name: Interned, + pub source_location: SourceLocation, +} + +impl GenericParamName { + #[track_caller] + pub fn new(name: &str) -> Self { + Self::new_with_loc(name, SourceLocation::caller()) + } + pub fn new_with_loc(name: &str, source_location: SourceLocation) -> Self { + let name = name.intern(); + Self { + name, + source_location, + } + } +} + +impl fmt::Debug for GenericParamName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.name.fmt(f) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct TypeParam { + pub name: GenericParamName, + pub default: Option TypeArg>, +} + +impl fmt::Debug for TypeParam { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TypeParam") + .field("name", &self.name) + .field("default", &self.default.map(|_| DebugAsDisplay("_"))) + .finish() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ConstParam { + pub name: GenericParamName, + pub default: Option ConstArg>, +} + +impl fmt::Debug for ConstParam { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ConstParam") + .field("name", &self.name) + .field("default", &self.default.map(|_| DebugAsDisplay("_"))) + .finish() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum GenericParam { + Type(TypeParam), + Const(ConstParam), +} + +impl fmt::Debug for GenericParam { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Type(v) => v.fmt(f), + Self::Const(v) => v.fmt(f), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct TypeArg> { + pub ty: T, +} + +impl TypeArg { + pub fn canonical(&self) -> TypeArg { + TypeArg { + ty: self.ty.canonical_dyn(), + } + } +} + +impl sealed::ToGenericArgSealed for T {} + +impl ToGenericArg for T { + fn to_generic_arg(this: &Self) -> GenericArg { + GenericArg::Type(TypeArg { + ty: this.canonical_dyn(), + }) + } +} + +impl sealed::ToGenericArgSealed for TypeArg {} + +impl ToGenericArg for TypeArg { + fn to_generic_arg(this: &Self) -> GenericArg { + GenericArg::Type(this.canonical()) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct ConstArg { + pub value: usize, +} + +impl sealed::ToGenericArgSealed for usize {} + +impl ToGenericArg for usize { + fn to_generic_arg(this: &Self) -> GenericArg { + GenericArg::Const(ConstArg { value: *this }) + } +} + +impl sealed::ToGenericArgSealed for ConstArg {} + +impl ToGenericArg for ConstArg { + fn to_generic_arg(this: &Self) -> GenericArg { + GenericArg::Const(*this) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub struct DefaultArg; + +impl sealed::ToGenericArgSealed for DefaultArg {} + +impl ToGenericArg for DefaultArg { + fn to_generic_arg(this: &Self) -> GenericArg { + GenericArg::Default(*this) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg> { + Type(TypeArg), + Const(ConstArg), + Default(DefaultArg), +} + +#[derive(Copy, Clone, Debug)] +pub struct NotATypeArg; + +impl fmt::Display for NotATypeArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("not a type argument") + } +} + +impl std::error::Error for NotATypeArg {} + +#[derive(Copy, Clone, Debug)] +pub struct NotAConstArg; + +impl fmt::Display for NotAConstArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("not a const argument") + } +} + +impl std::error::Error for NotAConstArg {} + +impl GenericArg { + pub fn canonical(&self) -> GenericArg { + match self { + GenericArg::Type(v) => GenericArg::Type(v.canonical()), + GenericArg::Const(v) => GenericArg::Const(*v), + GenericArg::Default(v) => GenericArg::Default(*v), + } + } + pub fn ty(self) -> Result { + match self { + GenericArg::Type(TypeArg { ty }) => Ok(ty), + GenericArg::Const(_) | GenericArg::Default(_) => Err(NotATypeArg), + } + } + pub fn const_(self) -> Result { + match self { + GenericArg::Const(ConstArg { value }) => Ok(value), + GenericArg::Type(_) | GenericArg::Default(_) => Err(NotAConstArg), + } + } +} + +impl fmt::Debug for GenericArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Type(v) => v.fmt(f), + Self::Const(v) => v.fmt(f), + Self::Default(v) => v.fmt(f), + } + } +} + +impl sealed::ToGenericArgSealed for GenericArg {} + +impl ToGenericArg for GenericArg { + fn to_generic_arg(this: &Self) -> GenericArg { + this.canonical() + } +} + +pub trait ToGenericArg: sealed::ToGenericArgSealed { + fn to_generic_arg(this: &Self) -> GenericArg; +} + +pub trait GenericArgs: sealed::GenericArgsSealed { + fn generic_args(this: &Self) -> Vec; +} + +impl sealed::GenericArgsSealed for Interned {} + +impl GenericArgs for Interned { + fn generic_args(this: &Self) -> Vec { + T::generic_args(this) + } +} + +impl sealed::GenericArgsSealed for Box {} + +impl GenericArgs for Box { + fn generic_args(this: &Self) -> Vec { + T::generic_args(this) + } +} + +impl sealed::GenericArgsSealed for &'_ T {} + +impl GenericArgs for &'_ T { + fn generic_args(this: &Self) -> Vec { + T::generic_args(this) + } +} + +impl sealed::GenericArgsSealed for &'_ mut T {} + +impl GenericArgs for &'_ mut T { + fn generic_args(this: &Self) -> Vec { + T::generic_args(this) + } +} + +impl sealed::GenericArgsSealed for [T] {} + +impl GenericArgs for [T] { + fn generic_args(this: &Self) -> Vec { + Vec::from_iter(this.iter().map(ToGenericArg::to_generic_arg)) + } +} + +impl sealed::GenericArgsSealed for Vec {} + +impl GenericArgs for Vec { + fn generic_args(this: &Self) -> Vec { + Vec::from_iter(this.iter().map(ToGenericArg::to_generic_arg)) + } +} + +impl sealed::GenericArgsSealed for [T; N] {} + +impl GenericArgs for [T; N] { + fn generic_args(this: &Self) -> Vec { + Vec::from_iter(this.iter().map(ToGenericArg::to_generic_arg)) + } +} + +macro_rules! impl_generic_args_for_tuples { + ($($v:ident: $T:ident),*) => { + impl<$($T: ToGenericArg,)*> sealed::GenericArgsSealed for ($($T,)*) {} + + impl<$($T: ToGenericArg,)*> GenericArgs for ($($T,)*) { + fn generic_args(this: &Self) -> Vec { + let ($($v,)*) = this; + vec![$($T::to_generic_arg($v),)*] + } + } + }; +} + +impl_generic_args_for_tuples!(); +impl_generic_args_for_tuples!(a: A); +impl_generic_args_for_tuples!(a: A, b: B); +impl_generic_args_for_tuples!(a: A, b: B, c: C); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K); +impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L); + +#[track_caller] +pub fn canonical_generic_args( + mut generic_args: Vec, + generic_params: &[GenericParam], +) -> Interned<[GenericArg]> { + assert!(generic_params.len() >= generic_args.len()); + for (i, generic_param) in generic_params.iter().enumerate() { + match generic_args.get(i) { + Some(GenericArg::Default(DefaultArg)) | None => {} + Some(GenericArg::Type(_)) | Some(GenericArg::Const(_)) => continue, + } + let generic_arg = match generic_param { + GenericParam::Type(TypeParam { + name: _, + default: Some(default), + }) => GenericArg::Type(default(&generic_args[..i])), + GenericParam::Type(TypeParam { + name: + GenericParamName { + name, + source_location, + }, + default: None, + }) => panic!("Missing type argument for {name} declared here: {source_location}"), + GenericParam::Const(ConstParam { + name: _, + default: Some(default), + }) => GenericArg::Const(default(&generic_args[..i])), + GenericParam::Const(ConstParam { + name: + GenericParamName { + name, + source_location, + }, + default: None, + }) => panic!("Missing const argument for {name} declared here: {source_location}"), + }; + if i == generic_args.len() { + generic_args.push(generic_arg) + } else { + generic_args[i] = generic_arg; + } + } + debug_assert_eq!(generic_params.len(), generic_args.len()); + Intern::intern_owned(generic_args) +} + +pub trait GenericType { + type GenericArgs: GenericArgs; + type Type: Type; + type Value: Value; + fn generic_params() -> Interned<[GenericParam]>; + #[track_caller] + fn canonical_generic_args(generic_args: &Self::GenericArgs) -> Interned<[GenericArg]> { + canonical_generic_args( + GenericArgs::generic_args(&generic_args), + &Self::generic_params(), + ) + } + fn ty(generic_args: Interned<[GenericArg]>) -> Self::Type; +} diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index c0a3775..8406a6d 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -32,6 +32,7 @@ pub mod clock; pub mod enum_; pub mod expr; pub mod firrtl; +pub mod generics; pub mod int; pub mod intern; pub mod memory;