zerovec_derive/
varule.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use crate::utils::{self, FieldInfo};
6use proc_macro2::Span;
7use proc_macro2::TokenStream as TokenStream2;
8use quote::quote;
9use syn::spanned::Spanned;
10use syn::{Data, DeriveInput, Error, Ident};
11
12/// Implementation for derive(VarULE). `custom_varule_validator` validates the last field bytes `last_field_bytes`
13/// if specified, if not, the VarULE implementation will be used.
14pub fn derive_impl(
15    input: &DeriveInput,
16    custom_varule_validator: Option<TokenStream2>,
17) -> TokenStream2 {
18    if !utils::ReprInfo::compute(&input.attrs).cpacked_or_transparent() {
19        return Error::new(
20            input.span(),
21            "derive(VarULE) must be applied to a #[repr(C, packed)] or #[repr(transparent)] type",
22        )
23        .to_compile_error();
24    }
25    if input.generics.type_params().next().is_some()
26        || input.generics.lifetimes().next().is_some()
27        || input.generics.const_params().next().is_some()
28    {
29        return Error::new(
30            input.generics.span(),
31            "derive(VarULE) must be applied to a struct without any generics",
32        )
33        .to_compile_error();
34    }
35    let struc = if let Data::Struct(ref s) = input.data {
36        if s.fields.iter().next().is_none() {
37            return Error::new(
38                input.span(),
39                "derive(VarULE) must be applied to a non-empty struct",
40            )
41            .to_compile_error();
42        }
43        s
44    } else {
45        return Error::new(input.span(), "derive(VarULE) must be applied to a struct")
46            .to_compile_error();
47    };
48
49    let n_fields = struc.fields.len();
50
51    let ule_fields = FieldInfo::make_list(struc.fields.iter().take(n_fields - 1));
52
53    let sizes = ule_fields.iter().map(|f| {
54        let ty = &f.field.ty;
55        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "core");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "mem");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "size_of");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote!(::core::mem::size_of::<#ty>())
56    });
57    let (validators, remaining_offset) = if n_fields > 1 {
58        // generate ULE validators
59        crate::ule::generate_ule_validators(&ule_fields)
60    } else {
61        // no ULE subfields
62        (
63            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "const");
    ::quote::__private::push_ident(&mut _s, "ZERO");
    ::quote::__private::push_colon(&mut _s);
    ::quote::__private::push_ident(&mut _s, "usize");
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::parse(&mut _s, "0");
    ::quote::__private::push_semi(&mut _s);
    _s
}quote!(
64                const ZERO: usize = 0;
65            ),
66            Ident::new("ZERO", Span::call_site()),
67        )
68    };
69
70    let unsized_field = &struc
71        .fields
72        .iter()
73        .next_back()
74        .expect("Already verified that struct is not empty")
75        .ty;
76
77    let name = &input.ident;
78    let ule_size = Ident::new(
79        &::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("__IMPL_VarULE_FOR_{0}_ULE_SIZE",
                name))
    })format!("__IMPL_VarULE_FOR_{name}_ULE_SIZE"),
80        Span::call_site(),
81    );
82
83    let last_field_validator = if let Some(custom_varule_validator) = custom_varule_validator {
84        custom_varule_validator
85    } else {
86        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&unsized_field, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_ident(&mut _s, "zerovec");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ule");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "VarULE");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "validate_bytes");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "last_field_bytes");
            _s
        });
    ::quote::__private::push_question(&mut _s);
    ::quote::__private::push_semi(&mut _s);
    _s
}quote!(<#unsized_field as zerovec::ule::VarULE>::validate_bytes(last_field_bytes)?;)
87    };
88
89    // Safety (based on the safety checklist on the ULE trait):
90    //  1. #name does not include any uninitialized or padding bytes
91    //     (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
92    //  2. #name is aligned to 1 byte.
93    //     (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
94    //  3. The impl of `validate_bytes()` returns an error if any byte is not valid.
95    //  4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety
96    //  5. The impl of `from_bytes_unchecked()` returns a reference to the same data.
97    //  6. The other VarULE methods use the default impl
98    //  7. [This impl does not enforce the non-safety equality constraint, it is up to the user to do so, ideally via a custom derive]
99    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "const");
    ::quote::ToTokens::to_tokens(&ule_size, &mut _s);
    ::quote::__private::push_colon(&mut _s);
    ::quote::__private::push_ident(&mut _s, "usize");
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::parse(&mut _s, "0");
    {
        use ::quote::__private::ext::*;
        let has_iter = ::quote::__private::HasIterator::<false>;
        #[allow(unused_mut)]
        let (mut sizes, i) = sizes.quote_into_iter();
        let has_iter = has_iter | i;
        <_ as ::quote::__private::CheckHasIterator<true>>::check(has_iter);
        while true {
            let sizes =
                match sizes.next() {
                    Some(_x) => ::quote::__private::RepInterp(_x),
                    None => break,
                };
            ::quote::__private::push_add(&mut _s);
            ::quote::ToTokens::to_tokens(&sizes, &mut _s);
        }
    }
    ::quote::__private::push_semi(&mut _s);
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::__private::push_ident(&mut _s, "zerovec");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ule");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "VarULE");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inline");
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "validate_bytes");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "u8");
                            _s
                        });
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "zerovec");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ule");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "UleError");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "debug_assert_eq");
                    ::quote::__private::push_bang(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&remaining_offset, &mut _s);
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::ToTokens::to_tokens(&ule_size, &mut _s);
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "Some");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "last_field_bytes");
                            _s
                        });
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "get");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&remaining_offset, &mut _s);
                            ::quote::__private::push_dot2(&mut _s);
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "else");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "return");
                            ::quote::__private::push_ident(&mut _s, "Err");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "zerovec");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "ule");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "UleError");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "parse");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_lt(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_gt(&mut _s);
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        ::quote::__private::TokenStream::new());
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::ToTokens::to_tokens(&validators, &mut _s);
                    ::quote::ToTokens::to_tokens(&last_field_validator,
                        &mut _s);
                    ::quote::__private::push_ident(&mut _s, "Ok");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            _s
                        });
                    _s
                });
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inline");
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "unsafe");
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "from_bytes_unchecked");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "u8");
                            _s
                        });
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_and(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Self");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "unsized_bytes");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "get_unchecked");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&ule_size, &mut _s);
                            ::quote::__private::push_dot2(&mut _s);
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "unsized_ref");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::ToTokens::to_tokens(&unsized_field, &mut _s);
                    ::quote::__private::push_ident(&mut _s, "as");
                    ::quote::__private::push_ident(&mut _s, "zerovec");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "ule");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "VarULE");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s,
                        "from_bytes_unchecked");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "unsized_bytes");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "_ptr");
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "metadata");
                            _s
                        });
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "usize");
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "usize");
                            _s
                        });
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "mem");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "transmute");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "unsized_ref");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s,
                        "entire_struct_as_slice");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_star(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "const");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "u8");
                            _s
                        });
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "slice");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "from_raw_parts");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "bytes");
                            ::quote::__private::push_dot(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "as_ptr");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            ::quote::__private::push_comma(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "metadata");
                            _s
                        });
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_star(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s,
                                "entire_struct_as_slice");
                            ::quote::__private::push_ident(&mut _s, "as");
                            ::quote::__private::push_star(&mut _s);
                            ::quote::__private::push_ident(&mut _s, "const");
                            ::quote::__private::push_ident(&mut _s, "Self");
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
100        // The size of the ULE section of this type
101        const #ule_size: usize = 0 #(+ #sizes)*;
102        unsafe impl zerovec::ule::VarULE for #name {
103            #[inline]
104            fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
105                debug_assert_eq!(#remaining_offset, #ule_size);
106
107                let Some(last_field_bytes) = bytes.get(#remaining_offset..) else {
108                    return Err(zerovec::ule::UleError::parse::<Self>());
109                };
110                #validators
111                #last_field_validator
112                Ok(())
113            }
114            #[inline]
115            unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
116                // just the unsized part
117                // Safety: The invariants of this function allow us to assume bytes is valid, and
118                // having at least #ule_size bytes is a validity constraint for the ULE type.
119                let unsized_bytes = bytes.get_unchecked(#ule_size..);
120                let unsized_ref = <#unsized_field as zerovec::ule::VarULE>::from_bytes_unchecked(unsized_bytes);
121                // We should use the pointer metadata APIs here when they are stable: https://github.com/rust-lang/rust/issues/81513
122                // For now we rely on all DST metadata being a usize to extract it via a fake slice pointer
123                let (_ptr, metadata): (usize, usize) = ::core::mem::transmute(unsized_ref);
124                let entire_struct_as_slice: *const [u8] = ::core::slice::from_raw_parts(bytes.as_ptr(), metadata);
125                &*(entire_struct_as_slice as *const Self)
126            }
127        }
128    }
129}