Skip to main content

pin_init_internal/
init.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use proc_macro2::{Span, TokenStream};
4use quote::{format_ident, quote};
5use syn::{
6    braced,
7    parse::{End, Parse},
8    parse_quote,
9    punctuated::Punctuated,
10    spanned::Spanned,
11    token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
12};
13
14use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
15
16pub(crate) struct Initializer {
17    attrs: Vec<InitializerAttribute>,
18    this: Option<This>,
19    path: Path,
20    brace_token: token::Brace,
21    fields: Punctuated<InitializerField, Token![,]>,
22    rest: Option<(Token![..], Expr)>,
23    error: Option<(Token![?], Type)>,
24}
25
26struct This {
27    _and_token: Token![&],
28    ident: Ident,
29    _in_token: Token![in],
30}
31
32struct InitializerField {
33    attrs: Vec<Attribute>,
34    kind: InitializerKind,
35}
36
37enum InitializerKind {
38    Value {
39        ident: Ident,
40        value: Option<(Token![:], Expr)>,
41    },
42    Init {
43        ident: Ident,
44        _left_arrow_token: Token![<-],
45        value: Expr,
46    },
47    Code {
48        _underscore_token: Token![_],
49        _colon_token: Token![:],
50        block: Block,
51    },
52}
53
54impl InitializerKind {
55    fn ident(&self) -> Option<&Ident> {
56        match self {
57            Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident),
58            Self::Code { .. } => None,
59        }
60    }
61}
62
63enum InitializerAttribute {
64    DefaultError(DefaultErrorAttribute),
65}
66
67struct DefaultErrorAttribute {
68    ty: Box<Type>,
69}
70
71pub(crate) fn expand(
72    Initializer {
73        attrs,
74        this,
75        path,
76        brace_token,
77        fields,
78        rest,
79        error,
80    }: Initializer,
81    default_error: Option<&'static str>,
82    pinned: bool,
83    dcx: &mut DiagCtxt,
84) -> Result<TokenStream, ErrorGuaranteed> {
85    let error = error.map_or_else(
86        || {
87            if let Some(default_error) = attrs.iter().fold(None, |acc, attr| {
88                #[expect(irrefutable_let_patterns)]
89                if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr {
90                    Some(ty.clone())
91                } else {
92                    acc
93                }
94            }) {
95                default_error
96            } else if let Some(default_error) = default_error {
97                syn::parse_str(default_error).unwrap()
98            } else {
99                dcx.error(brace_token.span.close(), "expected `? <type>` after `}`");
100                parse_quote!(::core::convert::Infallible)
101            }
102        },
103        |(_, err)| Box::new(err),
104    );
105    let slot = format_ident!("slot");
106    let (has_data_trait, get_data, init_from_closure) = if pinned {
107        (
108            format_ident!("HasPinData"),
109            format_ident!("__pin_data"),
110            format_ident!("pin_init_from_closure"),
111        )
112    } else {
113        (
114            format_ident!("HasInitData"),
115            format_ident!("__init_data"),
116            format_ident!("init_from_closure"),
117        )
118    };
119    let init_kind = get_init_kind(rest, dcx);
120    let zeroable_check = match init_kind {
121        InitKind::Normal => quote!(),
122        InitKind::Zeroing => quote! {
123            // The user specified `..Zeroable::zeroed()` at the end of the list of fields.
124            // Therefore we check if the struct implements `Zeroable` and then zero the memory.
125            // This allows us to also remove the check that all fields are present (since we
126            // already set the memory to zero and that is a valid bit pattern).
127            fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
128            where T: ::pin_init::Zeroable
129            {}
130            // Ensure that the struct is indeed `Zeroable`.
131            assert_zeroable(#slot);
132            // SAFETY: The type implements `Zeroable` by the check above.
133            unsafe { ::core::ptr::write_bytes(#slot, 0, 1) };
134        },
135    };
136    let this = match this {
137        None => quote!(),
138        Some(This { ident, .. }) => quote! {
139            // Create the `this` so it can be referenced by the user inside of the
140            // expressions creating the individual fields.
141            let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
142        },
143    };
144    // `mixed_site` ensures that the data is not accessible to the user-controlled code.
145    let data = Ident::new("__data", Span::mixed_site());
146    let init_fields = init_fields(&fields, pinned, &data, &slot);
147    let field_check = make_field_check(&fields, init_kind, &path);
148    Ok(quote! {{
149        // Get the data about fields from the supplied type.
150        // SAFETY: TODO
151        let #data = unsafe {
152            use ::pin_init::__internal::#has_data_trait;
153            // Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit
154            // generics (which need to be present with that syntax).
155            #path::#get_data()
156        };
157        // Ensure that `#data` really is of type `#data` and help with type inference:
158        let init = #data.__make_closure::<_, #error>(
159            move |slot| {
160                #zeroable_check
161                #this
162                #init_fields
163                #field_check
164                // SAFETY: we are the `init!` macro that is allowed to call this.
165                Ok(unsafe { ::pin_init::__internal::InitOk::new() })
166            }
167        );
168        let init = move |slot| -> ::core::result::Result<(), #error> {
169            init(slot).map(|__InitOk| ())
170        };
171        // SAFETY: TODO
172        unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }
173    }})
174}
175
176enum InitKind {
177    Normal,
178    Zeroing,
179}
180
181fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKind {
182    let Some((dotdot, expr)) = rest else {
183        return InitKind::Normal;
184    };
185    match &expr {
186        Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func {
187            Expr::Path(ExprPath {
188                attrs,
189                qself: None,
190                path:
191                    Path {
192                        leading_colon: None,
193                        segments,
194                    },
195            }) if attrs.is_empty()
196                && segments.len() == 2
197                && segments[0].ident == "Zeroable"
198                && segments[0].arguments.is_none()
199                && segments[1].ident == "init_zeroed"
200                && segments[1].arguments.is_none() =>
201            {
202                return InitKind::Zeroing;
203            }
204            _ => {}
205        },
206        _ => {}
207    }
208    dcx.error(
209        dotdot.span().join(expr.span()).unwrap_or(expr.span()),
210        "expected nothing or `..Zeroable::init_zeroed()`.",
211    );
212    InitKind::Normal
213}
214
215/// Generate the code that initializes the fields of the struct using the initializers in `field`.
216fn init_fields(
217    fields: &Punctuated<InitializerField, Token![,]>,
218    pinned: bool,
219    data: &Ident,
220    slot: &Ident,
221) -> TokenStream {
222    let mut guards = vec![];
223    let mut guard_attrs = vec![];
224    let mut res = TokenStream::new();
225    for InitializerField { attrs, kind } in fields {
226        let cfgs = {
227            let mut cfgs = attrs.clone();
228            cfgs.retain(|attr| attr.path().is_ident("cfg"));
229            cfgs
230        };
231
232        let ident = match kind {
233            InitializerKind::Value { ident, .. } => ident,
234            InitializerKind::Init { ident, .. } => ident,
235            InitializerKind::Code { block, .. } => {
236                let stmt = &block.stmts;
237                res.extend(quote! {
238                    #(#attrs)*
239                    {
240                        #(#stmt)*
241                    }
242                });
243                continue;
244            }
245        };
246
247        let slot = if pinned {
248            quote! {
249                // SAFETY:
250                // - `slot` is valid and properly aligned.
251                // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
252                // - `make_field_check` prevents `#ident` from being used twice, therefore
253                //   `(*slot).#ident` is exclusively accessed and has not been initialized.
254                (unsafe { #data.#ident(#slot) })
255            }
256        } else {
257            quote! {
258                // For `init!()` macro, everything is unpinned.
259                // SAFETY:
260                // - `&raw mut (*slot).#ident` is valid.
261                // - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
262                // - `make_field_check` prevents `#ident` from being used twice, therefore
263                //   `(*slot).#ident` is exclusively accessed and has not been initialized.
264                (unsafe {
265                    ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
266                        &raw mut (*#slot).#ident
267                    )
268                })
269            }
270        };
271
272        // `mixed_site` ensures that the guard is not accessible to the user-controlled code.
273        let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
274
275        let init = match kind {
276            InitializerKind::Value { ident, value } => {
277                let value = value
278                    .as_ref()
279                    .map(|(_, value)| quote!(#value))
280                    .unwrap_or_else(|| quote!(#ident));
281
282                quote! {
283                    #(#attrs)*
284                    let mut #guard = #slot.write(#value);
285
286                }
287            }
288            InitializerKind::Init { value, .. } => {
289                quote! {
290                    #(#attrs)*
291                    let mut #guard = #slot.init(#value)?;
292                }
293            }
294            InitializerKind::Code { .. } => unreachable!(),
295        };
296
297        res.extend(quote! {
298            #init
299
300            #(#cfgs)*
301            // Allow `non_snake_case` since the same warning is going to be reported for the struct
302            // field.
303            #[allow(unused_variables, non_snake_case)]
304            let #ident = #guard.let_binding();
305        });
306
307        guards.push(guard);
308        guard_attrs.push(cfgs);
309    }
310    quote! {
311        #res
312        // If execution reaches this point, all fields have been initialized. Therefore we can now
313        // dismiss the guards by forgetting them.
314        #(
315            #(#guard_attrs)*
316            ::core::mem::forget(#guards);
317        )*
318    }
319}
320
321/// Generate the check for ensuring that every field has been initialized and aligned.
322fn make_field_check(
323    fields: &Punctuated<InitializerField, Token![,]>,
324    init_kind: InitKind,
325    path: &Path,
326) -> TokenStream {
327    let field_attrs: Vec<_> = fields
328        .iter()
329        .filter_map(|f| f.kind.ident().map(|_| &f.attrs))
330        .collect();
331    let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect();
332    let zeroing_trailer = match init_kind {
333        InitKind::Normal => None,
334        InitKind::Zeroing => Some(quote! {
335            ..::core::mem::zeroed()
336        }),
337    };
338    quote! {
339        #[allow(unreachable_code)]
340        // We use unreachable code to perform field checks. They're still checked by the compiler.
341        // SAFETY: this code is never executed.
342        let _ = || unsafe {
343            // Create references to ensure that the initialized field is properly aligned.
344            // Unaligned fields will cause the compiler to emit E0793. We do not support
345            // unaligned fields since `Init::__init` requires an aligned pointer; the call to
346            // `ptr::write` for value-initialization case has the same requirement.
347            #(
348                #(#field_attrs)*
349                let _ = &(*slot).#field_name;
350            )*
351
352            // If the zeroing trailer is not present, this checks that all fields have been
353            // mentioned exactly once. If the zeroing trailer is present, all missing fields will be
354            // zeroed, so this checks that all fields have been mentioned at most once. The use of
355            // struct initializer will still generate very natural error messages for any misuse.
356            ::core::ptr::write(slot, #path {
357                #(
358                    #(#field_attrs)*
359                    #field_name: loop {},
360                )*
361                #zeroing_trailer
362            })
363        };
364    }
365}
366
367impl Parse for Initializer {
368    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
369        let attrs = input.call(Attribute::parse_outer)?;
370        let this = input.peek(Token![&]).then(|| input.parse()).transpose()?;
371        let path = input.parse()?;
372        let content;
373        let brace_token = braced!(content in input);
374        let mut fields = Punctuated::new();
375        loop {
376            let lh = content.lookahead1();
377            if lh.peek(End) || lh.peek(Token![..]) {
378                break;
379            } else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Token![#]) {
380                fields.push_value(content.parse()?);
381                let lh = content.lookahead1();
382                if lh.peek(End) {
383                    break;
384                } else if lh.peek(Token![,]) {
385                    fields.push_punct(content.parse()?);
386                } else {
387                    return Err(lh.error());
388                }
389            } else {
390                return Err(lh.error());
391            }
392        }
393        let rest = content
394            .peek(Token![..])
395            .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?)))
396            .transpose()?;
397        let error = input
398            .peek(Token![?])
399            .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
400            .transpose()?;
401        let attrs = attrs
402            .into_iter()
403            .map(|a| {
404                if a.path().is_ident("default_error") {
405                    a.parse_args::<DefaultErrorAttribute>()
406                        .map(InitializerAttribute::DefaultError)
407                } else {
408                    Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
409                }
410            })
411            .collect::<Result<Vec<_>, _>>()?;
412        Ok(Self {
413            attrs,
414            this,
415            path,
416            brace_token,
417            fields,
418            rest,
419            error,
420        })
421    }
422}
423
424impl Parse for DefaultErrorAttribute {
425    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
426        Ok(Self { ty: input.parse()? })
427    }
428}
429
430impl Parse for This {
431    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
432        Ok(Self {
433            _and_token: input.parse()?,
434            ident: input.parse()?,
435            _in_token: input.parse()?,
436        })
437    }
438}
439
440impl Parse for InitializerField {
441    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
442        let attrs = input.call(Attribute::parse_outer)?;
443        Ok(Self {
444            attrs,
445            kind: input.parse()?,
446        })
447    }
448}
449
450impl Parse for InitializerKind {
451    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
452        let lh = input.lookahead1();
453        if lh.peek(Token![_]) {
454            Ok(Self::Code {
455                _underscore_token: input.parse()?,
456                _colon_token: input.parse()?,
457                block: input.parse()?,
458            })
459        } else if lh.peek(Ident) {
460            let ident = input.parse()?;
461            let lh = input.lookahead1();
462            if lh.peek(Token![<-]) {
463                Ok(Self::Init {
464                    ident,
465                    _left_arrow_token: input.parse()?,
466                    value: input.parse()?,
467                })
468            } else if lh.peek(Token![:]) {
469                Ok(Self::Value {
470                    ident,
471                    value: Some((input.parse()?, input.parse()?)),
472                })
473            } else if lh.peek(Token![,]) || lh.peek(End) {
474                Ok(Self::Value { ident, value: None })
475            } else {
476                Err(lh.error())
477            }
478        } else {
479            Err(lh.error())
480        }
481    }
482}