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 = make_field_init(&fields, pinned, false);
292 let drop_check = make_field_init(&fields, pinned, true);
293 let field_check = make_field_check(&fields, init_kind, &path);
294
295 Ok(quote_spanned! { Span::mixed_site() => {
296 let data = {
298 use ::pin_init::__internal::HasInitData;
299 #path::__init_data()
302 };
303
304 #get_pin_data
305
306 let init = data.__make_closure::<_, #error>(
308 move |slot, data_lt| {
309 #zeroable_check
310 #this
311 if true {
313 #init_fields
317 } else {
318 #drop_check
323 }
324 #field_check
325 Ok(unsafe { ::pin_init::__internal::InitOk::new() })
327 }
328 );
329 let init = move |slot| -> ::core::result::Result<(), #error> {
330 init(slot, data.__with_lt()).map(|__InitOk| ())
331 };
332 unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }
334 }})
335}
336
337enum InitKind {
338 Normal,
339 Zeroing,
340}
341
342fn get_init_kind(rest: Option<(Token![..], Expr)>, dcx: &mut DiagCtxt) -> InitKind {
343 let Some((dotdot, expr)) = rest else {
344 return InitKind::Normal;
345 };
346 match &expr {
347 Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func {
348 Expr::Path(ExprPath {
349 attrs,
350 qself: None,
351 path:
352 Path {
353 leading_colon: None,
354 segments,
355 },
356 }) if attrs.is_empty()
357 && segments.len() == 2
358 && segments[0].ident == "Zeroable"
359 && segments[0].arguments.is_none()
360 && segments[1].ident == "init_zeroed"
361 && segments[1].arguments.is_none() =>
362 {
363 return InitKind::Zeroing;
364 }
365 _ => {}
366 },
367 _ => {}
368 }
369 dcx.error(
370 dotdot.span().join(expr.span()).unwrap_or(expr.span()),
371 "expected nothing or `..Zeroable::init_zeroed()`.",
372 );
373 InitKind::Normal
374}
375
376fn make_field_init(
378 fields: &Punctuated<InitializerField, Token![,]>,
379 pinned: bool,
380 dropck: bool,
381) -> TokenStream {
382 let mut forget_guards = vec![];
383 let mut res = TokenStream::new();
384 for InitializerField { attrs, kind } in fields {
385 let cfgs = {
386 let mut cfgs = attrs.clone();
387 cfgs.retain(|attr| attr.path().is_ident("cfg"));
388 cfgs
389 };
390
391 let member = match kind {
392 InitializerKind::Value { member, .. } => member,
393 InitializerKind::Init { member, .. } => member,
394 InitializerKind::Code { block, .. } => {
395 let stmt = &block.stmts;
396 res.extend(quote! {
397 #(#attrs)*
398 {
399 #(#stmt)*
400 }
401 });
402 continue;
403 }
404 };
405 let ident = member.as_ident();
406 let span = Span::mixed_site().located_at(ident.span());
407
408 let slot = if pinned {
409 let data = if !dropck {
410 quote_spanned!(span => data_lt)
411 } else {
412 quote_spanned!(span => data.__with_lt())
413 };
414 quote_spanned! { span =>
415 (unsafe { #data.#ident(slot) })
421 }
422 } else {
423 quote_spanned! { span =>
424 (unsafe {
431 ::pin_init::__internal::Slot::<::pin_init::__internal::Unpinned, _>::new(
432 &raw mut (*slot).#member
433 )
434 })
435 }
436 };
437
438 let guard = format_ident!("__{ident}_guard", span = span);
440 let full_span = kind.span().resolved_at(Span::mixed_site());
441
442 let init = match kind {
443 InitializerKind::Value { value, .. } => {
444 let (colon_span, value) = value
445 .as_ref()
446 .map(|(colon_token, value)| (colon_token.span(), quote!(#value)))
447 .unwrap_or_else(|| (member.span(), quote!(#member)));
448
449 let write =
452 format_ident!("write", span = colon_span.resolved_at(Span::mixed_site()));
453
454 quote_spanned! { full_span =>
455 #(#attrs)*
456 let mut #guard = #slot.#write(#value);
457 }
458 }
459 InitializerKind::Init {
460 value,
461 left_arrow_token,
462 ..
463 } => {
464 let init = format_ident!(
467 "init",
468 span = left_arrow_token.span().resolved_at(Span::mixed_site())
469 );
470
471 quote_spanned! { full_span =>
472 #(#attrs)*
473 let mut #guard = #slot.#init(#value)?;
474 }
475 }
476 InitializerKind::Code { .. } => unreachable!(),
477 };
478
479 let let_binding_method = if !dropck {
482 format_ident!("let_binding", span = span)
483 } else {
484 format_ident!("let_binding_in_dropck", span = span)
485 };
486 let binding = match member {
487 Member::Named(ident) => quote_spanned! { span =>
488 #(#cfgs)*
489 #[allow(unused_variables, non_snake_case)]
492 let mut #ident = #guard.#let_binding_method();
494 },
495 Member::Unnamed(_) => quote!(),
496 };
497
498 res.extend(quote! {
499 #init
500
501 #binding
502 });
503
504 forget_guards.push(quote_spanned! { span =>
505 #(#cfgs)*
506 ::core::mem::forget(#guard);
507 });
508 }
509
510 forget_guards.reverse();
511
512 quote! {
513 #res
514 #(#forget_guards)*
517 }
518}
519
520fn make_field_check(
522 fields: &Punctuated<InitializerField, Token![,]>,
523 init_kind: InitKind,
524 path: &Path,
525) -> TokenStream {
526 let align_checks: TokenStream = fields
527 .iter()
528 .filter_map(|f| {
529 let member = f.kind.member()?;
530 let span = member.span().resolved_at(Span::mixed_site());
531 let attrs = &f.attrs;
532
533 Some(quote_spanned! {span =>
534 #(#attrs)*
539 let _ = &(*slot).#member;
540 })
541 })
542 .collect();
543
544 let fake_field_init: TokenStream = fields
545 .iter()
546 .filter_map(|f| {
547 let member = f.kind.member()?;
548 let span = member.span().resolved_at(Span::mixed_site());
549 let attrs = &f.attrs;
550
551 Some(quote_spanned! {span =>
552 #(#attrs)*
553 #member: loop {},
554 })
555 })
556 .collect();
557 let zeroing_trailer = match init_kind {
558 InitKind::Normal => None,
559 InitKind::Zeroing => Some(quote! {
560 ..::core::mem::zeroed()
561 }),
562 };
563 let field_dup_checks = quote_spanned! { Span::mixed_site() =>
564 ::core::ptr::write(slot, #path {
569 #fake_field_init
570 #zeroing_trailer
571 })
572 };
573
574 quote_spanned! { Span::mixed_site() =>
575 #[allow(unreachable_code)]
576 let _ = || unsafe {
579 #align_checks
580 #field_dup_checks
581 };
582 }
583}
584
585impl InitExprStruct {
586 fn parse_with_path(path: Path, input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
587 let content;
588 let brace_token = braced!(content in input);
589 let mut fields = Punctuated::new();
590 loop {
591 let lh = content.lookahead1();
592 if lh.peek(End) || lh.peek(Token![..]) {
593 break;
594 } else if lh.peek(Ident) || lh.peek(LitInt) || lh.peek(Token![_]) || lh.peek(Token![#])
595 {
596 fields.push_value(content.parse()?);
597 let lh = content.lookahead1();
598 if lh.peek(End) {
599 break;
600 } else if lh.peek(Token![,]) {
601 fields.push_punct(content.parse()?);
602 } else {
603 return Err(lh.error());
604 }
605 } else {
606 return Err(lh.error());
607 }
608 }
609 let rest = content
610 .peek(Token![..])
611 .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?)))
612 .transpose()?;
613 Ok(Self {
614 path,
615 brace_token,
616 fields,
617 rest,
618 })
619 }
620}
621
622impl InitExprTuple {
623 fn parse_with_path(path: Path, input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
624 let content;
625 let paren_token = parenthesized!(content in input);
626 let mut fields = Punctuated::new();
627 while !content.is_empty() {
628 let attrs = content.call(Attribute::parse_outer)?;
629
630 if let Some(left_arrow_token) = content.parse::<Option<Token![<-]>>()? {
631 DiagCtxt::current(|dcx| {
632 dcx.error(
633 left_arrow_token,
634 "`<-` is not supported in tuple constructor syntax; name the fields by \
635 index instead, e.g. `Type { 0 <- initializer, 1: value }`",
636 )
637 });
638 }
639
640 fields.push_value(InitTupleField {
641 attrs,
642 value: content.parse()?,
643 });
644 if content.is_empty() {
645 break;
646 }
647 fields.push_punct(content.parse()?);
648 }
649 Ok(InitExprTuple {
650 path,
651 paren_token,
652 fields,
653 })
654 }
655}
656
657impl Parse for Initializer {
658 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
659 let attrs = input.call(Attribute::parse_outer)?;
660 let this = input.peek(Token![&]).then(|| input.parse()).transpose()?;
661 let path = input.parse()?;
662 let kind = if input.peek(token::Brace) {
663 InitExprKind::Struct(InitExprStruct::parse_with_path(path, input)?)
664 } else if input.peek(token::Paren) {
665 InitExprKind::Tuple(InitExprTuple::parse_with_path(path, input)?)
666 } else {
667 return Err(input.error("expected curly braces or parentheses"));
668 };
669 let error = input
670 .peek(Token![?])
671 .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?)))
672 .transpose()?;
673 let attrs = attrs
674 .into_iter()
675 .map(|a| {
676 if a.path().is_ident("default_error") {
677 a.parse_args::<DefaultErrorAttribute>()
678 .map(InitializerAttribute::DefaultError)
679 } else {
680 Err(syn::Error::new_spanned(a, "unknown initializer attribute"))
681 }
682 })
683 .collect::<Result<Vec<_>, _>>()?;
684 Ok(Self {
685 attrs,
686 this,
687 kind,
688 error,
689 })
690 }
691}
692
693impl Parse for DefaultErrorAttribute {
694 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
695 Ok(Self { ty: input.parse()? })
696 }
697}
698
699impl Parse for This {
700 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
701 Ok(Self {
702 _and_token: input.parse()?,
703 ident: input.parse()?,
704 _in_token: input.parse()?,
705 })
706 }
707}
708
709impl Parse for InitializerField {
710 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
711 let attrs = input.call(Attribute::parse_outer)?;
712 Ok(Self {
713 attrs,
714 kind: input.parse()?,
715 })
716 }
717}
718
719impl Parse for InitializerKind {
720 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
721 let lh = input.lookahead1();
722 let member = if lh.peek(Token![_]) {
723 return Ok(Self::Code {
724 _underscore_token: input.parse()?,
725 _colon_token: input.parse()?,
726 block: input.parse()?,
727 });
728 } else if lh.peek(Ident) || lh.peek(LitInt) {
729 input.parse::<Member>()?
730 } else {
731 return Err(lh.error());
732 };
733
734 let lh = input.lookahead1();
735 if lh.peek(Token![<-]) {
736 Ok(Self::Init {
737 member,
738 left_arrow_token: input.parse()?,
739 value: input.parse()?,
740 })
741 } else if lh.peek(Token![:]) {
742 Ok(Self::Value {
743 member,
744 value: Some((input.parse()?, input.parse()?)),
745 })
746 } else if matches!(member, Member::Named(_)) && (lh.peek(Token![,]) || lh.peek(End)) {
747 Ok(Self::Value {
749 member,
750 value: None,
751 })
752 } else {
753 Err(lh.error())
754 }
755 }
756}
757
758impl<Kind: ToTokens> ToTokens for Initializer<Kind> {
759 fn to_tokens(&self, tokens: &mut TokenStream) {
760 let Self {
761 attrs,
762 this,
763 kind,
764 error,
765 } = self;
766 tokens.append_all(attrs);
767 this.to_tokens(tokens);
768 kind.to_tokens(tokens);
769 if let Some((question, ty)) = error {
770 question.to_tokens(tokens);
771 ty.to_tokens(tokens);
772 }
773 }
774}
775
776impl ToTokens for InitExprKind {
777 fn to_tokens(&self, tokens: &mut TokenStream) {
778 match self {
779 Self::Struct(init) => init.to_tokens(tokens),
780 Self::Tuple(init) => init.to_tokens(tokens),
781 }
782 }
783}
784
785impl ToTokens for InitExprStruct {
786 fn to_tokens(&self, tokens: &mut TokenStream) {
787 let Self {
788 path,
789 brace_token,
790 fields,
791 rest,
792 } = self;
793 path.to_tokens(tokens);
794 brace_token.surround(tokens, |tokens| {
795 fields.to_tokens(tokens);
796 if let Some((dotdot, expr)) = rest {
797 dotdot.to_tokens(tokens);
798 expr.to_tokens(tokens);
799 }
800 });
801 }
802}
803
804impl ToTokens for InitExprTuple {
805 fn to_tokens(&self, tokens: &mut TokenStream) {
806 let Self {
807 path,
808 paren_token,
809 fields,
810 } = self;
811 path.to_tokens(tokens);
812 paren_token.surround(tokens, |tokens| fields.to_tokens(tokens));
813 }
814}
815
816impl ToTokens for InitTupleField {
817 fn to_tokens(&self, tokens: &mut TokenStream) {
818 let Self { attrs, value } = self;
819 tokens.append_all(attrs);
820 value.to_tokens(tokens);
821 }
822}
823
824impl ToTokens for InitializerAttribute {
825 fn to_tokens(&self, tokens: &mut TokenStream) {
826 match self {
827 Self::DefaultError(DefaultErrorAttribute { ty }) => {
828 quote!(#[default_error(#ty)]).to_tokens(tokens);
829 }
830 }
831 }
832}
833
834impl ToTokens for This {
835 fn to_tokens(&self, tokens: &mut TokenStream) {
836 let Self {
837 _and_token,
838 ident,
839 _in_token,
840 } = self;
841 _and_token.to_tokens(tokens);
842 ident.to_tokens(tokens);
843 _in_token.to_tokens(tokens);
844 }
845}
846
847impl ToTokens for InitializerField {
848 fn to_tokens(&self, tokens: &mut TokenStream) {
849 let Self { attrs, kind } = self;
850 tokens.append_all(attrs);
851 kind.to_tokens(tokens);
852 }
853}
854
855impl ToTokens for InitializerKind {
856 fn to_tokens(&self, tokens: &mut TokenStream) {
857 match self {
858 Self::Value { member, value } => {
859 member.to_tokens(tokens);
860 if let Some((colon, expr)) = value {
861 colon.to_tokens(tokens);
862 expr.to_tokens(tokens);
863 }
864 }
865 Self::Init {
866 member,
867 left_arrow_token,
868 value,
869 } => {
870 member.to_tokens(tokens);
871 left_arrow_token.to_tokens(tokens);
872 value.to_tokens(tokens);
873 }
874 Self::Code {
875 _underscore_token,
876 _colon_token,
877 block,
878 } => {
879 _underscore_token.to_tokens(tokens);
880 _colon_token.to_tokens(tokens);
881 block.to_tokens(tokens);
882 }
883 }
884 }
885}