const-field-offset-macro-0.2.0/.cargo_vcs_info.json0000644000000002040000000000100156210ustar { "git": { "sha1": "443908daeed77db500781670448d3688e4be0b9a" }, "path_in_vcs": "helper_crates/const-field-offset/macro" }const-field-offset-macro-0.2.0/Cargo.lock0000644000000030270000000000100136020ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "const-field-offset-macro" version = "0.2.0" dependencies = [ "memoffset", "proc-macro2", "quote", "syn", ] [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" const-field-offset-macro-0.2.0/Cargo.toml0000644000000022700000000000100136240ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.88" name = "const-field-offset-macro" version = "0.2.0" authors = ["Slint Developers "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Procedural macro to generate constant field offsets using core::mem::offset_of!" homepage = "https://slint.dev" readme = false license = "MIT OR Apache-2.0" repository = "https://github.com/slint-ui/slint" [lib] name = "const_field_offset_macro" path = "macro.rs" proc-macro = true [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["derive"] [dev-dependencies.memoffset] version = "0.9.0" const-field-offset-macro-0.2.0/Cargo.toml.orig000064400000000000000000000012321046102023000173020ustar 00000000000000# Copyright © SixtyFPS GmbH # SPDX-License-Identifier: MIT OR Apache-2.0 [package] name = "const-field-offset-macro" version = "0.2.0" authors = ["Slint Developers "] edition = "2024" rust-version = "1.88" license = "MIT OR Apache-2.0" description = "Procedural macro to generate constant field offsets using core::mem::offset_of!" repository = "https://github.com/slint-ui/slint" homepage = "https://slint.dev" [lib] path = "macro.rs" proc-macro = true [dependencies] syn = { version = "2.0", features = ["derive"] } quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] memoffset = "0.9.0" const-field-offset = { path = ".." } const-field-offset-macro-0.2.0/macro.rs000064400000000000000000000164361046102023000160760ustar 00000000000000// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: MIT OR Apache-2.0 /*! This crate allow to get the offset of a field of a structure in a const or static context. To be used re-exported from the `const_field_offset` crate */ extern crate proc_macro; use proc_macro::TokenStream; use quote::{format_ident, quote, quote_spanned}; use syn::{DeriveInput, parse_macro_input, spanned::Spanned}; /** The macro FieldOffsets adds a `FIELD_OFFSETS` associated const to the struct. That is an object which has fields with the same name as the fields of the original struct, each field is of type `const_field_offset::FieldOffset` ```rust use const_field_offset::FieldOffsets; #[repr(C)] #[derive(FieldOffsets)] struct Foo { field_1 : u8, field_2 : u32, } const FOO : usize = Foo::FIELD_OFFSETS.field_2().get_byte_offset(); assert_eq!(FOO, 4); ``` ## Attributes ### `pin` Add a `AllowPin` to the FieldOffset. In order for this to be safe, the macro will add code to prevent a custom `Drop` or `Unpin` implementation. ```rust use const_field_offset::*; #[derive(FieldOffsets)] #[pin] struct Foo { field_1 : u8, field_2 : u32, } const FIELD_2 : FieldOffset = Foo::FIELD_OFFSETS.field_2(); let pin_box = Box::pin(Foo{field_1: 1, field_2: 2}); assert_eq!(*FIELD_2.apply_pin(pin_box.as_ref()), 2); ``` ### `pin_drop` This attribute works like the `pin` attribute but it does not prevent a custom Drop implementation. Instead it provides a Drop implementation that forwards to the [PinnedDrop](../const_field_offset/trait.PinnedDrop.html) trait that you need to implement for our type. ```rust use const_field_offset::*; use core::pin::Pin; struct TypeThatRequiresSpecialDropHandling(); // ... #[derive(FieldOffsets)] #[pin_drop] struct Foo { field : TypeThatRequiresSpecialDropHandling, } impl PinnedDrop for Foo { fn drop(self: Pin<&mut Self>) { // Do you safe drop handling here } } ``` ### `const-field-offset` In case the `const-field-offset` crate is re-exported, it is possible to specify the crate name using the `const_field_offset` attribute. ```rust // suppose you re-export the const_field_offset create from a different module mod xxx { pub use const_field_offset as cfo; } #[derive(xxx::cfo::FieldOffsets)] #[const_field_offset(xxx::cfo)] struct Foo { field_1 : u8, field_2 : u32, } ``` */ #[proc_macro_derive(FieldOffsets, attributes(const_field_offset, pin, pin_drop))] pub fn const_field_offset(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let mut crate_ = quote!(const_field_offset); let mut pin = false; let mut drop = false; for a in &input.attrs { if let Some(i) = a.path().get_ident() { if i == "const_field_offset" { match a.parse_args::() { Ok(c) => crate_ = quote!(#c), Err(_) => { return TokenStream::from( quote_spanned!(a.span()=> compile_error!{"const_field_offset attribute must be a crate name"}), ); } } } else if i == "pin" { pin = true; } else if i == "pin_drop" { drop = true; pin = true; } } } let struct_name = input.ident; let struct_vis = input.vis; let field_struct_name = quote::format_ident!("{}FieldsOffsets", struct_name); let (fields, types, vis) = if let syn::Data::Struct(s) = &input.data { if let syn::Fields::Named(n) = &s.fields { let (f, tv): (Vec<_>, Vec<_>) = n.named.iter().map(|f| (&f.ident, (&f.ty, &f.vis))).unzip(); let (t, v): (Vec<_>, Vec<_>) = tv.into_iter().unzip(); (f, t, v) } else { return TokenStream::from(quote! {compile_error!{"Only work for named fields"}}); } } else { return TokenStream::from(quote! {compile_error!("Only work for struct")}); }; let doc = format!( "Helper struct containing the offsets of the fields of the struct [`{struct_name}`]\n\n\ Generated from the `#[derive(FieldOffsets)]` macro from the [`const-field-offset`]({crate_}) crate", ); let (ensure_pin_safe, ensure_no_unpin, pin_flag, new_from_offset) = if !pin { (None, None, quote!(#crate_::NotPinned), quote!(new_from_offset)) } else { ( if drop { None } else { let drop_trait_ident = format_ident!("{}MustNotImplDrop", struct_name); Some(quote! { /// Make sure that Drop is not implemented #[allow(non_camel_case_types)] trait #drop_trait_ident {} impl #drop_trait_ident for T {} impl #drop_trait_ident for #struct_name {} }) }, Some(quote! { const _ : () = { /// Make sure that Unpin is not implemented #[allow(dead_code)] struct __MustNotImplUnpin<'__dummy_lifetime> ( ::core::marker::PhantomData<&'__dummy_lifetime ()> ); impl<'__dummy_lifetime> Unpin for #struct_name where __MustNotImplUnpin<'__dummy_lifetime> : Unpin {}; }; }), quote!(#crate_::AllowPin), quote!(new_from_offset_pinned), ) }; let pinned_drop_impl = if drop { Some(quote!( impl Drop for #struct_name { fn drop(&mut self) { use #crate_::PinnedDrop; self.do_safe_pinned_drop(); } } )) } else { None }; // Build the output, possibly using quasi-quotation let expanded = quote! { #[doc = #doc] #[allow(missing_docs, non_camel_case_types, dead_code)] #struct_vis struct #field_struct_name; #ensure_pin_safe #[allow(non_snake_case, missing_docs)] impl #field_struct_name { #( #vis const fn #fields(self) -> #crate_::FieldOffset<#struct_name, #types, #pin_flag> { // Safety: offset_of! returns the correct byte offset for this field unsafe { #crate_::FieldOffset::<#struct_name, #types, _>::#new_from_offset( ::core::mem::offset_of!(#struct_name, #fields) ) } } )* } impl #struct_name { /// Return a zero-sized helper whose methods return field offsets. pub const FIELD_OFFSETS : #field_struct_name = #field_struct_name; } #pinned_drop_impl #ensure_no_unpin }; // Hand the output tokens back to the compiler TokenStream::from(expanded) } /** ```compile_fail use const_field_offset::*; #[derive(FieldOffsets)] #[pin] struct Foo { x: u32, } impl Drop for Foo { fn drop(&mut self) {} } ``` */ #[cfg(doctest)] const _PIN_NO_DROP: u32 = 0; /** ```compile_fail use const_field_offset::*; #[derive(FieldOffsets)] #[pin] struct Foo { q: std::marker::PhantomPinned, x: u32, } impl Unpin for Foo {} ``` */ #[cfg(doctest)] const _PIN_NO_UNPIN: u32 = 0;