use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use core::hash::{Hash, Hasher};
use pliron_derive::{attr_interface, type_interface};
use crate::{
attribute::{AttrObj, Attribute, attr_cast},
context::Context,
parsable::{Parsable, parse_from_str},
printable::Printable,
result::Result,
r#type::{Type, TypeHandle, type_cast},
utils::trait_cast::any_to_trait,
};
pub trait StableHash {
fn stable_hash(&self, ctx: &Context, state: &mut dyn Hasher);
}
pub trait CloneIntoContext {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> Self;
}
#[attr_interface]
pub trait CloneAttributeIntoContext {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> AttrObj;
fn verify(_attr: &dyn Attribute, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
#[type_interface]
pub trait CloneTypeIntoContext {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> TypeHandle;
fn verify(_type: &dyn Type, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
impl StableHash for AttrObj {
fn stable_hash(&self, ctx: &Context, mut state: &mut dyn Hasher) {
match any_to_trait::<dyn StableHash>(self.as_any()) {
Some(h) => h.stable_hash(ctx, state),
None => {
self.disp(ctx).to_string().hash(&mut state)
}
}
}
}
impl CloneIntoContext for AttrObj {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> AttrObj {
let attr: &dyn Attribute = &**self;
if let Some(cloner) = attr_cast::<dyn CloneAttributeIntoContext>(attr) {
cloner.clone_into_context(src_ctx, dst_ctx)
} else {
let printed = self.disp(src_ctx).to_string();
parse_from_str(AttrObj::parser(()), dst_ctx, &printed).expect("Attribute failed parse")
}
}
}
impl StableHash for TypeHandle {
fn stable_hash(&self, ctx: &Context, mut state: &mut dyn Hasher) {
match any_to_trait::<dyn StableHash>(self.deref(ctx).as_any()) {
Some(h) => h.stable_hash(ctx, state),
None => {
self.disp(ctx).to_string().hash(&mut state);
}
}
}
}
impl CloneIntoContext for TypeHandle {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> TypeHandle {
if let Some(cloner) = type_cast::<dyn CloneTypeIntoContext>(&*self.deref(src_ctx)) {
cloner.clone_into_context(src_ctx, dst_ctx)
} else {
let printed = self.disp(src_ctx).to_string();
parse_from_str(TypeHandle::parser(()), dst_ctx, &printed).expect("Type failed to parse")
}
}
}
#[macro_export]
macro_rules! impl_stable_hash_for_hash {
($($ty:ty),* $(,)?) => {
$(
impl $crate::irbuild::decontext::StableHash for $ty {
fn stable_hash(
&self,
_ctx: &$crate::context::Context,
mut state: &mut dyn ::core::hash::Hasher,
) {
::core::hash::Hash::hash(
::core::concat!(::core::module_path!(), "::", ::core::stringify!($ty)),
&mut state,
);
::core::hash::Hash::hash(self, &mut state);
}
}
)*
};
}
#[macro_export]
macro_rules! impl_clone_into_context_for_clone {
($($ty:ty),* $(,)?) => {
$(
impl $crate::irbuild::decontext::CloneIntoContext for $ty {
fn clone_into_context(
&self,
_src_ctx: &$crate::context::Context,
_dst_ctx: &mut $crate::context::Context,
) -> $ty {
::core::clone::Clone::clone(self)
}
}
)*
};
}
impl_stable_hash_for_hash!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool, char, String
);
impl_clone_into_context_for_clone!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool, char, String
);
impl<T: StableHash> StableHash for Option<T> {
fn stable_hash(&self, ctx: &Context, mut state: &mut dyn Hasher) {
match self {
Some(v) => {
1u8.hash(&mut state);
v.stable_hash(ctx, state);
}
None => 0u8.hash(&mut state),
}
}
}
impl<T: CloneIntoContext> CloneIntoContext for Option<T> {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> Option<T> {
self.as_ref()
.map(|v| v.clone_into_context(src_ctx, dst_ctx))
}
}
impl<T: StableHash> StableHash for Vec<T> {
fn stable_hash(&self, ctx: &Context, mut state: &mut dyn Hasher) {
self.len().hash(&mut state);
for v in self {
v.stable_hash(ctx, state);
}
}
}
impl<T: CloneIntoContext> CloneIntoContext for Vec<T> {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> Vec<T> {
self.iter()
.map(|v| v.clone_into_context(src_ctx, dst_ctx))
.collect()
}
}
impl<T: StableHash> StableHash for Box<T> {
fn stable_hash(&self, ctx: &Context, state: &mut dyn Hasher) {
(**self).stable_hash(ctx, state);
}
}
impl<T: CloneIntoContext> CloneIntoContext for Box<T> {
fn clone_into_context(&self, src_ctx: &Context, dst_ctx: &mut Context) -> Box<T> {
Box::new(self.as_ref().clone_into_context(src_ctx, dst_ctx))
}
}
#[cfg(test)]
mod tests {
use pliron::derive::{
CloneAttributeIntoContext, CloneIntoContext, CloneTypeIntoContext, StableHash, pliron_attr,
pliron_type,
};
use super::*;
use crate::{
combine::stream::position::SourcePosition,
context::Context,
location::{Location, Source},
parsable::{ParseResult, StateStream},
printable,
r#type::TypedHandle,
type_to_trait,
utils::table::FxHasher,
};
fn stable_hash_of(ctx: &Context, v: &impl StableHash) -> u64 {
let mut state = FxHasher::default();
v.stable_hash(ctx, &mut state);
state.finish()
}
#[pliron_attr(
name = "test.decontext_attr",
format = "`<` $val `>`",
verifier = "succ"
)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, CloneAttributeIntoContext)]
struct TestAttr {
val: u64,
}
impl_clone_into_context_for_clone!(TestAttr);
impl StableHash for TestAttr {
fn stable_hash(&self, _ctx: &Context, mut state: &mut dyn Hasher) {
self.val.hash(&mut state);
}
}
type_to_trait!(TestAttr, StableHash);
#[pliron_attr(name = "test.decontext_derived_attr", verifier = "succ")]
#[derive(
PartialEq, Eq, Clone, Debug, Hash, StableHash, CloneIntoContext, CloneAttributeIntoContext,
)]
struct TestDerivedAttr {
val: u64,
}
impl Printable for TestDerivedAttr {
fn fmt(
&self,
_ctx: &Context,
_state: &printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "<{}>", self.val)
}
}
impl Parsable for TestDerivedAttr {
type Arg = ();
type Parsed = Self;
fn parse<'a>(
_state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed>
where
Self: Sized,
{
unreachable!("TestDerivedAttr must never be parsed")
}
}
#[test]
fn attr_derived_impls() {
let ctx = Context::new();
let mut dst_ctx = Context::new();
let a1: AttrObj = Box::new(TestDerivedAttr { val: 10 });
let a2: AttrObj = Box::new(TestDerivedAttr { val: 10 });
let a3: AttrObj = Box::new(TestDerivedAttr { val: 11 });
assert_eq!(stable_hash_of(&ctx, &a1), stable_hash_of(&ctx, &a2));
assert_ne!(stable_hash_of(&ctx, &a1), stable_hash_of(&ctx, &a3));
let a1_2 = a1.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(a1.disp(&ctx).to_string(), a1_2.disp(&dst_ctx).to_string());
}
#[pliron_attr(name = "test.decontext_attr_unregistered", format, verifier = "succ")]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
struct TestNoCloneIntoContextAttr;
#[test]
fn attr_clone_into() {
let ctx = Context::new();
let mut dst_ctx = Context::new();
let attr: AttrObj = Box::new(TestAttr { val: 10 });
let attr_2 = attr.clone_into_context(&ctx, &mut dst_ctx);
assert!(attr.disp(&ctx).to_string() == attr_2.disp(&dst_ctx).to_string());
let attr: AttrObj = Box::new(TestNoCloneIntoContextAttr);
let attr_2 = attr.clone_into_context(&ctx, &mut dst_ctx);
assert!(attr.disp(&ctx).to_string() == attr_2.disp(&dst_ctx).to_string());
}
#[test]
fn attr_stable_hash() {
let ctx = Context::new();
let a1: AttrObj = Box::new(TestAttr { val: 10 });
let a2: AttrObj = Box::new(TestAttr { val: 10 });
let a3: AttrObj = Box::new(TestAttr { val: 11 });
assert_eq!(stable_hash_of(&ctx, &a1), stable_hash_of(&ctx, &a2));
assert_ne!(stable_hash_of(&ctx, &a1), stable_hash_of(&ctx, &a3));
let u1: AttrObj = Box::new(TestNoCloneIntoContextAttr);
let u2: AttrObj = Box::new(TestNoCloneIntoContextAttr);
assert_eq!(stable_hash_of(&ctx, &u1), stable_hash_of(&ctx, &u2));
}
#[pliron_type(
name = "test.decontext_type",
format = "`<` $val `>`",
generate_get = true,
verifier = "succ"
)]
#[derive(PartialEq, Eq, Clone, Debug, Hash, CloneTypeIntoContext)]
struct TestType {
val: u32,
}
impl_clone_into_context_for_clone!(TestType);
impl StableHash for TestType {
fn stable_hash(&self, _ctx: &Context, mut state: &mut dyn Hasher) {
self.val.hash(&mut state);
}
}
type_to_trait!(TestType, StableHash);
#[pliron_type(
name = "test.decontext_derived_type",
generate_get = true,
verifier = "succ"
)]
#[derive(
PartialEq, Eq, Clone, Debug, Hash, StableHash, CloneIntoContext, CloneTypeIntoContext,
)]
struct TestDerivedType {
val: u32,
}
impl Printable for TestDerivedType {
fn fmt(
&self,
_ctx: &Context,
_state: &printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "<{}>", self.val)
}
}
impl Parsable for TestDerivedType {
type Arg = ();
type Parsed = TypedHandle<Self>;
fn parse<'a>(
_state_stream: &mut StateStream<'a>,
_arg: Self::Arg,
) -> ParseResult<'a, Self::Parsed>
where
Self: Sized,
{
unreachable!("TestDerivedType must never be parsed")
}
}
#[test]
fn type_derived_impls() {
let ctx = Context::new();
let mut dst_ctx = Context::new();
let t1 = TestDerivedType::get(&ctx, 32).to_handle();
let t2 = TestDerivedType::get(&ctx, 32).to_handle();
let t3 = TestDerivedType::get(&ctx, 33).to_handle();
assert_eq!(stable_hash_of(&ctx, &t1), stable_hash_of(&ctx, &t2));
assert_ne!(stable_hash_of(&ctx, &t1), stable_hash_of(&ctx, &t3));
let t1_2 = t1.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(t1.disp(&ctx).to_string(), t1_2.disp(&dst_ctx).to_string());
}
#[pliron_type(
name = "test.decontext_type_unregistered",
format,
generate_get = true,
verifier = "succ"
)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
struct TestNoCloneIntoContextType;
#[test]
fn type_clone_into() {
let ctx = Context::new();
let mut dst_ctx = Context::new();
let ty = TestType::get(&ctx, 32).to_handle();
let ty_2 = ty.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(ty.disp(&ctx).to_string(), ty_2.disp(&dst_ctx).to_string());
let unreg_ty = TestNoCloneIntoContextType::get(&ctx).to_handle();
let unreg_ty_2 = unreg_ty.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(
unreg_ty.disp(&ctx).to_string(),
unreg_ty_2.disp(&dst_ctx).to_string()
);
}
#[test]
fn type_stable_hash() {
let ctx = Context::new();
let t1 = TestType::get(&ctx, 32).to_handle();
let t3 = TestType::get(&ctx, 33).to_handle();
assert_ne!(stable_hash_of(&ctx, &t1), stable_hash_of(&ctx, &t3));
let u1 = TestNoCloneIntoContextType::get(&ctx).to_handle();
let u2 = TestNoCloneIntoContextType::get(&ctx).to_handle();
assert_eq!(stable_hash_of(&ctx, &u1), stable_hash_of(&ctx, &u2));
}
#[test]
fn source_stable_hash() {
let mut ctx = Context::new();
let s1 = Source::new_from_file(&mut ctx, "foo.mlir");
let s2 = Source::new_from_file(&mut ctx, "foo.mlir");
let s3 = Source::new_from_file(&mut ctx, "bar.mlir");
assert_eq!(stable_hash_of(&ctx, &s1), stable_hash_of(&ctx, &s2));
assert_ne!(stable_hash_of(&ctx, &s1), stable_hash_of(&ctx, &s3));
assert_ne!(
stable_hash_of(&ctx, &s1),
stable_hash_of(&ctx, &Source::InMemory)
);
}
#[test]
fn source_clone_into_context() {
let mut ctx = Context::new();
let mut dst_ctx = Context::new();
let file_src = Source::new_from_file(&mut ctx, "foo.mlir");
let cloned_src = file_src.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(
file_src.disp(&ctx).to_string(),
cloned_src.disp(&dst_ctx).to_string()
);
let cloned = Source::InMemory.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(cloned, Source::InMemory);
}
#[test]
fn location_stable_hash() {
let mut ctx = Context::new();
let src = Source::new_from_file(&mut ctx, "foo.mlir");
let loc1 = Location::SrcPos {
src,
pos: SourcePosition { line: 1, column: 2 },
};
let loc2 = Location::SrcPos {
src,
pos: SourcePosition { line: 1, column: 2 },
};
let loc3 = Location::SrcPos {
src,
pos: SourcePosition { line: 1, column: 3 },
};
assert_eq!(stable_hash_of(&ctx, &loc1), stable_hash_of(&ctx, &loc2));
assert_ne!(stable_hash_of(&ctx, &loc1), stable_hash_of(&ctx, &loc3));
let named1 = Location::Named {
name: "foo".to_string(),
child_loc: Box::new(Location::Unknown),
};
let named2 = Location::Named {
name: "bar".to_string(),
child_loc: Box::new(Location::Unknown),
};
assert_ne!(stable_hash_of(&ctx, &named1), stable_hash_of(&ctx, &named2));
assert_ne!(
stable_hash_of(&ctx, &named1),
stable_hash_of(&ctx, &Location::Unknown)
);
}
#[test]
fn location_clone_into_context() {
let mut ctx = Context::new();
let mut dst_ctx = Context::new();
let src = Source::new_from_file(&mut ctx, "foo.mlir");
let loc = Location::Named {
name: "foo".to_string(),
child_loc: Box::new(Location::SrcPos {
src,
pos: SourcePosition { line: 5, column: 6 },
}),
};
let cloned_loc = loc.clone_into_context(&ctx, &mut dst_ctx);
assert_eq!(
loc.disp(&ctx).to_string(),
cloned_loc.disp(&dst_ctx).to_string()
);
}
}