]> git.proxmox.com Git - rustc.git/blob - vendor/zerovec-derive/src/ule.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / vendor / zerovec-derive / src / ule.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
5 use proc_macro2::TokenStream as TokenStream2;
6 use quote::quote;
7
8 use crate::utils::{self, FieldInfo};
9 use syn::spanned::Spanned;
10 use syn::{Data, DeriveInput, Error};
11
12 pub fn derive_impl(input: &DeriveInput) -> TokenStream2 {
13 if !utils::has_valid_repr(&input.attrs, |r| r == "packed" || r == "transparent") {
14 return Error::new(
15 input.span(),
16 "derive(ULE) must be applied to a #[repr(packed)] or #[repr(transparent)] type",
17 )
18 .to_compile_error();
19 }
20 if input.generics.type_params().next().is_some()
21 || input.generics.lifetimes().next().is_some()
22 || input.generics.const_params().next().is_some()
23 {
24 return Error::new(
25 input.generics.span(),
26 "derive(ULE) must be applied to a struct without any generics",
27 )
28 .to_compile_error();
29 }
30 let struc = if let Data::Struct(ref s) = input.data {
31 if s.fields.iter().next().is_none() {
32 return Error::new(
33 input.span(),
34 "derive(ULE) must be applied to a non-empty struct",
35 )
36 .to_compile_error();
37 }
38 s
39 } else {
40 return Error::new(input.span(), "derive(ULE) must be applied to a struct")
41 .to_compile_error();
42 };
43
44 let fields = FieldInfo::make_list(struc.fields.iter());
45 let (validators, remaining_offset) = generate_ule_validators(&fields);
46
47 let name = &input.ident;
48
49 // Safety (based on the safety checklist on the ULE trait):
50 // 1. #name does not include any uninitialized or padding bytes.
51 // (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
52 // 2. #name is aligned to 1 byte.
53 // (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
54 // 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
55 // 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
56 // 5. The other ULE methods use the default impl.
57 // 6. [This impl does not enforce the non-safety equality constraint, it is up to the user to do so, ideally via a custom derive]
58 quote! {
59 unsafe impl zerovec::ule::ULE for #name {
60 #[inline]
61 fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> {
62 const SIZE: usize = ::core::mem::size_of::<#name>();
63 #[allow(clippy::modulo_one)]
64 if bytes.len() % SIZE != 0 {
65 return Err(zerovec::ZeroVecError::length::<Self>(bytes.len()));
66 }
67 // Validate the bytes
68 #[allow(clippy::indexing_slicing)] // We're slicing a chunk of known size
69 for chunk in bytes.chunks_exact(SIZE) {
70 #validators
71 debug_assert_eq!(#remaining_offset, SIZE);
72 }
73 Ok(())
74 }
75 }
76 }
77 }
78
79 /// Given an slice over ULE struct fields, returns code validating that a slice variable `bytes` contains valid instances of those ULE types
80 /// in order, plus the byte offset of any remaining unvalidated bytes. ULE types should not have any remaining bytes, but VarULE types will since
81 /// the last field is the unsized one.
82 pub(crate) fn generate_ule_validators(
83 fields: &[FieldInfo],
84 // (validators, remaining_offset)
85 ) -> (TokenStream2, syn::Ident) {
86 utils::generate_per_field_offsets(fields, false, |field, prev_offset_ident, size_ident| {
87 let ty = &field.field.ty;
88 quote! {
89 #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid indices
90 <#ty as zerovec::ule::ULE>::validate_byte_slice(&bytes[#prev_offset_ident .. #prev_offset_ident + #size_ident])?;
91 }
92 })
93 }
94
95 /// Make corresponding ULE fields for each field
96 pub(crate) fn make_ule_fields(fields: &[FieldInfo]) -> Vec<TokenStream2> {
97 fields
98 .iter()
99 .map(|f| {
100 let ty = &f.field.ty;
101 let ty = quote!(<#ty as zerovec::ule::AsULE>::ULE);
102 let setter = f.setter();
103 let vis = &f.field.vis;
104 quote!(#vis #setter #ty)
105 })
106 .collect::<Vec<_>>()
107 }