1use 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 fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
128 where T: ::pin_init::Zeroable
129 {}
130 assert_zeroable(#slot);
132 unsafe { ::core::ptr::write_bytes(#slot, 0, 1) };
134 },
135 };
136 let this = match this {
137 None => quote!(),
138 Some(This { ident, .. }) => quote! {
139 let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
142 },
143 };
144 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 let #data = unsafe {
152 use ::pin_init::__internal::#has_data_trait;
153 #path::#get_data()
156 };
157 let init = #data.__make_closure::<_, #error>(
159 move |slot| {
160 #zeroable_check
161 #this
162 #init_fields
163 #field_check
164 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 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
215fn 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 (unsafe { #data.#ident(#slot) })
255 }
256 } else {
257 quote! {
258 (unsafe {
265 ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
266 &raw mut (*#slot).#ident
267 )
268 })
269 }
270 };
271
272 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(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 #(
315 #(#guard_attrs)*
316 ::core::mem::forget(#guards);
317 )*
318 }
319}
320
321fn 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 let _ = || unsafe {
343 #(
348 #(#field_attrs)*
349 let _ = &(*slot).#field_name;
350 )*
351
352 ::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}