Normalize lifetime arguments out of registry type names

This commit is contained in:
Dennis Kobert
2026-08-28 13:30:05 +00:00
parent 1e5b2c7f62
commit f34ec257e7
4 changed files with 67 additions and 52 deletions

View File

@@ -147,7 +147,7 @@ macro_rules! tagged_value {
let name = td.name.as_ref();
macro_rules! check {
($type_default:ty) => {
if name == std::any::type_name::<$type_default>() { return Box::new(<$type_default>::default()); }
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Box::new(<$type_default>::default()); }
};
}
for_each_type_default!(check);
@@ -194,7 +194,7 @@ macro_rules! tagged_value {
let name = td.name.as_ref();
macro_rules! check {
($type_default:ty) => {
if name == std::any::type_name::<$type_default>() { return Arc::new(<$type_default>::default()); }
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Arc::new(<$type_default>::default()); }
};
}
for_each_type_default!(check);
@@ -240,11 +240,11 @@ macro_rules! tagged_value {
// `Type` axis, it rides the layout proven at wiring.
Self::TypeDefault(td) => {
let name = td.name.as_ref();
if name == std::any::type_name::<List<Graphic>>() { return concrete!(Graphic); }
if name == std::any::type_name::<List<Artboard>>() { return concrete!(Artboard); }
if name == std::any::type_name::<List<Raster<CPU>>>() { return concrete!(Raster<CPU>); }
if name == std::any::type_name::<List<Vector>>() { return concrete!(Vector); }
if name == std::any::type_name::<List<String>>() { return concrete!(String); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Graphic>>()) { return concrete!(Graphic); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Artboard>>()) { return concrete!(Artboard); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Raster<CPU>>>()) { return concrete!(Raster<CPU>); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Vector>>()) { return concrete!(Vector); }
if name == core_types::normalize_type_name(std::any::type_name::<List<String>>()) { return concrete!(String); }
Type::Concrete(td.clone())
}
Self::F64Array(_) => concrete!(f64),
@@ -271,23 +271,29 @@ macro_rules! tagged_value {
/// variants, element-only at rank 0 otherwise. `None` for a
/// [`Self::TypeDefault`] whose named type is outside `for_each_type_default!`.
pub fn value_layout(&self) -> Option<core_types::record::Layout> {
fn leveled<T: Clone + Send + Sync + CacheHash + PartialEq + 'static>() -> Option<core_types::record::Layout> {
fn leveled<T: Clone + Send + Sync + CacheHash + PartialEq + dyn_any::StaticTypeSized>() -> Option<core_types::record::Layout>
where
T::Static: Clone + Send + Sync,
{
Some(core_types::record::Layout::default().with_writes(1, core_types::record::element_write_hashed::<T>(), &[]))
}
fn scalar<T: Clone + Send + Sync + 'static>() -> Option<core_types::record::Layout> {
fn scalar<T: Clone + Send + Sync + dyn_any::StaticTypeSized>() -> Option<core_types::record::Layout>
where
T::Static: Clone + Send + Sync,
{
Some(core_types::record::Layout::default().with_writes(0, core_types::record::element_write::<T>(), &[]))
}
match self {
Self::None => scalar::<()>(),
Self::TypeDefault(td) => {
let name = td.name.as_ref();
if name == std::any::type_name::<List<Graphic>>() { return leveled::<Graphic>(); }
if name == std::any::type_name::<List<Artboard>>() { return leveled::<Artboard>(); }
if name == std::any::type_name::<List<Raster<CPU>>>() { return leveled::<Raster<CPU>>(); }
if name == std::any::type_name::<List<Vector>>() { return leveled::<Vector>(); }
if name == std::any::type_name::<List<String>>() { return leveled::<String>(); }
if name == std::any::type_name::<DocumentNode>() { return scalar::<DocumentNode>(); }
if name == std::any::type_name::<Resource>() { return scalar::<Resource>(); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Graphic>>()) { return leveled::<Graphic>(); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Artboard>>()) { return leveled::<Artboard>(); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Raster<CPU>>>()) { return leveled::<Raster<CPU>>(); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Vector>>()) { return leveled::<Vector>(); }
if name == core_types::normalize_type_name(std::any::type_name::<List<String>>()) { return leveled::<String>(); }
if name == core_types::normalize_type_name(std::any::type_name::<DocumentNode>()) { return scalar::<DocumentNode>(); }
if name == core_types::normalize_type_name(std::any::type_name::<Resource>()) { return scalar::<Resource>(); }
None
}
Self::F64Array(_) => leveled::<f64>(),
@@ -317,7 +323,7 @@ macro_rules! tagged_value {
// their default directly, mirroring `to_dynany`'s recursion guard.
macro_rules! check_level {
($list:ty, $element:ty) => {
if name == std::any::type_name::<$list>() {
if name == core_types::normalize_type_name(std::any::type_name::<$list>()) {
return Ok(leveled_record_value_edge(Vec::<$element>::new()));
}
};
@@ -327,10 +333,10 @@ macro_rules! tagged_value {
check_level!(List<Raster<CPU>>, Raster<CPU>);
// One default lane rather than an empty level: an unwired path input
// starts from a blank vector, as the legacy path modify synthesized itself.
if name == std::any::type_name::<List<Vector>>() { return Ok(leveled_record_value_edge(vec![Vector::default()])); }
if name == core_types::normalize_type_name(std::any::type_name::<List<Vector>>()) { return Ok(leveled_record_value_edge(vec![Vector::default()])); }
check_level!(List<String>, String);
if name == std::any::type_name::<DocumentNode>() { return Ok(record_value_edge(DocumentNode::default())); }
if name == std::any::type_name::<Resource>() { return Ok(record_value_edge(Resource::default())); }
if name == core_types::normalize_type_name(std::any::type_name::<DocumentNode>()) { return Ok(record_value_edge(DocumentNode::default())); }
if name == core_types::normalize_type_name(std::any::type_name::<Resource>()) { return Ok(record_value_edge(Resource::default())); }
Self::from_type_or_none(&Type::Concrete(td)).to_edge()
}
Self::F64Array(values) => Ok(leveled_record_value_edge(values)),
@@ -458,26 +464,26 @@ macro_rules! tagged_value {
let name = concrete_type.name.as_ref();
// TODO: Add default implementations for types such as TaggedValue::Subpaths, and use the defaults here and in document_node_types
// Tries using the default for the tagged value type. If it not implemented, then uses the default used in document_node_types. If it is not used there, then TaggedValue::None is returned.
if name == std::any::type_name::<()>() { return Some(TaggedValue::None) }
if name == core_types::normalize_type_name(std::any::type_name::<()>()) { return Some(TaggedValue::None) }
// List-wrapped types need a single-item default with the element's default, not an empty list
if name == std::any::type_name::<List<Color>>() { return Some(TaggedValue::Color(Some(Color::default()))) }
if name == std::any::type_name::<List<GradientStops>>() { return Some(TaggedValue::Gradient(GradientStops::default())) }
$( if name == std::any::type_name::<$ty>() { return Some(TaggedValue::$identifier(Default::default())) } )*
if name == std::any::type_name::<List<f64>>() { return Some(TaggedValue::F64Array(Vec::new())) }
if name == std::any::type_name::<List<BrushStroke>>() { return Some(TaggedValue::BrushStrokes(Vec::new())) }
if name == core_types::normalize_type_name(std::any::type_name::<List<Color>>()) { return Some(TaggedValue::Color(Some(Color::default()))) }
if name == core_types::normalize_type_name(std::any::type_name::<List<GradientStops>>()) { return Some(TaggedValue::Gradient(GradientStops::default())) }
$( if name == core_types::normalize_type_name(std::any::type_name::<$ty>()) { return Some(TaggedValue::$identifier(Default::default())) } )*
if name == core_types::normalize_type_name(std::any::type_name::<List<f64>>()) { return Some(TaggedValue::F64Array(Vec::new())) }
if name == core_types::normalize_type_name(std::any::type_name::<List<BrushStroke>>()) { return Some(TaggedValue::BrushStrokes(Vec::new())) }
// Leveled wires type by their element; each element name maps to the
// same tagged default as its legacy list form.
if name == std::any::type_name::<Color>() { return Some(TaggedValue::Color(Some(Color::default()))) }
if name == std::any::type_name::<GradientStops>() { return Some(TaggedValue::Gradient(GradientStops::default())) }
if name == std::any::type_name::<BrushStroke>() { return Some(TaggedValue::BrushStrokes(Vec::new())) }
if name == std::any::type_name::<Graphic>() { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Graphic>))) }
if name == std::any::type_name::<Artboard>() { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Artboard>))) }
if name == std::any::type_name::<Raster<CPU>>() { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Raster<CPU>>))) }
if name == std::any::type_name::<Vector>() { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Vector>))) }
if name == core_types::normalize_type_name(std::any::type_name::<Color>()) { return Some(TaggedValue::Color(Some(Color::default()))) }
if name == core_types::normalize_type_name(std::any::type_name::<GradientStops>()) { return Some(TaggedValue::Gradient(GradientStops::default())) }
if name == core_types::normalize_type_name(std::any::type_name::<BrushStroke>()) { return Some(TaggedValue::BrushStrokes(Vec::new())) }
if name == core_types::normalize_type_name(std::any::type_name::<Graphic>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Graphic>))) }
if name == core_types::normalize_type_name(std::any::type_name::<Artboard>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Artboard>))) }
if name == core_types::normalize_type_name(std::any::type_name::<Raster<CPU>>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Raster<CPU>>))) }
if name == core_types::normalize_type_name(std::any::type_name::<Vector>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Vector>))) }
// Types whose `TaggedValue` variant has been removed. They route through `TypeDefault` instead, with `to_dynany`/`to_any` constructing the actual default at execution time.
macro_rules! check {
($type_default:ty) => {
if name == std::any::type_name::<$type_default>() { return Some(TaggedValue::TypeDefault(concrete_type.clone())); }
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Some(TaggedValue::TypeDefault(concrete_type.clone())); }
};
}
for_each_type_default!(check);
@@ -917,7 +923,7 @@ mod typedefault_dispatch {
DynAny::type_id(&*dyn_value),
expected_type_id,
"`to_dynany(TypeDefault({0}))` did not produce a `{0}` — `for_each_type_default!` lists this type but the unwrap site doesn't handle it. Without a match, `to_dynany` falls back to `from_type_or_none`, which returns `TypeDefault({0})` again and recurses forever.",
std::any::type_name::<$type_default>(),
core_types::normalize_type_name(std::any::type_name::<$type_default>()),
);
let arc_value = TaggedValue::TypeDefault(descriptor).to_any();
@@ -925,7 +931,7 @@ mod typedefault_dispatch {
(*arc_value).type_id(),
expected_type_id,
"`to_any(TypeDefault({0}))` did not produce a `{0}` — same recursion hazard as above for the `to_any` path.",
std::any::type_name::<$type_default>(),
core_types::normalize_type_name(std::any::type_name::<$type_default>()),
);
}};
}

View File

@@ -12,12 +12,21 @@ macro_rules! concrete {
};
}
/// The type's name with lifetime arguments stripped, so a lifetimed type
/// keeps its pre-lifetime registry and document name.
pub fn normalize_type_name<'a>(name: &'a str) -> std::borrow::Cow<'a, str> {
if !name.contains("<'") && !name.contains(", '") {
return std::borrow::Cow::Borrowed(name);
}
std::borrow::Cow::Owned(name.replace("<'_>", "").replace("<'_, ", "<").replace(", '_", ""))
}
#[macro_export]
macro_rules! descriptor {
($type:ty) => {
$crate::TypeDescriptor {
id: Some(std::any::TypeId::of::<$type>()),
name: $crate::Cow::Borrowed(std::any::type_name::<$type>()),
name: $crate::normalize_type_name(std::any::type_name::<$type>()),
alias: None,
size: std::mem::size_of::<$type>(),
align: std::mem::align_of::<$type>(),
@@ -26,7 +35,7 @@ macro_rules! descriptor {
($type:ty, $name:ty) => {
$crate::TypeDescriptor {
id: Some(std::any::TypeId::of::<$type>()),
name: $crate::Cow::Borrowed(std::any::type_name::<$type>()),
name: $crate::normalize_type_name(std::any::type_name::<$type>()),
alias: Some($crate::Cow::Borrowed(stringify!($name))),
size: std::mem::size_of::<$type>(),
align: std::mem::align_of::<$type>(),
@@ -192,13 +201,13 @@ pub struct TypeDescriptor {
impl std::hash::Hash for TypeDescriptor {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
normalize_type_name(&self.name).hash(state);
}
}
impl graphene_hash::CacheHash for TypeDescriptor {
fn cache_hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
graphene_hash::CacheHash::cache_hash(&self.name, state);
graphene_hash::CacheHash::cache_hash(&normalize_type_name(&self.name), state);
}
}
@@ -216,7 +225,7 @@ impl PartialEq for TypeDescriptor {
_ => {
// TODO: Add a flag to disable this warning
// warn!("TypeDescriptor::eq: comparing types without ids based on name");
self.name == other.name
normalize_type_name(&self.name) == normalize_type_name(&other.name)
}
}
}
@@ -297,7 +306,7 @@ impl Type {
pub fn new<T: dyn_any::StaticType + Sized>() -> Self {
Self::Concrete(TypeDescriptor {
id: Some(TypeId::of::<T::Static>()),
name: Cow::Borrowed(std::any::type_name::<T::Static>()),
name: normalize_type_name(std::any::type_name::<T::Static>()),
alias: None,
size: size_of::<T>(),
align: align_of::<T>(),