Skip to content

Commit

Permalink
Merge pull request #85 from ZettaScaleLabs/v36.1.1-rc4
Browse files Browse the repository at this point in the history
Add support for more representations of structs
  • Loading branch information
p-avital authored Jul 5, 2024
2 parents 8c5fda2 + 569002c commit caeccb0
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 29 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 36.1.1-rc4 (api=2.0.0, abi=2.0.0)
- Add support for `#[repr(transparent)]` and `#[repr(align(n))]` in `#[stabby::stabby]` structs up to n=64kiB.

# 36.1.1-rc3 (api=2.0.0, abi=2.0.0)
- Allow structures to compute their single-niche evaluation properly.

Expand Down
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ 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 = "36.1.1-rc3" # Track
version = "36.1.1-rc4" # Track

[workspace.dependencies]
stabby-macros = { path = "./stabby-macros/", version = "36.1.1-rc3", default-features = false } # Track
stabby-abi = { path = "./stabby-abi/", version = "36.1.1-rc3", default-features = false } # Track
stabby = { path = "./stabby/", version = "36.1.1-rc3", default-features = false } # Track
stabby-macros = { path = "./stabby-macros/", version = "36.1.1-rc4", default-features = false } # Track
stabby-abi = { path = "./stabby-abi/", version = "36.1.1-rc4", default-features = false } # Track
stabby = { path = "./stabby/", version = "36.1.1-rc4", default-features = false } # Track

abi_stable = "0.11.0"
libc = "0.2"
Expand Down
6 changes: 5 additions & 1 deletion stabby-abi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@ fn typenum_unsigned() -> std::io::Result<()> {
}
}
for i in 0..128 {
let u = u(1 << i);
let p = 1 << i;
let u = u(p);
writeln!(file, "/// {i}\npub type U2pow{i} = {u};")?;
if p > SEQ_MAX {
writeln!(file, "/// {i}\npub type U{p} = {u};")?;
}
}
Ok(())
}
Expand Down
18 changes: 17 additions & 1 deletion stabby-abi/src/istable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use self::unsigned::{Alignment, IUnsignedBase};

use super::typenum2::*;
use super::unsigned::{IBitBase, NonZero};
use super::{FieldPair, Struct, Union};
use super::{AlignedStruct, FieldPair, Struct, Union};
use stabby_macros::tyeval;

/// A trait to describe the layout of a type, marking it as ABI-stable.
Expand Down Expand Up @@ -568,6 +568,22 @@ unsafe impl<T: IStable> IStable for Struct<T> {
primitive_report!("FP");
}

unsafe impl<T: IStable, Align: Alignment> IStable for AlignedStruct<T, Align> {
type Size = <T::Size as Unsigned>::NextMultipleOf<Self::Align>;
type Align = <Align as Alignment>::Max<T::Align>;
type ForbiddenValues = T::ForbiddenValues;
type UnusedBits = <T::UnusedBits as IBitMask>::BitOr<
<<tyeval!(<T::Size as Unsigned>::NextMultipleOf<T::Align> - T::Size) as IUnsignedBase>::PaddingBitMask as IBitMask>::Shift<T::Size>>;
type HasExactlyOneNiche = <<T::Size as Unsigned>::Equal<Self::Size> as Bit>::SaddTernary<
T::HasExactlyOneNiche,
Saturator,
>;
type ContainsIndirections = T::ContainsIndirections;
#[cfg(feature = "experimental-ctypes")]
type CType = ();
primitive_report!("FP");
}

/// Used by `stabby` to prevent proof cycles in types that contain indirections to themselves.
#[crate::stabby]
pub struct _Self;
3 changes: 3 additions & 0 deletions stabby-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ pub struct FieldPair<A, B>(core::marker::PhantomData<(A, B)>);
#[repr(transparent)]
pub struct Struct<T>(T);

/// Used by proc-macros to ensure a list of fields gets the proper end padding when specific alignments are requested.
pub struct AlignedStruct<T, Align>(core::marker::PhantomData<(T, Align)>);

/// Used by [`crate::result::Result`]
#[repr(C)]
pub union Union<A, B> {
Expand Down
27 changes: 27 additions & 0 deletions stabby-abi/src/typenum2/unsigned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,33 @@ impl Alignment for U16 {
type Max<T: Alignment> = <Self::Greater<T> as IBitBase>::_ATernary<Self, T>;
type AsUint = u128;
}
macro_rules! gen_align {
($n: literal, $path: ident, $u: ident, $backing: ty) => {
/// A type with the alignment specified in its name.
///
/// This type is also valid as a contiguous buffer.
#[crate::stabby]
#[repr(align($n))]
#[derive(Debug, Default, Copy, Clone)]
pub struct $path(pub $backing);
impl Alignment for $u {
type Max<T: Alignment> = <Self::Greater<T> as IBitBase>::_ATernary<Self, T>;
type AsUint = $path;
}
};
}
gen_align!(32, Align32, U32, [u8; 32]);
gen_align!(64, Align64, U64, [[u8; 32]; 2]);
gen_align!(128, Align128, U128, [[u8; 32]; 4]);
gen_align!(256, Align256, U256, [[u8; 32]; 8]);
gen_align!(512, Align512, U512, [[u8; 32]; 16]);
gen_align!(1024, Align1024, U1024, [[u8; 32]; 32]);
gen_align!(2048, Align2048, U2048, [[[u8; 32]; 32]; 2]);
gen_align!(4096, Align4096, U4096, [[[u8; 32]; 32]; 4]);
gen_align!(8192, Align8192, U8192, [[[u8; 32]; 32]; 8]);
gen_align!(16384, Align16384, U16384, [[[u8; 32]; 32]; 16]);
gen_align!(32768, Align32768, U32768, [[[u8; 32]; 32]; 32]);
gen_align!(65536, Align65536, U65536, [[[[u8; 32]; 32]; 32]; 2]);

#[allow(unknown_lints)]
#[allow(clippy::missing_transmute_annotations)]
Expand Down
78 changes: 73 additions & 5 deletions stabby-macros/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use proc_macro2::Ident;
use quote::quote;
use syn::{Attribute, DataStruct, Generics, Visibility};
use syn::{spanned::Spanned, Attribute, DataStruct, Generics, Visibility};

use crate::Unself;

Expand Down Expand Up @@ -55,6 +55,49 @@ impl syn::parse::Parse for Args {
Ok(this)
}
}
#[derive(Copy, Clone)]
enum AllowedRepr {
C,
Transparent,
Align(usize),
}
impl syn::parse::Parse for AllowedRepr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut content = input.fork();
if input.peek(syn::token::Paren) {
syn::parenthesized!(content in input);
}
let ident: Ident = content.parse()?;
Ok(match ident.to_string().as_str() {
"C" => AllowedRepr::C,
"transparent" => AllowedRepr::Transparent,
"packed" => return Err(input.error("stabby does not support packed structs, though you may implement IStable manually if you're very comfident")),
"align" => {
let input = content;
syn::parenthesized!(content in input);
let lit: syn::LitInt = content.parse()?;
AllowedRepr::Align(lit.base10_parse()?)
}
_ => {
return Err(input.error(
"Only #[repr(C)] and #[repr(transparent)] are allowed for stabby structs",
))
}
})
}
}
impl quote::ToTokens for AllowedRepr {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(match self {
AllowedRepr::C => quote!(#[repr(C)]),
AllowedRepr::Transparent => quote!(#[repr(transparent)]),
AllowedRepr::Align(n) => {
let n = syn::LitInt::new(&format!("{n}"), tokens.span());
quote!(#[repr(align(#n))])
}
})
}
}

pub fn stabby(
attrs: Vec<Attribute>,
Expand All @@ -79,6 +122,17 @@ pub fn stabby(
let clauses = where_clause.as_ref().map(|w| &w.predicates);
let mut layout = None;
let mut report = crate::Report::r#struct(ident.to_string(), version, module);
let repr = attrs
.iter()
.find_map(|attr| {
if attr.path.is_ident("repr") {
syn::parse2::<AllowedRepr>(attr.tokens.clone()).ok()
} else {
None
}
})
.unwrap_or(AllowedRepr::C);
optimize &= !matches!(repr, AllowedRepr::Align(_));
let struct_code = match &fields {
syn::Fields::Named(fields) => {
let fields = &fields.named;
Expand All @@ -92,7 +146,7 @@ pub fn stabby(
}
quote! {
#(#attrs)*
#[repr(C)]
#repr
#vis struct #ident #generics #where_clause {
#fields
}
Expand All @@ -110,19 +164,33 @@ pub fn stabby(
}
quote! {
#(#attrs)*
#[repr(C)]
#repr
#vis struct #ident #generics #where_clause (#fields);
}
}
syn::Fields::Unit => {
quote! {
#(#attrs)*
#[repr(C)]
#repr
#vis struct #ident #generics #where_clause;
}
}
};
let layout = layout.map_or_else(|| quote!(()), |layout| quote!(#st::Struct<#layout>));
let layout = layout.map_or_else(
|| quote!(()),
|layout| {
if let AllowedRepr::Align(mut n) = repr {
let mut align = quote!(#st::U1);
while n > 1 {
n /= 2;
align = quote!(#st::UInt<#align, #st::B0>);
}
quote!(#st::AlignedStruct<#layout, #align>)
} else {
quote!(#st::Struct<#layout>)
}
},
);
let opt_id = quote::format_ident!("OptimizedLayoutFor{ident}");
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"
Expand Down
25 changes: 7 additions & 18 deletions stabby/src/tests/layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,26 +254,15 @@ fn layouts() {
>(),
3 * 16
);
let _ = Align1024::ID;
}

#[allow(dead_code)]
#[stabby::stabby]
#[repr(align(16))]
struct Align128(u128);
unsafe impl stabby::abi::IStable for Align128 {
type Size = U16;
type Align = U16;
type ForbiddenValues = End;
type UnusedBits = End;
type HasExactlyOneNiche = B0;
type ContainsIndirections = B0;
#[cfg(feature = "experimental-ctypes")]
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!()),
fields: stabby::abi::StableLike::new(None),
version: 0,
tyty: stabby::abi::report::TyTy::Struct,
};
const ID: u64 = stabby::abi::report::gen_id(Self::REPORT);
}

#[allow(dead_code)]
#[stabby::stabby]
#[repr(align(1024))]
struct Align1024(u8);

0 comments on commit caeccb0

Please sign in to comment.