mod derive;
mod name;
mod registration;
mod value;
use alloc::rc::Rc;
use core::{
any::Any,
clone::CloneToUninit,
fmt,
ptr::{DynMetadata, NonNull, Pointee},
};
pub use self::{
derive::{
DerivableTypeAttribute, InferAttributeType, InferAttributeValueType,
MaybeInferAttributeType,
},
name::AttributeName,
registration::AttributeRegistration,
value::*,
};
use crate::{
Context, Dialect, Entity, EntityList, EntityListCursor, EntityListCursorMut, EntityListItem,
Immediate, Type, UnsafeIntrusiveEntityRef,
};
pub type AttributeRef = UnsafeIntrusiveEntityRef<dyn Attribute>;
pub trait IntoAttributeRef {
fn into_attribute_ref(self) -> AttributeRef;
}
impl IntoAttributeRef for AttributeRef {
#[inline(always)]
fn into_attribute_ref(self) -> AttributeRef {
self
}
}
impl<T> IntoAttributeRef for UnsafeIntrusiveEntityRef<T>
where
T: Attribute,
{
#[inline(always)]
fn into_attribute_ref(self) -> AttributeRef {
self.as_attribute_ref()
}
}
impl PartialEq for AttributeRef {
fn eq(&self, other: &Self) -> bool {
if Self::ptr_eq(self, other) {
true
} else {
self.borrow().dyn_eq(&other.borrow())
}
}
}
pub type AttrList = EntityList<Attr>;
pub type AttrCursor<'a> = EntityListCursor<'a, Attr>;
pub type AttrCursorMut<'a> = EntityListCursorMut<'a, Attr>;
pub trait Attribute:
crate::any::AsAny
+ CloneToUninit
+ fmt::Debug
+ crate::PartialEqable
+ crate::DynPartialEq
+ crate::DynHash
{
fn type_name(&self) -> &'static str;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn context(&self) -> &Context;
fn context_rc(&self) -> Rc<Context>;
fn name(&self) -> &AttributeName;
fn value(&self) -> &dyn AttributeValue;
fn value_mut(&mut self) -> &mut dyn AttributeValue;
fn ty(&self) -> &Type;
fn set_type(&mut self, ty: Type);
fn as_attr(&self) -> &Attr;
fn as_attr_mut(&mut self) -> &mut Attr;
}
impl core::hash::Hash for dyn Attribute {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
use crate::DynHash;
let hashable = self as &dyn DynHash;
hashable.dyn_hash(state);
}
}
impl Eq for dyn Attribute {}
impl PartialEq for dyn Attribute {
fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other)
}
}
impl dyn Attribute {
pub fn is<T: AttributeRegistration>(&self) -> bool {
Attribute::as_any(self).is::<T>()
}
pub fn downcast_ref<T: AttributeRegistration>(&self) -> Option<&T> {
Attribute::as_any(self).downcast_ref::<T>()
}
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
Attribute::as_any_mut(self).downcast_mut::<T>()
}
pub fn as_bool(&self) -> Option<bool> {
self.value().as_any().downcast_ref::<bool>().copied()
}
pub fn as_u32(&self) -> Option<u32> {
self.as_immediate().and_then(|imm| imm.as_u32())
}
pub fn as_immediate(&self) -> Option<Immediate> {
use super::IntegerLikeAttr;
Some(self.as_attr().as_trait::<dyn IntegerLikeAttr>()?.as_immediate())
}
pub fn dyn_clone(&self) -> AttributeRef {
self.name().dyn_clone(self)
}
}
#[repr(C)]
#[derive(Clone)]
pub struct Attr {
context: NonNull<Context>,
name: AttributeName,
ty: Type,
offset: usize,
}
impl Eq for Attr {}
impl PartialEq for Attr {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.ty == other.ty
}
}
impl core::hash::Hash for Attr {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.ty.hash(state);
}
}
impl Attr {
#[doc(hidden)]
pub unsafe fn uninit<T: Attribute>(
context: &Rc<Context>,
name: AttributeName,
ty: Type,
offset: usize,
) -> Self {
assert!(name.is::<T>());
Self {
context: unsafe { NonNull::new_unchecked(Rc::as_ptr(context).cast_mut()) },
name,
ty,
offset,
}
}
pub fn into_dyn_attribute(&self) -> &dyn Attribute {
self.as_trait::<dyn Attribute>().unwrap()
}
}
impl fmt::Debug for Attr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Attr")
.field_with("name", |f| write!(f, "{}", &self.name))
.field("ty", &self.ty)
.field("offset", &self.offset)
.finish_non_exhaustive()
}
}
impl fmt::Debug for UnsafeIntrusiveEntityRef<Attr> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
fmt::Debug::fmt(&self.borrow(), f)
}
}
impl fmt::Display for UnsafeIntrusiveEntityRef<Attr> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.borrow().name())
}
}
impl AsRef<dyn Attribute> for Attr {
fn as_ref(&self) -> &dyn Attribute {
self.name.upcast(self.container()).unwrap()
}
}
impl AsMut<dyn Attribute> for Attr {
fn as_mut(&mut self) -> &mut dyn Attribute {
self.name.upcast_mut(self.container().cast_mut()).unwrap()
}
}
impl Entity for Attr {}
impl EntityListItem for Attr {}
impl UnsafeIntrusiveEntityRef<dyn Attribute> {
pub fn name(&self) -> AttributeName {
let ptr = Self::into_raw(*self).cast::<Attr>();
unsafe {
let name_ptr = core::ptr::addr_of!((*ptr).name);
AttributeName::clone(&*name_ptr)
}
}
pub fn as_trait_ref<Trait>(self) -> Option<UnsafeIntrusiveEntityRef<Trait>>
where
Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
{
let ptr = self.name().upcast_raw::<Trait>(Self::into_raw(self).cast())?;
let (_, metadata) = ptr.to_raw_parts();
Some(unsafe { self.cast_unsized_unchecked::<Trait>(metadata) })
}
pub fn try_downcast_attr<T>(self) -> Result<UnsafeIntrusiveEntityRef<T>, Self>
where
T: AttributeRegistration,
{
if self.name().is::<T>() {
Ok(unsafe { self.cast_unchecked::<T>() })
} else {
Err(self)
}
}
#[track_caller]
pub fn downcast_attr<T>(self) -> UnsafeIntrusiveEntityRef<T>
where
T: AttributeRegistration,
{
match self.try_downcast_attr::<T>() {
Ok(attr) => attr,
Err(_) => panic!("invalid cast"),
}
}
}
impl Attr {
pub fn name(&self) -> &AttributeName {
&self.name
}
pub fn dialect(&self) -> Rc<dyn Dialect> {
self.context().get_registered_dialect(self.name.dialect())
}
#[inline(always)]
pub fn context(&self) -> &Context {
unsafe { self.context.as_ref() }
}
pub fn context_rc(&self) -> Rc<Context> {
unsafe {
let ptr = self.context.as_ptr().cast_const();
Rc::increment_strong_count(ptr);
Rc::from_raw(ptr)
}
}
#[inline]
pub fn ty(&self) -> &Type {
&self.ty
}
#[inline]
pub fn set_type(&mut self, ty: Type) {
self.ty = ty;
}
}
impl Attr {
#[doc(hidden)]
#[inline]
const fn container(&self) -> *const () {
unsafe {
let ptr = self as *const Self;
ptr.byte_sub(self.offset).cast()
}
}
#[inline(always)]
pub fn as_attr_ref(&self) -> UnsafeIntrusiveEntityRef<Self> {
unsafe { UnsafeIntrusiveEntityRef::from_raw(self.container().cast()) }
}
#[inline(always)]
pub fn as_attribute_ref(&self) -> AttributeRef {
let ptr = self.name.upcast_raw(self.container()).unwrap();
unsafe { AttributeRef::from_raw(ptr) }
}
#[inline]
pub fn is<T: 'static>(&self) -> bool {
self.name.is::<T>()
}
#[inline]
pub fn implements<Trait>(&self) -> bool
where
Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
{
self.name.implements::<Trait>()
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.name.downcast_ref::<T>(self.container())
}
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.name.downcast_mut::<T>(self.container().cast_mut())
}
pub fn as_trait<Trait>(&self) -> Option<&Trait>
where
Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
{
self.name.upcast(self.container())
}
pub fn as_trait_mut<Trait>(&mut self) -> Option<&mut Trait>
where
Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
{
self.name.upcast_mut(self.container().cast_mut())
}
}
#[cfg(test)]
mod tests {
use core::hash::Hasher;
use crate::{
Immediate, ImmediateAttr, Type, attributes::IntegerLikeAttr,
dialects::builtin::attributes::U32Attr, testing::Test,
};
#[test]
fn attribute_dyn_hash() {
let test = Test::default();
let zero = test.context_rc().create_attribute::<U32Attr, _>(0u32).as_attribute_ref();
let zero_two = test.context_rc().create_attribute::<U32Attr, _>(0u32).as_attribute_ref();
let zero_three =
test.context_rc().create_attribute::<ImmediateAttr, _>(0u32).as_attribute_ref();
let one = test.context_rc().create_attribute::<U32Attr, _>(1u32).as_attribute_ref();
let mut hasher = crate::FxHasher::default();
zero.borrow().dyn_hash(&mut hasher);
let zero_hash = hasher.finish();
let mut hasher = crate::FxHasher::default();
zero_two.borrow().dyn_hash(&mut hasher);
let zero_two_hash = hasher.finish();
let mut hasher = crate::FxHasher::default();
zero_three.borrow().dyn_hash(&mut hasher);
let zero_three_hash = hasher.finish();
let mut hasher = crate::FxHasher::default();
one.borrow().dyn_hash(&mut hasher);
let one_hash = hasher.finish();
assert_eq!(zero_hash, zero_two_hash);
assert_ne!(zero_hash, zero_three_hash);
assert_ne!(zero_hash, one_hash);
}
#[test]
fn attribute_dyn_eq() {
let test = Test::default();
let zero = test.context_rc().create_attribute::<U32Attr, _>(0u32).as_attribute_ref();
let zero_two = test.context_rc().create_attribute::<U32Attr, _>(0u32).as_attribute_ref();
let zero_three =
test.context_rc().create_attribute::<ImmediateAttr, _>(0u32).as_attribute_ref();
let one = test.context_rc().create_attribute::<U32Attr, _>(1u32).as_attribute_ref();
let zero = zero.borrow();
let zero_two = zero_two.borrow();
let zero_three = zero_three.borrow();
let one = one.borrow();
assert_eq!(&zero, &zero_two);
assert_ne!(&zero, &zero_three);
assert_ne!(&zero, &one);
}
#[test]
fn immediate_attribute_ref_roundtrips_with_type() {
let test = Test::default();
let immediate = test.context_rc().create_attribute::<ImmediateAttr, _>(Immediate::I32(1));
assert_eq!(immediate.borrow().ty().clone(), Type::I32);
let erased = immediate.as_attribute_ref();
assert_eq!(erased.borrow().ty().clone(), Type::I32);
let roundtrip = erased.try_downcast_attr::<ImmediateAttr>().unwrap();
let roundtrip = roundtrip.borrow();
assert_eq!(roundtrip.ty().clone(), Type::I32);
assert_eq!(roundtrip.as_immediate(), Immediate::I32(1));
}
}