From 2e650f1d39f44c16b805f8851590adc6ed286b33 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Fri, 21 Jun 2024 15:28:14 +0200 Subject: [PATCH 1/7] WIP icu4x_shared --- components/properties/icu4x_shared.rs | 20 ++++++++++++++++++++ components/properties/src/lib.rs | 4 ++++ components/properties/src/provider/names.rs | 13 ++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 components/properties/icu4x_shared.rs diff --git a/components/properties/icu4x_shared.rs b/components/properties/icu4x_shared.rs new file mode 100644 index 0000000000..c345eff558 --- /dev/null +++ b/components/properties/icu4x_shared.rs @@ -0,0 +1,20 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use alloc::boxed::Box; + +/// Safety: this type *must* be `repr(transparent)` over `Inner`. +pub(crate) unsafe trait Transparent {} + +pub(crate) const fn safe_cast_ref(inner: &Inner) -> &Outer where Inner: ?Sized, Outer: Transparent + ?Sized { + // Safety: Outer is repr(transparent) over Inner + // (enforced via trait safety invariant) + unsafe { &*(inner as *const Inner as *const Outer) } +} + +pub(crate) const fn safe_cast_box(inner: Box) -> Box where Inner: ?Sized, Outer: Transparent + ?Sized { + // Safety: Outer is repr(transparent) over Inner + // (enforced via trait safety invariant) + unsafe { core::mem::transmute(inner) } +} diff --git a/components/properties/src/lib.rs b/components/properties/src/lib.rs index 94bb757faf..fecfb76dbb 100644 --- a/components/properties/src/lib.rs +++ b/components/properties/src/lib.rs @@ -69,6 +69,10 @@ extern crate alloc; +#[macro_use] +#[path = "../icu4x_shared.rs"] +mod icu4x_shared; + #[cfg(feature = "bidi")] pub mod bidi; diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index 784c707c52..94ec714d28 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -16,7 +16,7 @@ use alloc::boxed::Box; use core::cmp::Ordering; use icu_provider::prelude::*; - +use crate::icu4x_shared::Transparent; use tinystr::TinyStr4; use zerovec::ule::{UnvalidatedStr, VarULE}; use zerovec::{maps::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroMap, ZeroVec}; @@ -71,6 +71,11 @@ use zerovec::{maps::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroMap, ZeroVec}; #[repr(transparent)] pub struct NormalizedPropertyNameStr(UnvalidatedStr); +// Safety: type is repr(transparent) +unsafe impl Transparent for NormalizedPropertyNameStr {} + +// repr_transparent_impl!(pub fn cast_ref(&UnvalidatedStr) -> &NormalizedPropertyNameStr); + /// This impl requires enabling the optional `serde` Cargo feature of the `icu::properties` crate #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Box { @@ -161,14 +166,12 @@ impl NormalizedPropertyNameStr { /// Convert a [`UnvalidatedStr`] reference to a [`NormalizedPropertyNameStr`] reference. pub const fn cast_ref(value: &UnvalidatedStr) -> &Self { - // Safety: repr(transparent) - unsafe { core::mem::transmute(value) } + crate::icu4x_shared::safe_cast_ref(value) } /// Convert a [`UnvalidatedStr`] box to a [`NormalizedPropertyNameStr`] box. pub const fn cast_box(value: Box) -> Box { - // Safety: repr(transparent) - unsafe { core::mem::transmute(value) } + crate::icu4x_shared::safe_cast_box(value) } /// Get a [`NormalizedPropertyNameStr`] box from a byte slice. From 343d12a2af7a12889b15b5331037598daeeac3e2 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 22 Jun 2024 16:38:00 +0200 Subject: [PATCH 2/7] Make it work --- components/properties/icu4x_shared.rs | 39 ++++++++++++++------- components/properties/src/provider/names.rs | 8 ++--- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/components/properties/icu4x_shared.rs b/components/properties/icu4x_shared.rs index c345eff558..724adad924 100644 --- a/components/properties/icu4x_shared.rs +++ b/components/properties/icu4x_shared.rs @@ -2,19 +2,34 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). -use alloc::boxed::Box; - +/// Trait asserting that a type is `repr(transparent)`. Used as a bound +/// for functions that require this invariant. +/// /// Safety: this type *must* be `repr(transparent)` over `Inner`. pub(crate) unsafe trait Transparent {} -pub(crate) const fn safe_cast_ref(inner: &Inner) -> &Outer where Inner: ?Sized, Outer: Transparent + ?Sized { - // Safety: Outer is repr(transparent) over Inner - // (enforced via trait safety invariant) - unsafe { &*(inner as *const Inner as *const Outer) } -} - -pub(crate) const fn safe_cast_box(inner: Box) -> Box where Inner: ?Sized, Outer: Transparent + ?Sized { - // Safety: Outer is repr(transparent) over Inner - // (enforced via trait safety invariant) - unsafe { core::mem::transmute(inner) } +/// Implements private helper functions for `repr(transparent)` types. +macro_rules! impl_transparent_helpers { + ($outer:ident($inner:path)) => { + impl $outer { + #[allow(dead_code)] + const fn safe_cast_ref(inner: &$inner) -> &$outer + where + $outer: Transparent<$inner>, + { + // Safety: Outer is repr(transparent) over Inner + // (enforced via trait safety invariant) + unsafe { &*(inner as *const $inner as *const $outer) } + } + #[allow(dead_code)] + const fn safe_cast_box(inner: alloc::boxed::Box<$inner>) -> alloc::boxed::Box<$outer> + where + $outer: Transparent<$inner>, + { + // Safety: Outer is repr(transparent) over Inner + // (enforced via trait safety invariant) + unsafe { core::mem::transmute(inner) } + } + } + }; } diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index 94ec714d28..6d872e4dbf 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -15,8 +15,8 @@ use alloc::boxed::Box; use core::cmp::Ordering; -use icu_provider::prelude::*; use crate::icu4x_shared::Transparent; +use icu_provider::prelude::*; use tinystr::TinyStr4; use zerovec::ule::{UnvalidatedStr, VarULE}; use zerovec::{maps::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroMap, ZeroVec}; @@ -74,7 +74,7 @@ pub struct NormalizedPropertyNameStr(UnvalidatedStr); // Safety: type is repr(transparent) unsafe impl Transparent for NormalizedPropertyNameStr {} -// repr_transparent_impl!(pub fn cast_ref(&UnvalidatedStr) -> &NormalizedPropertyNameStr); +impl_transparent_helpers!(NormalizedPropertyNameStr(UnvalidatedStr)); /// This impl requires enabling the optional `serde` Cargo feature of the `icu::properties` crate #[cfg(feature = "serde")] @@ -166,12 +166,12 @@ impl NormalizedPropertyNameStr { /// Convert a [`UnvalidatedStr`] reference to a [`NormalizedPropertyNameStr`] reference. pub const fn cast_ref(value: &UnvalidatedStr) -> &Self { - crate::icu4x_shared::safe_cast_ref(value) + Self::safe_cast_ref(value) } /// Convert a [`UnvalidatedStr`] box to a [`NormalizedPropertyNameStr`] box. pub const fn cast_box(value: Box) -> Box { - crate::icu4x_shared::safe_cast_box(value) + Self::safe_cast_box(value) } /// Get a [`NormalizedPropertyNameStr`] box from a byte slice. From a1e174ced4ddabfa3f6cda0d4e69d1593444d662 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 22 Jun 2024 17:07:48 +0200 Subject: [PATCH 3/7] Add VarULE impls --- components/properties/icu4x_shared.rs | 59 +++++++++++++++++++-- components/properties/src/provider/names.rs | 23 +++++--- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/components/properties/icu4x_shared.rs b/components/properties/icu4x_shared.rs index 724adad924..d1d54dba0e 100644 --- a/components/properties/icu4x_shared.rs +++ b/components/properties/icu4x_shared.rs @@ -5,15 +5,26 @@ /// Trait asserting that a type is `repr(transparent)`. Used as a bound /// for functions that require this invariant. /// -/// Safety: this type *must* be `repr(transparent)` over `Inner`. -pub(crate) unsafe trait Transparent {} +/// # Safety +/// +/// 1. This outer type *must* be `repr(transparent)` over `Inner` +/// 2. `validate_inner` *must* return `false` if `Inner` does not uphold +/// invariants enforced by this outer type. +pub(crate) unsafe trait Transparent { + fn validate_inner(inner: &Inner) -> bool; +} /// Implements private helper functions for `repr(transparent)` types. macro_rules! impl_transparent_helpers { ($outer:ident($inner:path)) => { impl $outer { + /// Casts the inner type to the outer type. + /// + /// This function is safe, but it does not validate invariants + /// that the outer type might enforce. It is made available as + /// a private fn which can be called by another fn. #[allow(dead_code)] - const fn safe_cast_ref(inner: &$inner) -> &$outer + const fn cast_ref_unchecked(inner: &$inner) -> &$outer where $outer: Transparent<$inner>, { @@ -21,8 +32,13 @@ macro_rules! impl_transparent_helpers { // (enforced via trait safety invariant) unsafe { &*(inner as *const $inner as *const $outer) } } + /// Casts the inner type to the outer type. + /// + /// This function is safe, but it does not validate invariants + /// that the outer type might enforce. It is made available as + /// a private fn which can be called by another fn. #[allow(dead_code)] - const fn safe_cast_box(inner: alloc::boxed::Box<$inner>) -> alloc::boxed::Box<$outer> + const fn cast_box_unchecked(inner: alloc::boxed::Box<$inner>) -> alloc::boxed::Box<$outer> where $outer: Transparent<$inner>, { @@ -33,3 +49,38 @@ macro_rules! impl_transparent_helpers { } }; } + +macro_rules! impl_transparent_varule { + ($outer:ident($inner:path)) => { + // Safety: + // + // 1. `repr(transparent)`, enforced by trait bound, implies no padding + // 2. `repr(transparent)`, enforced by trait bound, implies alignment 1 + // 3. Composition of `repr(transparent)` `validate_by_slice` with + // `validate_inner` from the `Transparent` trait implies + // valid bytes + // 4. The `repr(transparent)` `validate_byte_slice` implies + // that all bytes are covered + // 5. Composition of `repr(transparent)` `from_byte_slice_unchecked` with + // `cast_ref_unchecked` retains the same reference + // 6. Other methods are left as default + // 7. Equality enforced via `Eq` bound + unsafe impl zerovec::ule::VarULE for $outer + where + $outer: Transparent<$inner> + Eq, + { + #[inline] + fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> { + let inner = <$inner>::parse_byte_slice(bytes)?; + Self::validate_inner(inner) + .then_some(()) + .ok_or(zerovec::ZeroVecError::parse::()) + } + #[inline] + unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { + let inner = <$inner>::from_byte_slice_unchecked(bytes); + Self::cast_ref_unchecked(inner) + } + } + }; +} diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index 6d872e4dbf..108c0a21dd 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -18,7 +18,7 @@ use core::cmp::Ordering; use crate::icu4x_shared::Transparent; use icu_provider::prelude::*; use tinystr::TinyStr4; -use zerovec::ule::{UnvalidatedStr, VarULE}; +use zerovec::ule::UnvalidatedStr; use zerovec::{maps::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroMap, ZeroVec}; /// This is a property name that can be "loose matched" as according to @@ -66,15 +66,24 @@ use zerovec::{maps::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroMap, ZeroVec}; /// assert_eq!(Some(11), map.get_copied_by(|u| u.cmp_loose(key_exact))); /// ``` #[derive(PartialEq, Eq)] // VarULE wants these to be byte equality -#[derive(Debug, VarULE)] +#[derive(Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[repr(transparent)] pub struct NormalizedPropertyNameStr(UnvalidatedStr); -// Safety: type is repr(transparent) -unsafe impl Transparent for NormalizedPropertyNameStr {} +// Safety: +// +// 1. The type is `repr(transparent)` over the inner type +// 2. `validate_inner` is implemented (no invariants) +unsafe impl Transparent for NormalizedPropertyNameStr { + #[inline] + fn validate_inner(_inner: &UnvalidatedStr) -> bool { + true + } +} impl_transparent_helpers!(NormalizedPropertyNameStr(UnvalidatedStr)); +impl_transparent_varule!(NormalizedPropertyNameStr(UnvalidatedStr)); /// This impl requires enabling the optional `serde` Cargo feature of the `icu::properties` crate #[cfg(feature = "serde")] @@ -166,12 +175,14 @@ impl NormalizedPropertyNameStr { /// Convert a [`UnvalidatedStr`] reference to a [`NormalizedPropertyNameStr`] reference. pub const fn cast_ref(value: &UnvalidatedStr) -> &Self { - Self::safe_cast_ref(value) + // No invariants: we can directly call cast_*_unchecked + Self::cast_ref_unchecked(value) } /// Convert a [`UnvalidatedStr`] box to a [`NormalizedPropertyNameStr`] box. pub const fn cast_box(value: Box) -> Box { - Self::safe_cast_box(value) + // No invariants: we can directly call cast_*_unchecked + Self::cast_box_unchecked(value) } /// Get a [`NormalizedPropertyNameStr`] box from a byte slice. From d0053451c8fba4685dc82cd24fc654b11a20b816 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 22 Jun 2024 17:18:25 +0200 Subject: [PATCH 4/7] Add Deserialize impls --- components/properties/icu4x_shared.rs | 48 +++++++++++++++++++-- components/properties/src/provider/names.rs | 25 ++--------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/components/properties/icu4x_shared.rs b/components/properties/icu4x_shared.rs index d1d54dba0e..70c4a9398e 100644 --- a/components/properties/icu4x_shared.rs +++ b/components/properties/icu4x_shared.rs @@ -19,7 +19,7 @@ macro_rules! impl_transparent_helpers { ($outer:ident($inner:path)) => { impl $outer { /// Casts the inner type to the outer type. - /// + /// /// This function is safe, but it does not validate invariants /// that the outer type might enforce. It is made available as /// a private fn which can be called by another fn. @@ -33,12 +33,14 @@ macro_rules! impl_transparent_helpers { unsafe { &*(inner as *const $inner as *const $outer) } } /// Casts the inner type to the outer type. - /// + /// /// This function is safe, but it does not validate invariants /// that the outer type might enforce. It is made available as /// a private fn which can be called by another fn. #[allow(dead_code)] - const fn cast_box_unchecked(inner: alloc::boxed::Box<$inner>) -> alloc::boxed::Box<$outer> + const fn cast_box_unchecked( + inner: alloc::boxed::Box<$inner>, + ) -> alloc::boxed::Box<$outer> where $outer: Transparent<$inner>, { @@ -50,6 +52,7 @@ macro_rules! impl_transparent_helpers { }; } +/// Implements `VarULE` on a `repr(transparent)` type. macro_rules! impl_transparent_varule { ($outer:ident($inner:path)) => { // Safety: @@ -84,3 +87,42 @@ macro_rules! impl_transparent_varule { } }; } + +/// Implements `serde::Deserialize` on a `repr(transparent)` type. +macro_rules! impl_transparent_serde { + ($outer:ident($inner:path)) => { + impl<'de, 'a> serde::Deserialize<'de> for &'a $outer + where + 'de: 'a, + { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let inner = <&$inner>::deserialize(deserializer)?; + if !$outer::validate_inner(&inner) { + return Err(::custom(concat!( + "Failed validation: ", + stringify!($outer) + ))); + } + Ok($outer::cast_ref_unchecked(inner)) + } + } + impl<'de> serde::Deserialize<'de> for Box<$outer> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let inner = >::deserialize(deserializer)?; + if !$outer::validate_inner(&inner) { + return Err(::custom(concat!( + "Failed validation: ", + stringify!($outer) + ))); + } + Ok($outer::cast_box_unchecked(inner)) + } + } + }; +} diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index 108c0a21dd..917dd94204 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -85,29 +85,10 @@ unsafe impl Transparent for NormalizedPropertyNameStr { impl_transparent_helpers!(NormalizedPropertyNameStr(UnvalidatedStr)); impl_transparent_varule!(NormalizedPropertyNameStr(UnvalidatedStr)); -/// This impl requires enabling the optional `serde` Cargo feature of the `icu::properties` crate #[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for Box { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - >::deserialize(deserializer).map(NormalizedPropertyNameStr::cast_box) - } -} - -/// This impl requires enabling the optional `serde` Cargo feature of the `icu::properties` crate -#[cfg(feature = "serde")] -impl<'de, 'a> serde::Deserialize<'de> for &'a NormalizedPropertyNameStr -where - 'de: 'a, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - <&UnvalidatedStr>::deserialize(deserializer).map(NormalizedPropertyNameStr::cast_ref) - } +mod _serde { + use super::*; + impl_transparent_serde!(NormalizedPropertyNameStr(UnvalidatedStr)); } impl<'a> ZeroMapKV<'a> for NormalizedPropertyNameStr { From 4b96a13e17638b63a4d86486384a9664b48447a8 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 22 Jun 2024 17:20:59 +0200 Subject: [PATCH 5/7] Use a private module --- components/properties/src/provider/names.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index 917dd94204..e9eb596425 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -86,10 +86,10 @@ impl_transparent_helpers!(NormalizedPropertyNameStr(UnvalidatedStr)); impl_transparent_varule!(NormalizedPropertyNameStr(UnvalidatedStr)); #[cfg(feature = "serde")] -mod _serde { +const _: () = { use super::*; impl_transparent_serde!(NormalizedPropertyNameStr(UnvalidatedStr)); -} +}; impl<'a> ZeroMapKV<'a> for NormalizedPropertyNameStr { type Container = VarZeroVec<'a, NormalizedPropertyNameStr>; From e66be58a143d42aea4f20da81c993779b8cf1bfb Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 22 Jun 2024 17:29:59 +0200 Subject: [PATCH 6/7] Add Serialize impl to the macro helper --- components/properties/icu4x_shared.rs | 9 +++++++++ components/properties/src/provider/names.rs | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/components/properties/icu4x_shared.rs b/components/properties/icu4x_shared.rs index 70c4a9398e..fe585c1d3a 100644 --- a/components/properties/icu4x_shared.rs +++ b/components/properties/icu4x_shared.rs @@ -12,6 +12,7 @@ /// invariants enforced by this outer type. pub(crate) unsafe trait Transparent { fn validate_inner(inner: &Inner) -> bool; + fn as_inner(&self) -> &Inner; } /// Implements private helper functions for `repr(transparent)` types. @@ -91,6 +92,14 @@ macro_rules! impl_transparent_varule { /// Implements `serde::Deserialize` on a `repr(transparent)` type. macro_rules! impl_transparent_serde { ($outer:ident($inner:path)) => { + impl serde::Serialize for $outer { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + self.as_inner().serialize(serializer) + } + } impl<'de, 'a> serde::Deserialize<'de> for &'a $outer where 'de: 'a, diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index e9eb596425..f2b752bec9 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -67,7 +67,6 @@ use zerovec::{maps::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroMap, ZeroVec}; /// ``` #[derive(PartialEq, Eq)] // VarULE wants these to be byte equality #[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[repr(transparent)] pub struct NormalizedPropertyNameStr(UnvalidatedStr); @@ -80,6 +79,10 @@ unsafe impl Transparent for NormalizedPropertyNameStr { fn validate_inner(_inner: &UnvalidatedStr) -> bool { true } + #[inline] + fn as_inner(&self) -> &UnvalidatedStr { + &self.0 + } } impl_transparent_helpers!(NormalizedPropertyNameStr(UnvalidatedStr)); From 2d83e69250e02effecf979538fa240a33408bc72 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Sat, 22 Jun 2024 17:35:26 +0200 Subject: [PATCH 7/7] Cleanup --- components/properties/icu4x_shared.rs | 5 +++++ components/properties/src/provider/names.rs | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/properties/icu4x_shared.rs b/components/properties/icu4x_shared.rs index fe585c1d3a..ffca50c074 100644 --- a/components/properties/icu4x_shared.rs +++ b/components/properties/icu4x_shared.rs @@ -11,11 +11,14 @@ /// 2. `validate_inner` *must* return `false` if `Inner` does not uphold /// invariants enforced by this outer type. pub(crate) unsafe trait Transparent { + #[allow(dead_code)] fn validate_inner(inner: &Inner) -> bool; + #[allow(dead_code)] fn as_inner(&self) -> &Inner; } /// Implements private helper functions for `repr(transparent)` types. +#[allow(unused_macros)] macro_rules! impl_transparent_helpers { ($outer:ident($inner:path)) => { impl $outer { @@ -54,6 +57,7 @@ macro_rules! impl_transparent_helpers { } /// Implements `VarULE` on a `repr(transparent)` type. +#[allow(unused_macros)] macro_rules! impl_transparent_varule { ($outer:ident($inner:path)) => { // Safety: @@ -90,6 +94,7 @@ macro_rules! impl_transparent_varule { } /// Implements `serde::Deserialize` on a `repr(transparent)` type. +#[allow(unused_macros)] macro_rules! impl_transparent_serde { ($outer:ident($inner:path)) => { impl serde::Serialize for $outer { diff --git a/components/properties/src/provider/names.rs b/components/properties/src/provider/names.rs index f2b752bec9..a31b09545e 100644 --- a/components/properties/src/provider/names.rs +++ b/components/properties/src/provider/names.rs @@ -87,12 +87,8 @@ unsafe impl Transparent for NormalizedPropertyNameStr { impl_transparent_helpers!(NormalizedPropertyNameStr(UnvalidatedStr)); impl_transparent_varule!(NormalizedPropertyNameStr(UnvalidatedStr)); - #[cfg(feature = "serde")] -const _: () = { - use super::*; - impl_transparent_serde!(NormalizedPropertyNameStr(UnvalidatedStr)); -}; +impl_transparent_serde!(NormalizedPropertyNameStr(UnvalidatedStr)); impl<'a> ZeroMapKV<'a> for NormalizedPropertyNameStr { type Container = VarZeroVec<'a, NormalizedPropertyNameStr>;