1use proc_macro2::{Span, TokenStream};
4use quote::{format_ident, quote, quote_spanned, ToTokens, TokenStreamExt};
5use syn::{
6 braced, parenthesized,
7 parse::{End, Parse},
8 parse_quote,
9 punctuated::{Pair, Punctuated},
10 spanned::Spanned,
11 token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Index, LitInt, Member, Path, Token,
12 Type,
13};
14
15use crate::{
16 diagnostics::{DiagCtxt, ErrorGuaranteed},
17 util::*,
18};
19
20pub(crate) struct Initializer<Kind = InitExprKind> {
21 attrs: Vec<InitializerAttribute>,
22 this: Option<This>,
23 kind: Kind,
24 error: Option<(Token![?], Type)>,
25}
26
27pub(crate) struct InitExprStruct {
28 path: Path,
29 brace_token: token::Brace,
30 fields: Punctuated<InitializerField, Token![,]>,
31 rest: Option<(Token![..], Expr)>,
32}
33
34pub(crate) struct InitExprTuple {
35 path: Path,
36 paren_token: token::Paren,
37 fields: Punctuated<InitTupleField, Token![,]>,
38}
39
40pub(crate) enum InitExprKind {
41 Struct(InitExprStruct),
42 Tuple(InitExprTuple),
43}
44
45struct InitTupleField {
46 attrs: Vec<Attribute>,
47 left_arrow_token: Option<Token![<-]>,
50 value: Expr,
51}
52
53impl InitExprTuple {
54 fn normalize(self) -> InitExprStruct {
55 let InitExprTuple {
56 path,
57 paren_token,
58 fields,
59 } = self;
60 InitExprStruct {
61 path,
62 brace_token: token::Brace {
63 span: paren_token.span,
64 },
65 fields: fields
66 .into_pairs()
67 .enumerate()
68 .map(|(index, pair)| {
69 let (field, comma) = pair.into_tuple();
70 let span = field.value.span();
71 let field = InitializerField {
72 attrs: field.attrs,
73 kind: InitializerKind::Value {
74 member: Member::Unnamed(Index {
75 index: index.try_into().unwrap(),
76 span,
77 }),
78 value: Some((Token, field.value)),
79 },
80 };
81 Pair::new(field, comma)
82 })
83 .collect(),
84 rest: None,
85 }
86 }
87
88 fn validate(&self, dcx: &mut DiagCtxt) -> Result<(), ErrorGuaranteed> {
89 let mut result = Ok(());
90 for field in &self.fields {
91 if let Some(left_arrow_token) = &field.left_arrow_token {
92 result = Err(dcx.error(
93 left_arrow_token,
94 "`<-` is not supported in tuple constructor syntax; name the fields by index \
95 instead, e.g. `Type { 0 <- initializer, 1: value }`",
96 ));
97 }
98 }
99 result
100 }
101}
102
103struct This {
104 _and_token: Token![&],
105 ident: Ident,
106 _in_token: Token![in],
107}
108
109struct InitializerField {
110 attrs: Vec<Attribute>,
111 kind: InitializerKind,
112}
113
114enum InitializerKind {
115 Value {
116 member: Member,
117 value: Option<(Token![:], Expr)>,
118 },
119 Init {
120 member: Member,
121 _left_arrow_token: Token![<-],
122 value: Expr,
123 },
124 Code {
125 _underscore_token: Token![_],
126 _colon_token: Token![:],
127 block: Block,
128 },
129}
130
131impl InitializerKind {
132 fn member(&self) -> Option<&Member> {
133 match self {
134 Self::Value { member, .. } | Self::Init { member, .. } => Some(member),
135 Self::Code { .. } => None,
136 }
137 }
138}
139
140enum InitializerAttribute {
141 DefaultError(DefaultErrorAttribute),
142}
143
144struct DefaultErrorAttribute {
145 ty: Box<Type>,
146}
147
148pub(crate) fn expand_with_cfg(
149 initializer: Initializer,
150 default_error: Option<&'static str>,
151 pinned: bool,
152 dcx: &mut DiagCtxt,
153) -> Result<TokenStream, ErrorGuaranteed> {
154 let initializer = match initializer.kind {
155 InitExprKind::Tuple(expr) => {
156 expr.validate(dcx)?;
157
158 let mut initializer = Initializer {
159 attrs: initializer.attrs,
160 this: initializer.this,
161 kind: expr,
162 error: initializer.error,
163 };
164
165 for (field_idx, field) in initializer.kind.fields.iter_mut().enumerate() {
171 let cfg = field.attrs.extract_cfg_attrs();
172
173 if cfg.is_empty() {
174 continue;
175 }
176
177 let true_initializer = initializer.to_token_stream();
178 initializer.kind.fields = initializer
179 .kind
180 .fields
181 .into_pairs()
182 .enumerate()
183 .filter(|&(index, _)| index != field_idx)
184 .map(|(_, pair)| pair)
185 .collect();
186
187 let false_initializer = &initializer;
188
189 let macro_name = if pinned {
190 quote!(::pin_init::pin_init)
191 } else {
192 quote!(::pin_init::init)
193 };
194
195 return Ok(quote! {
200 {
201 #[cfg(all(#(#cfg,)*))]
204 #macro_name! { #true_initializer }
205
206 #[cfg(not(all(#(#cfg,)*)))]
207 #macro_name! { #false_initializer }
208 }
209 });
210 }
211
212 Initializer {
214 attrs: initializer.attrs,
215 this: initializer.this,
216 kind: initializer.kind.normalize(),
217 error: initializer.error,
218 }
219 }
220
221 InitExprKind::Struct(expr) => Initializer {
222 attrs: initializer.attrs,
223 this: initializer.this,
224 kind: expr,
225 error: initializer.error,
226 },
227 };
228
229 expand(initializer, default_error, pinned, dcx)
230}
231
232fn expand(
233 Initializer {
234 attrs,
235 this,
236 kind:
237 InitExprStruct {
238 path,
239 brace_token,
240 fields,
241 rest,
242 },
243 error,
244 }: Initializer<InitExprStruct>,
245 default_error: Option<&'static str>,
246 pinned: bool,
247 dcx: &mut DiagCtxt,
248) -> Result<TokenStream, ErrorGuaranteed> {
249 let error = error.map_or_else(
250 || {
251 if let Some(default_error) = attrs.iter().fold(None, |acc, attr| {
252 #[expect(irrefutable_let_patterns)]
253 if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr {
254 Some(ty.clone())
255 } else {
256 acc
257 }
258 }) {
259 default_error
260 } else if let Some(default_error) = default_error {
261 syn::parse_str(default_error).unwrap()
262 } else {
263 dcx.error(
264 brace_token.span.close(),
265 "expected `? <type>` after initializer",
266 );
267 parse_quote!(::core::convert::Infallible)
268 }
269 },
270 |(_, err)| Box::new(err),
271 );
272 let (has_data_trait, get_data, init_from_closure) = if pinned {
273 (
274 format_ident!("HasPinData"),
275 format_ident!("__pin_data"),
276 format_ident!("pin_init_from_closure"),
277 )
278 } else {
279 (
280 format_ident!("HasInitData"),
281 format_ident!("__init_data"),
282 format_ident!("init_from_closure"),
283 )
284 };
285 let init_kind = get_init_kind(rest, dcx);
286 let zeroable_check = match init_kind {
287 InitKind::Normal => quote!(),
288 InitKind::Zeroing => quote_spanned! { Span::mixed_site() =>
289 fn assert_zeroable<T: ?::core::marker::Sized>(_: *mut T)
294 where T: ::pin_init::Zeroable
295 {}
296 assert_zeroable(slot);
298 unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
300 },
301 };
302 let this = match this {
303 None => quote!(),
304 Some(This { ident, .. }) => quote_spanned! { Span::mixed_site() =>
305 let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };
308 },
309 };
310 let init_fields = init_fields(&fields, pinned);
312 let field_check = make_field_check(&fields, init_kind, &path);
313 Ok(quote_spanned! { Span::mixed_site() => {
314 let data = unsafe {
317 use ::pin_init::__internal::#has_data_trait;
318 #path::#get_data()
321 };
322 let init = data.__make_closure::<_, #error>(
324 move |slot| {
325 #zeroable_check
326 #this
327 #init_fields
328 #field_check
329 Ok(unsafe { ::pin_init::__internal::InitOk::new() })
331 }
332 );
333 let init = move |slot| -> ::core::result::Result<(), #error> {
334 init(slot).map(|__InitOk| ())
335 };
336 unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }
338 }})
339}
340
341enum InitKind {
342 Normal,
343 Zeroing,
344}
345
346fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKind {
347 let Some((dotdot, expr)) = rest else {
348 return InitKind::Normal;
349 };
350 match &expr {
351 Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func {
352 Expr::Path(ExprPath {
353 attrs,
354 qself: None,
355 path:
356 Path {
357 leading_colon: None,
358 segments,
359 },
360 }) if attrs.is_empty()
361 && segments.len() == 2
362 && segments[0].ident == "Zeroable"
363 && segments[0].arguments.is_none()
364 && segments[1].ident == "init_zeroed"
365 && segments[1].arguments.is_none() =>
366 {
367 return InitKind::Zeroing;
368 }
369 _ => {}
370 },
371 _ => {}
372 }
373 dcx.error(
374 dotdot.span().join(expr.span()).unwrap_or(expr.span()),
375 "expected nothing or `..Zeroable::init_zeroed()`.",
376 );
377 InitKind::Normal
378}
379
380fn init_fields(fields: &Punctuated<InitializerField, Token![,]>, pinned: bool) -> TokenStream {
382 let mut guards = vec![];
383 let mut guard_attrs = vec![];
384 let mut res = TokenStream::new();
385 for InitializerField { attrs, kind } in fields {
386 let cfgs = {
387 let mut cfgs = attrs.clone();
388 cfgs.retain(|attr| attr.path().is_ident("cfg"));
389 cfgs
390 };
391
392 let member = match kind {
393 InitializerKind::Value { member, .. } => member,
394 InitializerKind::Init { member, .. } => member,
395 InitializerKind::Code { block, .. } => {
396 let stmt = &block.stmts;
397 res.extend(quote! {
398 #(#attrs)*
399 {
400 #(#stmt)*
401 }
402 });
403 continue;
404 }
405 };
406 let ident = member.as_ident();
407 let span = Span::mixed_site().located_at(ident.span());
408
409 let slot = if pinned {
410 quote_spanned! { span =>
411 (unsafe { data.#ident(slot) })
417 }
418 } else {
419 quote_spanned! { span =>
420 (unsafe {
427 ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
428 &raw mut (*slot).#member
429 )
430 })
431 }
432 };
433
434 let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
436 let full_span = kind.span();
437
438 let init = match kind {
439 InitializerKind::Value { value, .. } => {
440 let value = value
441 .as_ref()
442 .map(|(_, value)| quote!(#value))
443 .unwrap_or_else(|| quote!(#member));
444
445 quote_spanned! { full_span =>
446 #(#attrs)*
447 let mut #guard = #slot.write(#value);
448 }
449 }
450 InitializerKind::Init { value, .. } => {
451 quote_spanned! { full_span =>
452 #(#attrs)*
453 let mut #guard = #slot.init(#value)?;
454 }
455 }
456 InitializerKind::Code { .. } => unreachable!(),
457 };
458
459 let binding = match member {
462 Member::Named(ident) => quote_spanned! { span =>
463 #(#cfgs)*
464 #[allow(unused_variables, non_snake_case)]
467 let #ident = #guard.let_binding();
468 },
469 Member::Unnamed(_) => quote!(),
470 };
471
472 res.extend(quote! {
473 #init
474
475 #binding
476 });
477
478 guards.push(guard);
479 guard_attrs.push(cfgs);
480 }
481 quote! {
482 #res
483 #(
486 #(#guard_attrs)*
487 ::core::mem::forget(#guards);
488 )*
489 }
490}
491
492fn make_field_check(
494 fields: &Punctuated<InitializerField, Token![,]>,
495 init_kind: InitKind,
496 path: &Path,
497) -> TokenStream {
498 let field_attrs: Vec<_> = fields
499 .iter()
500 .filter_map(|f| f.kind.member().map(|_| &f.attrs))
501 .collect();
502 let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.member()).collect();
503 let zeroing_trailer = match init_kind {
504 InitKind::Normal => None,
505 InitKind::Zeroing => Some(quote! {
506 ..::core::mem::zeroed()
507 }),
508 };
509 quote_spanned! { Span::mixed_site() =>
510 #[allow(unreachable_code)]
511 let _ = || unsafe {
514 #(
519 #(#field_attrs)*
520 let _ = &(*slot).#field_name;
521 )*
522
523 ::core::ptr::write(slot, #path {
528 #(
529 #(#field_attrs)*
530 #field_name: loop {},
531 )*
532 #zeroing_trailer
533 })
534 };
535 }
536}
537
538impl InitExprStruct {
539 fn parse_with_path(path: Path, input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
540 let content;
541 let brace_token = braced!(content in input);
542 let mut fields = Punctuated::new();
543 loop {
544 let lh = content.lookahead1();
545 if lh.peek(End) || lh.peek(Token![..]) {
546 break;
547 } else if lh.peek(Ident) || lh.peek(LitInt) || lh.peek(Token![_]) || lh.peek(Token![#])
548 {
549 fields.push_value(content.parse()?);
550 let lh = content.lookahead1();
551 if lh.peek(End) {
552 break;
553 } else if lh.peek(Token![,]) {
554 fields.push_punct(content.parse()?);
555 } else {
556 return Err(lh.error());
557 }
558 } else {
559 return Err(lh.error());
560 }
561 }
562 let rest = content
563 .peek(Token![..])
564 .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?)))
565 .transpose()?;
566 Ok(Self {
567 path,
568 brace_token,
569 fields,
570 rest,
571 })
572 }
573}
574
575impl InitExprTuple {
576 fn parse_with_path(path: Path, input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
577 let content;
578 let paren_token = parenthesized!(content in input);
579 let mut fields = Punctuated::new();
580 while !content.is_empty() {
581 fields.push_value(InitTupleField {
582 attrs: content.call(Attribute::parse_outer)?,
583 left_arrow_token: content.parse()?,
584 value: content.parse()?,
585 });
586 if content.is_empty() {
587 break;
588 }
589 fields.push_punct(content.parse()?);
590 }
591 Ok(InitExprTuple {
592 path,
593 paren_token,
594 fields,
595 })
596 }
597}
598
599impl Parse for Initializer {
600 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
601 let attrs = input.call(Attribute::parse_outer)?;
602 let this = input.peek(Token![&]).then(|| input.parse()).transpose()?;
603 let path = input.parse()?;
604 let kind = if input.peek(token::Brace) {
605 InitExprKind::Struct(InitExprStruct::parse_with_path(path, input)?)
606 } else if input.peek(token::Paren) {
607 InitExprKind::Tuple(InitExprTuple::parse_with_path(path, input)?)
608 } else {
609 return Err(input.error("expected curly braces or parentheses"));
610 };
611 let error = input
612 .peek(Token![?])
613 .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
614 .transpose()?;
615 let attrs = attrs
616 .into_iter()
617 .map(|a| {
618 if a.path().is_ident("default_error") {
619 a.parse_args::<DefaultErrorAttribute>()
620 .map(InitializerAttribute::DefaultError)
621 } else {
622 Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
623 }
624 })
625 .collect::<Result<Vec<_>, _>>()?;
626 Ok(Self {
627 attrs,
628 this,
629 kind,
630 error,
631 })
632 }
633}
634
635impl Parse for DefaultErrorAttribute {
636 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
637 Ok(Self { ty: input.parse()? })
638 }
639}
640
641impl Parse for This {
642 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
643 Ok(Self {
644 _and_token: input.parse()?,
645 ident: input.parse()?,
646 _in_token: input.parse()?,
647 })
648 }
649}
650
651impl Parse for InitializerField {
652 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
653 let attrs = input.call(Attribute::parse_outer)?;
654 Ok(Self {
655 attrs,
656 kind: input.parse()?,
657 })
658 }
659}
660
661impl Parse for InitializerKind {
662 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
663 let lh = input.lookahead1();
664 let member = if lh.peek(Token![_]) {
665 return Ok(Self::Code {
666 _underscore_token: input.parse()?,
667 _colon_token: input.parse()?,
668 block: input.parse()?,
669 });
670 } else if lh.peek(Ident) || lh.peek(LitInt) {
671 input.parse::<Member>()?
672 } else {
673 return Err(lh.error());
674 };
675
676 let lh = input.lookahead1();
677 if lh.peek(Token![<-]) {
678 Ok(Self::Init {
679 member,
680 _left_arrow_token: input.parse()?,
681 value: input.parse()?,
682 })
683 } else if lh.peek(Token![:]) {
684 Ok(Self::Value {
685 member,
686 value: Some((input.parse()?, input.parse()?)),
687 })
688 } else if matches!(member, Member::Named(_)) && (lh.peek(Token![,]) || lh.peek(End)) {
689 Ok(Self::Value {
691 member,
692 value: None,
693 })
694 } else {
695 Err(lh.error())
696 }
697 }
698}
699
700impl<Kind: ToTokens> ToTokens for Initializer<Kind> {
701 fn to_tokens(&self, tokens: &mut TokenStream) {
702 let Self {
703 attrs,
704 this,
705 kind,
706 error,
707 } = self;
708 tokens.append_all(attrs);
709 this.to_tokens(tokens);
710 kind.to_tokens(tokens);
711 if let Some((question, ty)) = error {
712 question.to_tokens(tokens);
713 ty.to_tokens(tokens);
714 }
715 }
716}
717
718impl ToTokens for InitExprKind {
719 fn to_tokens(&self, tokens: &mut TokenStream) {
720 match self {
721 Self::Struct(init) => init.to_tokens(tokens),
722 Self::Tuple(init) => init.to_tokens(tokens),
723 }
724 }
725}
726
727impl ToTokens for InitExprStruct {
728 fn to_tokens(&self, tokens: &mut TokenStream) {
729 let Self {
730 path,
731 brace_token,
732 fields,
733 rest,
734 } = self;
735 path.to_tokens(tokens);
736 brace_token.surround(tokens, |tokens| {
737 fields.to_tokens(tokens);
738 if let Some((dotdot, expr)) = rest {
739 dotdot.to_tokens(tokens);
740 expr.to_tokens(tokens);
741 }
742 });
743 }
744}
745
746impl ToTokens for InitExprTuple {
747 fn to_tokens(&self, tokens: &mut TokenStream) {
748 let Self {
749 path,
750 paren_token,
751 fields,
752 } = self;
753 path.to_tokens(tokens);
754 paren_token.surround(tokens, |tokens| fields.to_tokens(tokens));
755 }
756}
757
758impl ToTokens for InitTupleField {
759 fn to_tokens(&self, tokens: &mut TokenStream) {
760 let Self {
761 attrs,
762 left_arrow_token,
763 value,
764 } = self;
765 tokens.append_all(attrs);
766 left_arrow_token.to_tokens(tokens);
767 value.to_tokens(tokens);
768 }
769}
770
771impl ToTokens for InitializerAttribute {
772 fn to_tokens(&self, tokens: &mut TokenStream) {
773 match self {
774 Self::DefaultError(DefaultErrorAttribute { ty }) => {
775 quote!(#[default_error(#ty)]).to_tokens(tokens);
776 }
777 }
778 }
779}
780
781impl ToTokens for This {
782 fn to_tokens(&self, tokens: &mut TokenStream) {
783 let Self {
784 _and_token,
785 ident,
786 _in_token,
787 } = self;
788 _and_token.to_tokens(tokens);
789 ident.to_tokens(tokens);
790 _in_token.to_tokens(tokens);
791 }
792}
793
794impl ToTokens for InitializerField {
795 fn to_tokens(&self, tokens: &mut TokenStream) {
796 let Self { attrs, kind } = self;
797 tokens.append_all(attrs);
798 kind.to_tokens(tokens);
799 }
800}
801
802impl ToTokens for InitializerKind {
803 fn to_tokens(&self, tokens: &mut TokenStream) {
804 match self {
805 Self::Value { member, value } => {
806 member.to_tokens(tokens);
807 if let Some((colon, expr)) = value {
808 colon.to_tokens(tokens);
809 expr.to_tokens(tokens);
810 }
811 }
812 Self::Init {
813 member,
814 _left_arrow_token,
815 value,
816 } => {
817 member.to_tokens(tokens);
818 _left_arrow_token.to_tokens(tokens);
819 value.to_tokens(tokens);
820 }
821 Self::Code {
822 _underscore_token,
823 _colon_token,
824 block,
825 } => {
826 _underscore_token.to_tokens(tokens);
827 _colon_token.to_tokens(tokens);
828 block.to_tokens(tokens);
829 }
830 }
831 }
832}