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