xref: /linux/rust/pin-init/internal/src/init.rs (revision f4cdf7ca9a1fdcca413157df19753f388a5a224e)
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use proc_macro2::{Span, TokenStream};
4 use quote::{format_ident, quote};
5 use 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 
14 use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
15 
16 pub(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 
26 struct This {
27     _and_token: Token![&],
28     ident: Ident,
29     _in_token: Token![in],
30 }
31 
32 struct InitializerField {
33     attrs: Vec<Attribute>,
34     kind: InitializerKind,
35 }
36 
37 enum 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 
54 impl 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 
63 enum InitializerAttribute {
64     DefaultError(DefaultErrorAttribute),
65 }
66 
67 struct DefaultErrorAttribute {
68     ty: Box<Type>,
69 }
70 
71 pub(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 
176 enum InitKind {
177     Normal,
178     Zeroing,
179 }
180 
181 fn 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`.
216 fn 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.
322 fn 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 
367 impl 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 
424 impl Parse for DefaultErrorAttribute {
425     fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
426         Ok(Self { ty: input.parse()? })
427     }
428 }
429 
430 impl 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 
440 impl 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 
450 impl 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 }
483