diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b296be..a494ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 6.1.1 - Add support for multi-fields variants in `repr(C, u*)` enums. - Deprecate support for `repr(C)` by deprecating any enum that uses it without also specifying a determinant size. +- Remove `CompilerVersion_X_Y_Z` types and their `CurrentCompilerVersion` type alias: the former were hard to keep up to date and the latter could break your code if you upgraded to a version of the compiler that `stabby` didn't know of. - Prepare integration of `stabby` and [`safer-ffi`](https://crates.io/crates/safer-ffi) by adding `CType` and `is_invalid` to `IStable`. # 5.1.0 diff --git a/Cargo.toml b/Cargo.toml index fac2666..e057819 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,8 +33,13 @@ license = " EPL-2.0 OR Apache-2.0" categories = ["development-tools::ffi", "no-std::no-alloc"] repository = "https://github.com/ZettaScaleLabs/stabby" readme = "stabby/README.md" +version = "6.1.1" [workspace.dependencies] +stabby-macros = { path = "./stabby-macros/", version = "6.1.1", default-features = false } +stabby-abi = { path = "./stabby-abi/", version = "6.1.1", default-features = false } +stabby = { path = "./stabby/", version = "6.1.1", default-features = false } + abi_stable = "0.11.2" criterion = "0.5.1" lazy_static = "1.4.0" diff --git a/stabby-abi/Cargo.toml b/stabby-abi/Cargo.toml index 3636ff3..e07f300 100644 --- a/stabby-abi/Cargo.toml +++ b/stabby-abi/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "stabby-abi" -version = "5.1.0" +version = { workspace = true } edition = "2021" authors = { workspace = true } license = { workspace = true } @@ -39,7 +39,8 @@ abi_stable-channels = ["abi_stable", "abi_stable/channels"] # stabby_unsafe_wakers = [] # stabby_unsafe_wakers is no longer a feature, but a compile option: you can enable them using `RUST_FLAGS='--cfg stabby_unsafe_wakers="true"'` [dependencies] -stabby-macros = { path = "../stabby-macros/", version = "5.1.0" } +stabby-macros.workspace = true + abi_stable = { workspace = true, optional = true } libc = { workspace = true, optional = true } rustversion = { workspace = true } diff --git a/stabby-abi/build.rs b/stabby-abi/build.rs index b801dec..9bdc000 100644 --- a/stabby-abi/build.rs +++ b/stabby-abi/build.rs @@ -60,10 +60,10 @@ fn typenum_unsigned() -> std::io::Result<()> { Ok(()) } -fn tuples() -> std::io::Result<()> { +fn tuples(max_tuple: usize) -> std::io::Result<()> { let filename = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("tuples.rs"); let mut file = BufWriter::new(File::create(filename).unwrap()); - for i in 0..128 { + for i in 0..=max_tuple { writeln!( file, r##"/// An ABI stable tuple of {i} elements. @@ -103,7 +103,11 @@ impl<{generics}> From> for ({generics}) {{ fn main() { typenum_unsigned().unwrap(); - tuples().unwrap(); + println!("cargo:rustc-check-cfg=cfg(stabby_max_tuple, values(any()))"); + let max_tuple = std::env::var("CARGO_CFG_STABBY_MAX_TUPLE") + .map_or(32, |s| s.parse().unwrap_or(32)) + .max(10); + tuples(max_tuple).unwrap(); println!("cargo:rustc-check-cfg=cfg(stabby_nightly, values(none()))"); println!( r#"cargo:rustc-check-cfg=cfg(stabby_check_unreachable, values(none(), "true", "false"))"# diff --git a/stabby-abi/src/alloc/collections/arc_btree.rs b/stabby-abi/src/alloc/collections/arc_btree.rs index 6c9a9a6..7278352 100644 --- a/stabby-abi/src/alloc/collections/arc_btree.rs +++ b/stabby-abi/src/alloc/collections/arc_btree.rs @@ -175,6 +175,7 @@ where type UnusedBits = <*const T as IStable>::UnusedBits; type HasExactlyOneNiche = crate::B0; type ContainsIndirections = crate::B1; + type CType = <*const T as IStable>::CType; const REPORT: &'static crate::report::TypeReport = &crate::report::TypeReport { name: crate::str::Str::new("ArcBTreeSet"), module: crate::str::Str::new("stabby_abi::alloc::collections::arc_btree"), @@ -248,7 +249,7 @@ impl const fn as_ptr( &self, ) -> *mut ArcBTreeSetNodeInner { - unsafe { core::mem::transmute_copy(self) } + unsafe { core::mem::transmute(core::ptr::read(self)) } } fn copy_from_ptr( ptr: *const ArcBTreeSetNodeInner, diff --git a/stabby-abi/src/alloc/sync.rs b/stabby-abi/src/alloc/sync.rs index fe9328e..7140a15 100644 --- a/stabby-abi/src/alloc/sync.rs +++ b/stabby-abi/src/alloc/sync.rs @@ -230,10 +230,16 @@ impl Arc { pub fn downgrade(this: &Self) -> Weak { this.into() } + #[rustversion::since(1.73)] /// Returns a reference to the allocator used to construct `this` pub const fn allocator(this: &Self) -> &Alloc { unsafe { &this.ptr.prefix().alloc } } + #[rustversion::before(1.73)] + /// Returns a reference to the allocator used to construct `this` + pub fn allocator(this: &Self) -> &Alloc { + unsafe { &this.ptr.prefix().alloc } + } } impl Drop for Arc { fn drop(&mut self) { diff --git a/stabby-abi/src/enums/mod.rs b/stabby-abi/src/enums/mod.rs index 45b8a32..677768f 100644 --- a/stabby-abi/src/enums/mod.rs +++ b/stabby-abi/src/enums/mod.rs @@ -55,6 +55,7 @@ unsafe impl IStable for BitDeterminant { type UnusedBits = Array; type HasExactlyOneNiche = Saturator; type ContainsIndirections = B0; + type CType = u8; primitive_report!("BitDeterminant"); } @@ -95,6 +96,7 @@ unsafe impl IStable for ValueIsErr core::fmt::Debug @@ -186,6 +188,7 @@ unsafe impl IStable for Not { type UnusedBits = Determinant::UnusedBits; type HasExactlyOneNiche = Determinant::HasExactlyOneNiche; type ContainsIndirections = Determinant::ContainsIndirections; + type CType = Determinant::CType; primitive_report!("Not", Determinant); } impl IDeterminant for Not diff --git a/stabby-abi/src/future.rs b/stabby-abi/src/future.rs index dc67f02..207e32c 100644 --- a/stabby-abi/src/future.rs +++ b/stabby-abi/src/future.rs @@ -114,7 +114,7 @@ mod stable_waker { /// /// While this is the only way to guarantee ABI-stability when interacting with futures, this does add /// a layer of indirection, and cloning this waker will cause an allocation. To bench the performance cost - /// of this wrapper and decide if you want to risk ABI-unstability on wakers, you may use `RUST_FLAGS='--cfg stabby_unsafe_wakers="true"'`, which will turn [`StableWaker`] into a newtype of [`core::task::Waker`]. + /// of this wrapper and decide if you want to risk ABI-unstability on wakers, you may use `RUSTFLAGS='--cfg stabby_unsafe_wakers="true"'`, which will turn [`StableWaker`] into a newtype of [`core::task::Waker`]. #[crate::stabby] pub struct StableWaker<'a, Alloc: IAlloc = crate::alloc::DefaultAllocator> { waker: StableLike<&'a Waker, &'a ()>, diff --git a/stabby-abi/src/istable.rs b/stabby-abi/src/istable.rs index 686c735..ab1c59f 100644 --- a/stabby-abi/src/istable.rs +++ b/stabby-abi/src/istable.rs @@ -20,18 +20,7 @@ use super::typenum2::*; use super::unsigned::{IBitBase, NonZero}; use super::{FieldPair, Struct, Union}; use stabby_macros::tyeval; -macro_rules! same_as { - ($t: ty) => { - type Align = <$t as IStable>::Align; - type Size = <$t as IStable>::Size; - type UnusedBits = <$t as IStable>::UnusedBits; - type ForbiddenValues = <$t as IStable>::ForbiddenValues; - type HasExactlyOneNiche = <$t as IStable>::HasExactlyOneNiche; - type ContainsIndirections = <$t as IStable>::ContainsIndirections; - const REPORT: &'static TypeReport = <$t as IStable>::REPORT; - const ID: u64 = <$t as IStable>::ID; - }; -} + /// A trait to describe the layout of a type, marking it as ABI-stable. /// /// Every layout is assumed to start at the type's first byte. @@ -55,6 +44,8 @@ pub unsafe trait IStable: Sized { type HasExactlyOneNiche: ISaturatingAdd; /// Whether or not the type contains indirections (pointers, indices in independent data-structures...) type ContainsIndirections: Bit; + /// A support mechanism for [`safer-ffi`](https://crates.io/crates/safer-ffi), allowing all [`IStable`] types to also be `safer_ffi::ReprC` + type CType: IStable; /// A compile-time generated report of the fields of the type, allowing for compatibility inspection. const REPORT: &'static TypeReport; /// A stable (and ideally unique) identifier for the type. Often generated using [`crate::report::gen_id`], but can be manually set. @@ -132,6 +123,7 @@ unsafe impl IStable for NotPod { type ForbiddenValues = T::ForbiddenValues; type HasExactlyOneNiche = T::HasExactlyOneNiche; type UnusedBits = T::UnusedBits; + type CType = T::CType; primitive_report!("NotPod", T); } @@ -196,6 +188,7 @@ unsafe impl< type UnusedBits = UnusedBits; type HasExactlyOneNiche = HasExactlyOneNiche; type ContainsIndirections = B0; + type CType = (); primitive_report!("NicheExporter"); } @@ -393,6 +386,7 @@ unsafe impl IStable for FieldPair { as IStable>::HasExactlyOneNiche, >; type ContainsIndirections = ::Or; + type CType = (); primitive_report!("FP"); } /// Runtime values for [`ISaturatingAdd`] @@ -516,27 +510,18 @@ impl IncludesComputer<(O1, T1, type Output = End; } -unsafe impl IStable for Union -where - (Self, tyeval!(A::Align == B::Align)): IStable, -{ - same_as!((Self, tyeval!(A::Align == B::Align))); -} -unsafe impl IStable for (Union, B1) { +unsafe impl IStable for Union { type ForbiddenValues = End; type UnusedBits = End; type Size = ::Max; type Align = ::Max; type HasExactlyOneNiche = B0; type ContainsIndirections = ::Or; + type CType = <::Divide as IUnsignedBase>::Array< + ::AsUint, + >; primitive_report!("Union"); } -unsafe impl IStable for (Union, B0) -where - Struct<(Union, B1)>: IStable, -{ - same_as!(Struct<(Union, B1)>); -} /// Computes a `T`-typed field's layout when it's after `Start` bytes, taking `T`'s alignment into account. pub struct AlignedAfter(core::marker::PhantomData<(T, Start)>); @@ -552,6 +537,7 @@ unsafe impl IStable for AlignedAfter { >; type HasExactlyOneNiche = T::HasExactlyOneNiche; type ContainsIndirections = T::ContainsIndirections; + type CType = (); primitive_report!("FP"); } @@ -563,5 +549,6 @@ unsafe impl IStable for Struct { <::NextMultipleOf - T::Size) as IUnsignedBase>::PaddingBitMask as IBitMask>::Shift>; type HasExactlyOneNiche = Saturator; type ContainsIndirections = T::ContainsIndirections; + type CType = (); primitive_report!("FP"); } diff --git a/stabby-abi/src/lib.rs b/stabby-abi/src/lib.rs index 284bb6a..43255dc 100644 --- a/stabby-abi/src/lib.rs +++ b/stabby-abi/src/lib.rs @@ -40,9 +40,7 @@ use core::fmt::{Debug, Display}; pub const fn assert_stable() {} /// An ABI-stable tuple. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple(pub A, pub B); +pub use tuple::Tuple2 as Tuple; /// Generate the [`IStable::REPORT`] and [`IStable::ID`] fields for an implementation of [`IStable`]. #[macro_export] @@ -263,6 +261,7 @@ unsafe impl IStable for StableLike { type UnusedBits = As::UnusedBits; type HasExactlyOneNiche = As::HasExactlyOneNiche; type ContainsIndirections = As::ContainsIndirections; + type CType = As::CType; const ID: u64 = crate::report::gen_id(Self::REPORT); const REPORT: &'static report::TypeReport = As::REPORT; } @@ -295,6 +294,7 @@ unsafe impl< type UnusedBits = End; type HasExactlyOneNiche = HasExactlyOneNiche; type ContainsIndirections = ContainsIndirections; + type CType = (); primitive_report!("NoNiches"); } @@ -346,6 +346,7 @@ unsafe impl IStable for StableIf { type UnusedBits = T::UnusedBits; type HasExactlyOneNiche = T::HasExactlyOneNiche; type ContainsIndirections = T::ContainsIndirections; + type CType = T::CType; const REPORT: &'static report::TypeReport = T::REPORT; const ID: u64 = crate::report::gen_id(Self::REPORT); } @@ -391,7 +392,7 @@ pub mod slice; /// ABI-stable strs. pub mod str; /// ABI-stable tuples. -pub mod tuples { +pub mod tuple { include!(concat!(env!("OUT_DIR"), "/tuples.rs")); } diff --git a/stabby-abi/src/num.rs b/stabby-abi/src/num.rs index de02603..bf365cf 100644 --- a/stabby-abi/src/num.rs +++ b/stabby-abi/src/num.rs @@ -63,6 +63,7 @@ macro_rules! define_non_max { type UnusedBits = <$NonZeroU8 as crate::IStable>::UnusedBits; type HasExactlyOneNiche = <$NonZeroU8 as crate::IStable>::HasExactlyOneNiche; type ContainsIndirections = <$NonZeroU8 as crate::IStable>::ContainsIndirections; + type CType = <$NonZeroU8 as crate::IStable>::CType; const ID: u64 = $crate::report::gen_id(Self::REPORT); const REPORT: &'static $crate::report::TypeReport = &$crate::report::TypeReport { name: $crate::str::Str::new(stringify!($NonMaxU8)), @@ -139,6 +140,7 @@ macro_rules! define_non_x { type UnusedBits = <$NonZeroU8 as crate::IStable>::UnusedBits; type HasExactlyOneNiche = <$NonZeroU8 as crate::IStable>::HasExactlyOneNiche; type ContainsIndirections = <$NonZeroU8 as crate::IStable>::ContainsIndirections; + type CType = <$NonZeroU8 as crate::IStable>::CType; const ID: u64 = $crate::report::gen_id(Self::REPORT); const REPORT: &'static $crate::report::TypeReport = &$crate::report::TypeReport { name: $crate::str::Str::new(stringify!($NonMaxU8)), diff --git a/stabby-abi/src/padding.rs b/stabby-abi/src/padding.rs index f931076..fc5df07 100644 --- a/stabby-abi/src/padding.rs +++ b/stabby-abi/src/padding.rs @@ -35,6 +35,7 @@ unsafe impl IStable for Padded { >; type HasExactlyOneNiche = Saturator; type ContainsIndirections = T::ContainsIndirections; + type CType = Tuple<::CType, T::CType>; const REPORT: &'static report::TypeReport = T::REPORT; const ID: u64 = crate::report::gen_id(Self::REPORT); } diff --git a/stabby-abi/src/result.rs b/stabby-abi/src/result.rs index 932eac3..3b6340f 100644 --- a/stabby-abi/src/result.rs +++ b/stabby-abi/src/result.rs @@ -53,6 +53,7 @@ where <>::NicheExporter as IStable>::ForbiddenValues; type UnusedBits = <, ::AsUint> as IStable>::UnusedBits as IBitMask>::BitOr<<<>::NicheExporter as IStable>::UnusedBits as IBitMask>::Shift<< as IStable>::Size as Unsigned>::NextMultipleOf>>; type HasExactlyOneNiche = B0; + type CType = ::Size, ::Align> as IStable>::CType; const REPORT: &'static crate::report::TypeReport = &crate::report::TypeReport { name: Str::new("Result"), module: Str::new("stabby_abi::result"), @@ -70,10 +71,13 @@ where }; const ID: u64 = crate::report::gen_id(Self::REPORT); } - -#[stabby::stabby] -struct Storage { - inner: as IUnsignedBase>::Array, +use seal::Storage; +mod seal { + use super::*; + #[stabby::stabby] + pub struct Storage { + inner: as IUnsignedBase>::Array, + } } impl Storage { diff --git a/stabby-abi/src/stable_impls/mod.rs b/stabby-abi/src/stable_impls/mod.rs index 78d4d84..cbbe790 100644 --- a/stabby-abi/src/stable_impls/mod.rs +++ b/stabby-abi/src/stable_impls/mod.rs @@ -21,6 +21,7 @@ macro_rules! same_as { type UnusedBits = <$t as IStable>::UnusedBits; type ForbiddenValues = <$t as IStable>::ForbiddenValues; type HasExactlyOneNiche = <$t as IStable>::HasExactlyOneNiche; + type CType = <$t as IStable>::CType; primitive_report!($($name)*); }; ($t: ty) => { @@ -29,6 +30,7 @@ macro_rules! same_as { type UnusedBits = <$t as IStable>::UnusedBits; type ForbiddenValues = <$t as IStable>::ForbiddenValues; type HasExactlyOneNiche = <$t as IStable>::HasExactlyOneNiche; + type CType = <$t as IStable>::CType; }; } @@ -172,6 +174,7 @@ unsafe impl IStable for () { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = (); primitive_report!("()"); } unsafe impl IStable for core::marker::PhantomData { @@ -181,6 +184,7 @@ unsafe impl IStable for core::marker::PhantomData { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = (); primitive_report!("core::marker::PhantomData"); } unsafe impl IStable for core::marker::PhantomPinned { @@ -190,6 +194,7 @@ unsafe impl IStable for core::marker::PhantomPinned { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = (); primitive_report!("core::marker::PhantomPinned"); } unsafe impl IStable for bool { @@ -199,6 +204,7 @@ unsafe impl IStable for bool { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("bool"); } @@ -209,6 +215,7 @@ unsafe impl IStable for u8 { type Size = U1; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("u8"); } check!(u8); @@ -219,6 +226,7 @@ unsafe impl IStable for core::num::NonZeroU8 { type ForbiddenValues = nz_holes!(U0); type HasExactlyOneNiche = B1; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("core::num::NonZeroU8"); } unsafe impl IStable for u16 { @@ -228,6 +236,7 @@ unsafe impl IStable for u16 { type Size = U2; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("u16"); } check!(u16); @@ -238,6 +247,7 @@ unsafe impl IStable for core::num::NonZeroU16 { type Size = U2; type HasExactlyOneNiche = B1; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("core::num::NonZeroU16"); } unsafe impl IStable for u32 { @@ -247,6 +257,7 @@ unsafe impl IStable for u32 { type Size = U4; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("u32"); } check!(u32); @@ -257,6 +268,7 @@ unsafe impl IStable for core::num::NonZeroU32 { type Size = U4; type HasExactlyOneNiche = B1; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("core::num::NonZeroU32"); } unsafe impl IStable for u64 { @@ -266,6 +278,7 @@ unsafe impl IStable for u64 { type Size = U8; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("u64"); } check!(u64); @@ -276,6 +289,7 @@ unsafe impl IStable for core::num::NonZeroU64 { type Size = U8; type HasExactlyOneNiche = B1; type ContainsIndirections = B0; + type CType = ::AsUint; primitive_report!("core::num::NonZeroU64"); } @@ -293,6 +307,7 @@ unsafe impl IStable for u128 { #[cfg(target_arch = "aarch64")] type Align = U16; type ContainsIndirections = B0; + type CType = ::AsUint; #[rustversion::before(1.77)] #[cfg(not(target_arch = "aarch64"))] primitive_report!("u128(8)"); @@ -312,6 +327,7 @@ unsafe impl IStable for core::num::NonZeroU128 { type Size = U16; type HasExactlyOneNiche = B1; type Align = ::Align; + type CType = ::AsUint; type ContainsIndirections = B0; primitive_report!("core::num::NonZeroU128"); } @@ -498,6 +514,7 @@ unsafe impl IStable for HasExactlyOneNiche, type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = T::ContainsIndirections; + type CType = T::CType; const REPORT: &'static report::TypeReport = &report::TypeReport { name: Str::new("Option"), module: Str::new("core::option"), @@ -529,6 +546,7 @@ unsafe impl IStable type ForbiddenValues = End; type UnusedBits = End; type HasExactlyOneNiche = B0; + type CType = Ok::CType; type ContainsIndirections = ::Or; const REPORT: &'static report::TypeReport = &report::TypeReport { name: Str::new("Result"), @@ -562,6 +580,7 @@ unsafe impl IStable type ForbiddenValues = End; type UnusedBits = End; type HasExactlyOneNiche = B0; + type CType = Err::CType; type ContainsIndirections = ::Or; const REPORT: &'static report::TypeReport = &report::TypeReport { name: Str::new("Result"), @@ -585,6 +604,7 @@ unsafe impl IStable for NameAggregator { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = (); const REPORT: &'static report::TypeReport = &report::TypeReport { name: Str::new("signature"), module: Str::new("stabby"), @@ -703,6 +723,7 @@ macro_rules! sliceimpl { <<$size as Unsigned>::Equal as Bit>::SaddTernary, >; type ContainsIndirections = T::ContainsIndirections; + type CType = T::CType; primitive_report!(ARRAY_NAME[<$size as Unsigned>::USIZE], T); } }; @@ -732,5 +753,6 @@ unsafe impl IStable for core::cmp::Ordering { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = u8; primitive_report!("core::cmp::Ordering"); } diff --git a/stabby-abi/src/typenum2/unsigned.rs b/stabby-abi/src/typenum2/unsigned.rs index 96c4e27..32bcc05 100644 --- a/stabby-abi/src/typenum2/unsigned.rs +++ b/stabby-abi/src/typenum2/unsigned.rs @@ -44,6 +44,7 @@ unsafe impl IStable for PadByte { type UnusedBits = Array; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = u8; primitive_report!("PadByte"); } @@ -432,6 +433,7 @@ unsafe impl IStable for OneMoreByte { type UnusedBits = ::BitOr>; type HasExactlyOneNiche = L::HasExactlyOneNiche; type ContainsIndirections = L::ContainsIndirections; + type CType = Tuple; primitive_report!("OneMoreByte"); } impl NonZero for UInt { diff --git a/stabby-macros/Cargo.toml b/stabby-macros/Cargo.toml index 55246ea..150c0e1 100644 --- a/stabby-macros/Cargo.toml +++ b/stabby-macros/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "stabby-macros" -version = "5.1.0" +version = { workspace = true } edition = "2021" authors = { workspace = true } license = { workspace = true } diff --git a/stabby-macros/build.rs b/stabby-macros/build.rs index 71b41c6..3343f85 100644 --- a/stabby-macros/build.rs +++ b/stabby-macros/build.rs @@ -24,6 +24,7 @@ fn main() -> Result<(), std::io::Error> { io::{BufWriter, Write}, path::PathBuf, }; + // println!("cargo:rustc-check-cfg=cfg(stabby_max_tuple, values(any()))"); let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); let output = String::from_utf8( Command::new(rustc) diff --git a/stabby-macros/src/enums.rs b/stabby-macros/src/enums.rs index 50090b5..399404c 100644 --- a/stabby-macros/src/enums.rs +++ b/stabby-macros/src/enums.rs @@ -18,7 +18,7 @@ use proc_macro2::TokenStream; use quote::quote; use syn::{Attribute, DataEnum, Generics, Ident, Visibility}; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum Repr { Stabby, C, @@ -53,6 +53,25 @@ impl syn::parse::Parse for Repr { } } } +impl core::fmt::Debug for Repr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Repr::Stabby => "stabby", + Repr::C => "C", + Repr::U8 => "u8", + Repr::U16 => "u16", + Repr::U32 => "u32", + Repr::U64 => "u64", + Repr::Usize => "usize", + Repr::I8 => "i8", + Repr::I16 => "i16", + Repr::I32 => "i32", + Repr::I64 => "i64", + Repr::Isize => "isize", + }) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct FullRepr { repr: Option, @@ -171,7 +190,7 @@ pub fn stabby( } } let mut deprecation = None; - let reprstr = match repr + let trepr = match repr .as_ref() .and_then(|r| r.repr.or_else(|| r.is_c.then_some(Repr::C))) { @@ -192,34 +211,43 @@ pub fn stabby( Some(Repr::C) => { let msg = format!("{repr:?} stabby doesn't support variable size repr and implicitly replaces repr(C) with repr(C, u8), you can silence this warning by picking an explict fixed-size repr"); deprecation = Some(quote!(#[deprecated = #msg])); - "u8" + Repr::U8 } // TODO: Remove support for `#[repr(C)]` alone on the next API-breaking release - Some(Repr::U8) => "u8", - Some(Repr::U16) => "u16", - Some(Repr::U32) => "u32", - Some(Repr::U64) => "u64", - Some(Repr::Usize) => "usize", - Some(Repr::I8) => "i8", - Some(Repr::I16) => "i16", - Some(Repr::I32) => "i32", - Some(Repr::I64) => "i64", - Some(Repr::Isize) => "isize", + Some(Repr::U8) => Repr::U8, + Some(Repr::U16) => Repr::U16, + Some(Repr::U32) => Repr::U32, + Some(Repr::U64) => Repr::U64, + Some(Repr::Usize) => Repr::Usize, + Some(Repr::I8) => Repr::I8, + Some(Repr::I16) => Repr::I16, + Some(Repr::I32) => Repr::I32, + Some(Repr::I64) => Repr::I64, + Some(Repr::Isize) => Repr::Isize, }; - let reprid = quote::format_ident!("{}", reprstr); + let reprid = quote::format_ident!("{trepr:?}"); let reprattr = if repr.map_or(false, |r| r.is_c) { quote!(#[repr(C, #reprid)]) } else { quote!(#[repr(#reprid)]) }; layout = quote!(#st::Tuple<#reprid, #layout>); - report.tyty = quote!(#st::report::TyTy::Enum(#st::str::Str::new(#reprstr))); + report.tyty = crate::Tyty::Enum(trepr); let report_bounds = report.bounds(); + let ctype = report.crepr(); let size_bug = format!( "{ident}'s size was mis-evaluated by stabby, this is definitely a bug and may cause UB, please file an issue" ); let align_bug = format!( "{ident}'s align was mis-evaluated by stabby, this is definitely a bug and may cause UB, please file an issue" ); + let reprc_bug = format!( + "{ident}'s CType was mis-evaluated by stabby, this is definitely a bug and may cause UB, please file an issue" + ); + let assertion = generics + .params + .is_empty() + .then(|| quote!(const _: () = {<#ident as #st::IStable>::ID;};)); + quote! { #(#new_attrs)* #reprattr @@ -227,7 +255,7 @@ pub fn stabby( #vis enum #ident #generics { #variants } - + #assertion #[automatically_derived] unsafe impl #generics #st::IStable for #ident <#unbound_generics> where #report_bounds #layout: #st::IStable { type ForbiddenValues = <#layout as #st::IStable>::ForbiddenValues; @@ -236,8 +264,13 @@ pub fn stabby( type Align = <#layout as #st::IStable>::Align; type HasExactlyOneNiche = #st::B0; type ContainsIndirections = <#layout as #st::IStable>::ContainsIndirections; + type CType = #ctype; const REPORT: &'static #st::report::TypeReport = & #report; const ID: u64 ={ + if (<::Size as #st::Unsigned>::USIZE != <<::CType as #st::IStable>::Size as #st::Unsigned>::USIZE) + || (<::Align as #st::Unsigned>::USIZE != <<::CType as #st::IStable>::Align as #st::Unsigned>::USIZE) { + panic!(#reprc_bug) + } if core::mem::size_of::() != <::Size as #st::Unsigned>::USIZE { panic!(#size_bug) } @@ -347,7 +380,7 @@ pub(crate) fn repr_stabby( ident: &Ident, generics: &Generics, data: DataEnum, - mut report: crate::Report, + report: crate::Report, check: bool, ) -> TokenStream { let st = crate::tl_mod(); @@ -459,7 +492,6 @@ pub(crate) fn repr_stabby( let bounds2 = generics.where_clause.as_ref().map(|c| &c.predicates); let bounds = quote!(#bounds #bounds2); - report.tyty = quote!(#st::report::TyTy::Enum(#st::str::Str::new("stabby"))); let report_bounds = report.bounds(); let enum_as_struct = quote! { #(#attrs)* @@ -522,6 +554,7 @@ pub(crate) fn repr_stabby( type Align = <#layout as #st::IStable>::Align; type HasExactlyOneNiche = #st::B0; type ContainsIndirections = <#layout as #st::IStable>::ContainsIndirections; + type CType = <#layout as #st::IStable>::CType; const REPORT: &'static #st::report::TypeReport = & #report; const ID: u64 = #st::report::gen_id(Self::REPORT); } diff --git a/stabby-macros/src/lib.rs b/stabby-macros/src/lib.rs index c8d666b..560468a 100644 --- a/stabby-macros/src/lib.rs +++ b/stabby-macros/src/lib.rs @@ -253,6 +253,7 @@ pub fn gen_closures_impl(_: TokenStream) -> TokenStream { gen_closures::gen_closures().into() } +#[derive(Clone)] enum Type<'a> { Syn(&'a syn::Type), Report(Report<'a>), @@ -267,29 +268,49 @@ impl<'a> From> for Type<'a> { Self::Report(value) } } + +#[derive(Debug, Clone, Copy)] +pub(crate) enum Tyty { + Struct, + Union, + Enum(enums::Repr), +} +impl ToTokens for Tyty { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let st = crate::tl_mod(); + let tyty = match self { + Tyty::Struct => quote!(#st::report::TyTy::Struct), + Tyty::Union => quote!(#st::report::TyTy::Union), + Tyty::Enum(r) => { + let s = format!("{r:?}"); + quote!(#st::report::TyTy::Enum(#st::str::Str::new(#s))) + } + }; + tokens.extend(tyty); + } +} +#[derive(Clone)] pub(crate) struct Report<'a> { name: String, fields: Vec<(String, Type<'a>)>, version: u32, - pub tyty: proc_macro2::TokenStream, + pub tyty: Tyty, } impl<'a> Report<'a> { pub fn r#struct(name: impl Into, version: u32) -> Self { - let st = crate::tl_mod(); Self { name: name.into(), fields: Vec::new(), version, - tyty: quote!(#st::report::TyTy::Struct), + tyty: Tyty::Struct, } } pub fn r#enum(name: impl Into, version: u32) -> Self { - let st = crate::tl_mod(); Self { name: name.into(), fields: Vec::new(), version, - tyty: quote!(#st::report::TyTy::Struct), + tyty: Tyty::Enum(enums::Repr::Stabby), } } pub fn add_field(&mut self, name: String, ty: impl Into>) { @@ -320,6 +341,50 @@ impl<'a> Report<'a> { let mut bounded_types = HashSet::new(); self.__bounds(&mut bounded_types, quote!(), &st) } + + pub fn crepr(&self) -> proc_macro2::TokenStream { + let st = crate::tl_mod(); + match self.tyty { + Tyty::Struct => { + // TODO: For user comfort, having this would be better, but reading from env vars doesn't work in proc-macros. + // let max_tuple = std::env::var("CARGO_CFG_STABBY_MAX_TUPLE") + // .map_or(32, |s| s.parse().unwrap_or(32)) + // .max(10); + // panic!("{max_tuple}"); + // if self.fields.len() > max_tuple { + // panic!("stabby doesn't support structures with more than {max_tuple} direct fields, you should probably split it at that point; or you can also raise this limit using `--cfg stabby_max_tuple=N` to your RUSTFLAGS at the cost of higher compile times") + // } + let tuple = quote::format_ident!("Tuple{}", self.fields.len()); + let fields = self.fields.iter().map(|f| match &f.1 { + Type::Syn(ty) => quote! (<#ty as #st::IStable>::CType), + Type::Report(r) => r.crepr(), + }); + quote! { + #st::tuple::#tuple <#(#fields,)*> + } + } + Tyty::Union => { + let mut crepr = quote!(()); + for f in &self.fields { + let ty = match &f.1 { + Type::Syn(ty) => quote! (#ty), + Type::Report(r) => r.crepr(), + }; + crepr = quote!(#st::Union<#ty, #crepr>); + } + quote! {<#crepr as #st::IStable>::CType} + } + Tyty::Enum(r) => { + let mut clone = self.clone(); + clone.tyty = Tyty::Union; + let crepr = clone.crepr(); + let determinant = quote::format_ident!("{r:?}"); + quote! { + #st::tuple::Tuple2<#determinant, #crepr> + } + } + } + } } impl ToTokens for Report<'_> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { diff --git a/stabby-macros/src/structs.rs b/stabby-macros/src/structs.rs index db5cade..6c6fb5e 100644 --- a/stabby-macros/src/structs.rs +++ b/stabby-macros/src/structs.rs @@ -90,6 +90,9 @@ pub fn stabby( let align_bug = format!( "{ident}'s align was mis-evaluated by stabby, this is definitely a bug and may cause UB, please file an issue" ); + // let reprc_bug = format!( + // "{ident}'s CType was mis-evaluated by stabby, this is definitely a bug and may cause UB, please file an issue" + // ); let assertion = opt.then(|| { let sub_optimal_message = format!( "{ident}'s layout is sub-optimal, reorder fields or use `#[stabby::stabby(no_opt)]`" @@ -103,6 +106,7 @@ pub fn stabby( } }); let report_bounds = report.bounds(); + let ctype = report.crepr(); let optdoc = format!("Returns true if the layout for [`{ident}`] is smaller or equal to that Rust would have generated for it."); quote! { #struct_code @@ -115,8 +119,13 @@ pub fn stabby( type Align = <#layout as #st::IStable>::Align; type HasExactlyOneNiche = <#layout as #st::IStable>::HasExactlyOneNiche; type ContainsIndirections = <#layout as #st::IStable>::ContainsIndirections; + type CType = #ctype; const REPORT: &'static #st::report::TypeReport = &#report; const ID: u64 = { + // if (<::Size as #st::Unsigned>::USIZE != <<::CType as #st::IStable>::Size as #st::Unsigned>::USIZE) + // || (<::Align as #st::Unsigned>::USIZE != <<::CType as #st::IStable>::Align as #st::Unsigned>::USIZE) { + // panic!(#reprc_bug) + // } if core::mem::size_of::() != <::Size as #st::Unsigned>::USIZE { panic!(#size_bug) } diff --git a/stabby-macros/src/unions.rs b/stabby-macros/src/unions.rs index 9937d9c..90bc2aa 100644 --- a/stabby-macros/src/unions.rs +++ b/stabby-macros/src/unions.rs @@ -51,6 +51,7 @@ pub fn stabby( type Align = <#layout as #st::IStable>::Align; type HasExactlyOneNiche = #st::B0; type ContainsIndirections = <#layout as #st::IStable>::ContainsIndirections; + type CType = <#layout as #st::IStable>::CType; const REPORT: &'static #st::report::TypeReport = & #st::report::TypeReport { name: #st::str::Str::new(#sident), module: #st::str::Str::new(core::module_path!()), diff --git a/stabby/Cargo.toml b/stabby/Cargo.toml index ab92a08..d25469b 100644 --- a/stabby/Cargo.toml +++ b/stabby/Cargo.toml @@ -14,7 +14,7 @@ [package] name = "stabby" -version = "5.1.0" +version = { workspace = true } edition = "2021" authors = { workspace = true } license = { workspace = true } @@ -30,7 +30,7 @@ libloading = ["dep:libloading", "std"] libc = ["stabby-abi/libc"] [dependencies] -stabby-abi = { path = "../stabby-abi/", version = "5.1.0" } +stabby-abi = { workspace = true } lazy_static = { workspace = true } libloading = { workspace = true, optional = true } @@ -40,7 +40,7 @@ rustversion = { workspace = true } smol = { workspace = true } criterion = { workspace = true } rand = { workspace = true } -stabby-abi = { path = "../stabby-abi/", version = "5.1.0", features = ["test"] } +stabby-abi = { workspace = true, features = ["test"] } [package.metadata.docs.rs] all-features = true diff --git a/stabby/build.rs b/stabby/build.rs deleted file mode 100644 index 3a0a7ca..0000000 --- a/stabby/build.rs +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// Pierre Avital, -// - -use std::{ - fs::File, - io::{BufWriter, Write}, - path::PathBuf, -}; - -fn main() { - let compiler_versions = - PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("compiler_versions.rs"); - let mut compiler_versions = BufWriter::new(File::create(compiler_versions).unwrap()); - writeln!(compiler_versions, r"use crate::abi::IStable;").unwrap(); - for version in [ - "1.65.0", "1.66.0", "1.66.1", "1.67.0", "1.67.1", "1.68.0", "1.69.0", "1.70.0", "1.71.0", - "1.72.0", "1.72.1", "1.73.0", "1.74.0", "1.74.1", "1.75.0", "1.76.0", - ] { - let snake_version = version.replace('.', "_"); - writeln!( - compiler_versions, - r#" -/// A ZST that allows inserting some information about the expected compiler for a type. -#[allow(non_camel_case_types)] -pub struct CompilerVersion_{snake_version}(core::marker::PhantomData); -impl CompilerVersion_{snake_version} {{ - /// The constructor for the compiler version. - pub const UNIT: Self = Self(core::marker::PhantomData); -}} - -#[rustversion::stable({version})] -/// This type alias resolves to the compiler that is currently in use to compile the crate -pub type LocalCompiler = CompilerVersion_{snake_version}; - -#[rustversion::stable({version})] -unsafe impl IStable for CompilerVersion_{snake_version} {{ - type Size = Layout::Size; - type Align = Layout::Align; - type ForbiddenValues = Layout::ForbiddenValues; - type UnusedBits = Layout::UnusedBits; - type HasExactlyOneNiche = Layout::HasExactlyOneNiche; - type ContainsIndirections = Layout::ContainsIndirections; - const REPORT: &'static crate::abi::report::TypeReport = &crate::abi::report::TypeReport {{ - name: crate::abi::str::Str::new("CompilerVersion_{snake_version}"), - module: crate::abi::str::Str::new(core::module_path!()), - fields: crate::abi::StableLike::new(Some(&crate::abi::report::FieldReport {{ - name: crate::abi::str::Str::new("inner"), - ty: ::REPORT, - next_field: crate::abi::StableLike::new(None), - }})), - version: 0, - tyty: crate::abi::report::TyTy::Struct, - }}; - const ID: u64 = crate::abi::report::gen_id(Self::REPORT); -}} -"# - ) - .unwrap(); - } -} diff --git a/stabby/src/compiler_version.rs b/stabby/src/compiler_version.rs deleted file mode 100644 index 93c9242..0000000 --- a/stabby/src/compiler_version.rs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// Pierre Avital, -// - -//! Provides ZSTs that only implement `IStable` when built with their corresponding version of the compiler. -//! -//! This allow the `StableLike>` pattern. -//! -//! `CompilerVersion_MAJ_MIN_PATCH` will only `impl IStable` as if it was `Layout`, but only if -//! compiled with the specified version of the compiler, providing you with a compile-time proof that you -//! are using the expected compiler version. -//! -//! Note that it is EXTREMELY memory-unsafe to lie about `Layout` if any type that contains this is -//! used in a `#[repr(stabby)]` enum, since `CompilerVersion` is ALWAYS a ZST, and non-`()` -//! layouts should only be used in combination with `StableLike`. -//! -//! You can also add a `compiler_version: CompilerVersion_VERSION<()>` marker field in your structs to ensure -//! that they are marked as stable only if compiled with the appropriate compiler version, however since the -//! rest of the fields of the struct need to bi ABI-stable for IStable to be implemented, I think the -//! applications are few and far between. - -include!(concat!(env!("OUT_DIR"), "/compiler_versions.rs")); diff --git a/stabby/src/lib.rs b/stabby/src/lib.rs index fc4204d..305cd6e 100644 --- a/stabby/src/lib.rs +++ b/stabby/src/lib.rs @@ -34,10 +34,8 @@ pub use stabby_abi::alloc::{self, boxed, collections, string, sync, vec}; pub use stabby_abi::{Dyn, DynRef}; -pub mod compiler_version; - /// ABI-stable tuples -pub use crate::abi::tuples as tuple; +pub use stabby_abi::tuple; /// Futures can be ABI-stable if you wish hard enough #[cfg_attr( diff --git a/stabby/src/tests/layouts.rs b/stabby/src/tests/layouts.rs index 66302e7..ca0aa21 100644 --- a/stabby/src/tests/layouts.rs +++ b/stabby/src/tests/layouts.rs @@ -250,6 +250,7 @@ unsafe impl stabby::abi::IStable for Align128 { type UnusedBits = End; type HasExactlyOneNiche = B0; type ContainsIndirections = B0; + type CType = Align128; const REPORT: &'static stabby::abi::report::TypeReport = &stabby::abi::report::TypeReport { name: stabby::abi::str::Str::new("Align128"), module: stabby::abi::str::Str::new(core::module_path!()), diff --git a/stabby/src/tuple.rs b/stabby/src/tuple.rs deleted file mode 100644 index 66264ed..0000000 --- a/stabby/src/tuple.rs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// Pierre Avital, -// - -/// A tuple of 2 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple2(pub A, pub B); - -/// A tuple of 3 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple3(pub A, pub B, pub C); - -/// A tuple of 4 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple4(pub A, pub B, pub C, pub D); - -/// A tuple of 5 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple5(pub A, pub B, pub C, pub D, pub E); - -/// A tuple of 6 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple6(pub A, pub B, pub C, pub D, pub E, pub F); - -/// A tuple of 7 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple7(pub A, pub B, pub C, pub D, pub E, pub F, pub G); - -/// A tuple of 8 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple8(pub A, pub B, pub C, pub D, pub E, pub F, pub G, pub H); - -/// A tuple of 9 elements. -#[crate::stabby] -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] -pub struct Tuple9( - pub A, - pub B, - pub C, - pub D, - pub E, - pub F, - pub G, - pub H, - pub I, -);