1use std::collections::HashMap;
2
3use core_extensions::SelfOps;
4
5use syn::{visit_mut::VisitMut, Ident};
6
7use quote::{quote, ToTokens};
8
9use proc_macro2::{Span, TokenStream as TokenStream2};
10
11use as_derive_utils::{
12 datastructure::DataStructure,
13 gen_params_in::{GenParamsIn, InWhat},
14 return_spanned_err, spanned_err,
15 to_token_fn::ToTokenFnMut,
16};
17
18use super::{
19 attribute_parsing::{StabilityKind, StableAbiOptions},
20 common_tokens::CommonTokens,
21 StartLen,
22};
23
24use crate::{
25 arenas::{AllocMethods, Arenas},
26 impl_interfacetype::{private_associated_type, UsableTrait, TRAIT_LIST},
27 literals_constructors::rstr_tokenizer,
28 parse_utils::{parse_str_as_ident, parse_str_as_path},
29 set_span_visitor::SetSpanVisitor,
30 utils::{LinearResult, SynResultExt},
31};
32
33#[derive(Clone, Default)]
35pub(crate) struct UncheckedNonExhaustive<'a> {
36 pub(crate) alignment: Option<ExprOrType<'a>>,
37 pub(crate) size: Option<ExprOrType<'a>>,
38 pub(crate) enum_interface: Option<EnumInterface<'a>>,
39 pub(crate) assert_nonexh: Vec<&'a syn::Type>,
40}
41
42#[derive(Clone)]
44pub(crate) struct NonExhaustive<'a> {
45 pub(crate) nonexhaustive_alias: &'a Ident,
46 pub(crate) nonexhaustive_marker: &'a Ident,
48 pub(crate) enum_storage: &'a Ident,
50 pub(crate) alignment: ExprOrType<'a>,
52 pub(crate) size: ExprOrType<'a>,
54 pub(crate) enum_interface: Option<EnumInterface<'a>>,
61 pub(crate) new_interface: Option<&'a Ident>,
63 pub(crate) default_interface: TokenStream2,
65 pub(crate) assert_nonexh: Vec<&'a syn::Type>,
67 pub(crate) bounds_trait: Option<BoundsTrait<'a>>,
72 pub(crate) ne_variants: Vec<NEVariant<'a>>,
75}
76
77#[derive(Clone)]
80pub struct BoundsTrait<'a> {
81 ident: &'a Ident,
82 bounds: Vec<&'a syn::Path>,
83}
84
85#[derive(Clone)]
86pub struct UncheckedNEVariant {
87 pub(crate) constructor: Option<UncheckedVariantConstructor>,
88 pub(crate) is_hidden: bool,
89}
90
91#[derive(Clone)]
92pub struct NEVariant<'a> {
93 pub(crate) constructor: Option<VariantConstructor<'a>>,
94 pub(crate) is_hidden: bool,
95}
96
97#[derive(Clone)]
99pub enum UncheckedVariantConstructor {
100 Regular,
103 Boxed,
106}
107
108#[derive(Clone)]
110pub enum VariantConstructor<'a> {
111 Regular,
114 Boxed {
120 referent: Option<&'a syn::Type>,
121 pointer: &'a syn::Type,
122 },
123}
124
125#[derive(Clone)]
127pub(crate) enum EnumInterface<'a> {
128 New(NewEnumInterface<'a>),
129 Old(&'a syn::Type),
130}
131
132#[derive(Default, Clone)]
136pub(crate) struct NewEnumInterface<'a> {
137 pub(crate) impld: Vec<&'a Ident>,
138 pub(crate) unimpld: Vec<&'a Ident>,
139}
140
141impl<'a> NonExhaustive<'a> {
142 pub fn new(
143 mut unchecked: UncheckedNonExhaustive<'a>,
144 ne_variants: Vec<UncheckedNEVariant>,
145 ds: &'a DataStructure<'a>,
146 arenas: &'a Arenas,
147 ) -> Result<Self, syn::Error> {
148 let name = ds.name;
149
150 let alignment = unchecked.alignment.unwrap_or(ExprOrType::Usize);
151
152 let parse_ident = move |s: &str, span: Option<Span>| -> &'a Ident {
153 let mut ident = parse_str_as_ident(s);
154 if let Some(span) = span {
155 ident.set_span(span)
156 }
157 arenas.alloc(ident)
158 };
159
160 let mut errors = LinearResult::ok(());
161
162 let size = unchecked.size.unwrap_or_else(|| {
163 errors.push_err(spanned_err!(
164 name,
165 "\n\
166 You must specify the size of the enum storage in NonExhaustive<> using \
167 the `size=integer literal` or `size=\"type\"` argument inside of \
168 the `#[sabi(kind(WithNonExhaustive(...)))]` helper attribute.\n\
169 "
170 ));
171 ExprOrType::Int(0)
172 });
173
174 let mut bounds_trait = None::<BoundsTrait<'a>>;
175
176 if let Some(EnumInterface::New(enum_interface)) = &mut unchecked.enum_interface {
177 let mut trait_map = TRAIT_LIST
178 .iter()
179 .map(|x| (parse_ident(x.name, None), x))
180 .collect::<HashMap<&'a syn::Ident, &'static UsableTrait>>();
181
182 let mut bounds_trait_inner = Vec::<&'a syn::Path>::new();
183
184 for &trait_ in &enum_interface.impld {
185 match trait_map.remove(trait_) {
186 Some(ut) => {
187 use crate::impl_interfacetype::WhichTrait as WT;
188 if let WT::Deserialize = ut.which_trait {
189 continue;
190 }
191 let mut full_path = parse_str_as_path(ut.full_path)?;
192
193 SetSpanVisitor::new(trait_.span()).visit_path_mut(&mut full_path);
194
195 bounds_trait_inner.push(arenas.alloc(full_path));
196 }
197 None => {
198 panic!("Trait {} was not in TRAIT_LIST.", trait_)
200 }
201 }
202 }
203
204 bounds_trait = Some(BoundsTrait {
205 ident: parse_ident(&format!("{}_Bounds", name), None),
206 bounds: bounds_trait_inner,
207 });
208
209 for &trait_ in &enum_interface.unimpld {
210 assert!(
212 trait_map.remove(trait_).is_some(),
213 "Trait {} was not in TRAIT_LIST.",
214 trait_
215 );
216 }
217
218 for (trait_, _) in trait_map {
219 enum_interface.unimpld.push(trait_);
220 }
221 }
222
223 let (default_interface, new_interface) = match unchecked.enum_interface {
224 Some(EnumInterface::New { .. }) => {
225 let name = parse_ident(&format!("{}_Interface", name), None);
226 (name.into_token_stream(), Some(name))
227 }
228 Some(EnumInterface::Old(ty)) => ((&ty).into_token_stream(), None),
229 None => (quote!(()), None),
230 };
231
232 let ne_variants = ne_variants
233 .into_iter()
234 .zip(&ds.variants)
235 .map(|(vc, variant)| {
236 let constructor = match vc.constructor {
237 Some(UncheckedVariantConstructor::Regular) => Some(VariantConstructor::Regular),
238 Some(UncheckedVariantConstructor::Boxed) => match variant.fields.first() {
239 Some(first_field) => Some(VariantConstructor::Boxed {
240 referent: extract_first_type_param(first_field.ty),
241 pointer: first_field.ty,
242 }),
243 None => Some(VariantConstructor::Regular),
244 },
245 None => None,
246 };
247
248 NEVariant {
249 constructor,
250 is_hidden: vc.is_hidden,
251 }
252 })
253 .collect();
254
255 errors.into_result()?;
256
257 Ok(Self {
258 nonexhaustive_alias: parse_ident(&format!("{}_NE", name), None),
259 nonexhaustive_marker: parse_ident(&format!("{}_NEMarker", name), None),
260 enum_storage: parse_ident(&format!("{}_Storage", name), None),
261 alignment,
262 size,
263 enum_interface: unchecked.enum_interface,
264 default_interface,
265 new_interface,
266 assert_nonexh: unchecked.assert_nonexh,
267 bounds_trait,
268 ne_variants,
269 })
270 }
271}
272
273#[derive(Copy, Clone)]
274pub enum ExprOrType<'a> {
275 Int(usize),
276 Expr(&'a syn::Expr),
277 Type(&'a syn::Type),
278 Usize,
279}
280
281fn extract_first_type_param(ty: &syn::Type) -> Option<&syn::Type> {
283 match ty {
284 syn::Type::Path(path) => {
285 if path.qself.is_some() {
286 return None;
287 }
288 let args = &path.path.segments.last()?.arguments;
289 let args = match args {
290 syn::PathArguments::AngleBracketed(x) => x,
291 _ => return None,
292 };
293 args.args.iter().find_map(|arg| match arg {
294 syn::GenericArgument::Type(ty) => Some(ty),
295 _ => None,
296 })
297 }
298 _ => None,
299 }
300}
301
302fn hide_docs_if<T, F>(opt: &Option<T>, func: F) -> String
303where
304 F: FnOnce() -> String,
305{
306 if opt.is_some() {
307 String::new()
308 } else {
309 func()
310 }
311}
312
313pub(crate) fn tokenize_nonexhaustive_items<'a>(
316 ds: &'a DataStructure<'a>,
317 config: &'a StableAbiOptions<'a>,
318 _ct: &'a CommonTokens<'a>,
319) -> impl ToTokens + 'a {
320 ToTokenFnMut::new(move |ts| {
321 let this = match &config.kind {
322 StabilityKind::NonExhaustive(x) => x,
323 _ => return,
324 };
325 let doc_hidden_attr = config.doc_hidden_attr;
326 let vis = ds.vis;
327 let nonexhaustive_alias = this.nonexhaustive_alias;
328 let nonexhaustive_marker = this.nonexhaustive_marker;
329 let enum_storage = this.enum_storage;
330
331 let (aligner_attribute, aligner_field) = match this.alignment {
332 ExprOrType::Int(bytes) => {
333 let bytes = crate::utils::expr_from_int(bytes as _);
334 (Some(quote!(#[repr(align(#bytes))])), None)
335 }
336 ExprOrType::Expr(expr) => (
337 None,
338 Some(quote!(
339 __aligner: [
340 ::abi_stable::pmr::GetAlignerFor<::abi_stable::pmr::u8, #expr>;
341 0
342 ]
343 )),
344 ),
345 ExprOrType::Type(ty) => (None, Some(quote!(__aligner:[#ty;0],))),
346 ExprOrType::Usize => (
347 None,
348 Some(quote!(__aligner: [::abi_stable::pmr::usize; 0],)),
349 ),
350 };
351
352 let aligner_size = match this.size {
353 ExprOrType::Int(size) => quote!( #size ),
354 ExprOrType::Expr(expr) => quote!( (#expr) ),
355 ExprOrType::Type(ty) => quote!( ::std::mem::size_of::<#ty>() ),
356 ExprOrType::Usize => quote!(::std::mem::size_of::<::abi_stable::pmr::usize>()),
357 };
358
359 let name = ds.name;
360
361 let generics_header = GenParamsIn::new(ds.generics, InWhat::ImplHeader);
362
363 let mut type_generics_decl = GenParamsIn::new(ds.generics, InWhat::ImplHeader);
364 type_generics_decl.set_no_bounds();
365
366 let type_generics_use = GenParamsIn::new(ds.generics, InWhat::ItemUse);
367
368 let mut storage_docs = String::new();
369 let mut alias_docs = String::new();
370 let mut marker_docs = String::new();
371
372 if doc_hidden_attr.is_none() {
373 storage_docs = format!(
374 "The default InlineStorage that `NonExhaustive` uses for \
375 [`{E}`](./enum.{E}.html).",
376 E = name
377 );
378 alias_docs = format!(
379 "An alias for `NonExhaustive` wrapping a [`{E}`](./enum.{E}.html).",
380 E = name
381 );
382 marker_docs = format!(
383 "A marker type which implements StableAbi with the layout of \
384 [`{E}`](./enum.{E}.html),\
385 used as a phantom field of NonExhaustive.",
386 E = name
387 );
388 }
389
390 let default_interface = &this.default_interface;
391
392 quote!(
393 #[doc=#storage_docs]
394 #[repr(C)]
395 #[derive(::abi_stable::StableAbi)]
396 #aligner_attribute
397 #vis struct #enum_storage{
398 #[sabi(unsafe_opaque_field)]
399 _filler:[u8; #aligner_size ],
400 #aligner_field
401 }
402
403 #[doc=#alias_docs]
404 #vis type #nonexhaustive_alias<#type_generics_decl>=
405 ::abi_stable::pmr::NonExhaustive<
406 #name<#type_generics_use>,
407 #enum_storage,
408 #default_interface,
409 >;
410
411 unsafe impl ::abi_stable::pmr::InlineStorage for #enum_storage{}
412
413 #[doc=#marker_docs]
414 #vis struct #nonexhaustive_marker<T,S>(
415 std::marker::PhantomData<T>,
416 std::marker::PhantomData<S>,
417 );
418 )
419 .to_tokens(ts);
420
421 if let Some(BoundsTrait { ident, bounds }) = &this.bounds_trait {
422 let trait_docs = hide_docs_if(&doc_hidden_attr, || {
423 format!(
424 "An alias for the traits that \
425 `NonExhaustive<{E},_,_>` requires to be constructed,\
426 and implements afterwards.",
427 E = name
428 )
429 });
430
431 quote!(
432 #[doc=#trait_docs]
433 #vis trait #ident:#(#bounds+)*{}
434
435 impl<This> #ident for This
436 where
437 This:#(#bounds+)*
438 {}
439 )
440 .to_tokens(ts);
441 }
442
443 if let Some(new_interface) = this.new_interface {
444 let interface_docs = hide_docs_if(&doc_hidden_attr, || {
445 format!(
446 "Describes the traits required when constructing a \
447 `NonExhaustive<>` from [`{E}`](./enum.{E}.html),\
448 by implementing `InterfaceType`.",
449 E = name
450 )
451 });
452
453 quote!(
454 #[doc=#interface_docs]
455 #[repr(C)]
456 #[derive(::abi_stable::StableAbi)]
457 #vis struct #new_interface;
458 )
459 .to_tokens(ts);
460 }
461
462 if this.ne_variants.iter().any(|x| x.constructor.is_some()) {
463 let constructors = this
464 .ne_variants
465 .iter()
466 .cloned()
467 .zip(&ds.variants)
468 .filter_map(|(vc, variant)| {
469 let constructor = vc.constructor.as_ref()?;
470 let variant_ident = variant.name;
471 let mut method_name = parse_str_as_ident(&format!("{}_NE", variant.name));
472 method_name.set_span(variant.name.span());
473
474 let method_docs = if vc.is_hidden {
475 quote!(#[doc(hidden)])
476 } else {
477 let v_doc = format!(
478 "Constructs the `{}::{}` variant inside a `NonExhaustive`.",
479 ds.name, variant.name,
480 );
481 quote!(#[doc= #v_doc])
482 };
483
484 match constructor {
485 VariantConstructor::Regular => {
486 let field_names_a = variant.fields.iter().map(|x| x.pat_ident());
487 let field_names_b = field_names_a.clone();
488 let field_names_c = variant.fields.iter().map(|x| &x.ident);
489 let field_types = variant.fields.iter().map(|x| x.ty);
490 quote! {
491 #method_docs
492 #vis fn #method_name(
493 #( #field_names_a : #field_types ,)*
494 )->#nonexhaustive_alias<#type_generics_use> {
495 let x=#name::#variant_ident{
496 #( #field_names_c:#field_names_b, )*
497 };
498 #nonexhaustive_alias::new(x)
499 }
500 }
501 }
502 VariantConstructor::Boxed { referent, pointer } => {
503 let ptr_field_ident = &variant.fields[0].ident;
504 let type_param = ToTokenFnMut::new(|ts| match referent {
505 Some(x) => x.to_tokens(ts),
506 None => {
507 quote!( <#pointer as ::abi_stable::pmr::GetPointerKind>::PtrTarget )
508 .to_tokens(ts)
509 }
510 });
511
512 quote! {
513 #method_docs
514 #vis fn #method_name(
515 value:#type_param,
516 )->#nonexhaustive_alias<#type_generics_use> {
517 let x=<#pointer>::new(value);
518 let x=#name::#variant_ident{
519 #ptr_field_ident:x,
520 };
521 #nonexhaustive_alias::new(x)
522 }
523 }
524 }
525 }
526 .piped(Some)
527 });
528
529 let preds = ds.generics.where_clause.as_ref().map(|w| &w.predicates);
530
531 let bound = match &this.bounds_trait {
532 Some(BoundsTrait { ident, .. }) => quote!(#ident),
533 None => quote!(
534 ::abi_stable::pmr::NonExhaustiveMarkerVTable<
535 #enum_storage,
536 #default_interface,
537 >
538 ),
539 };
540
541 quote!(
542 #[allow(non_snake_case)]
543 impl<#generics_header> #name<#type_generics_use>
544 where
545 Self: #bound ,
546 #preds
547 {
548 #(#constructors)*
549 }
550 )
551 .to_tokens(ts);
552 }
553 })
554}
555
556pub(crate) fn tokenize_enum_info<'a>(
559 ds: &'a DataStructure<'a>,
560 variant_names_start_len: StartLen,
561 config: &'a StableAbiOptions<'a>,
562 ct: &'a CommonTokens<'a>,
563) -> Result<impl ToTokens + 'a, syn::Error> {
564 let opt_type_ident = config.repr.type_ident();
565 if let (StabilityKind::NonExhaustive { .. }, None) = (&config.kind, &opt_type_ident) {
566 return_spanned_err!(
567 ds.name,
568 "Attempted to get type of discriminant for this representation:\n\t{:?}",
569 config.repr
570 );
571 }
572
573 Ok(ToTokenFnMut::new(move |ts| {
574 let this = match &config.kind {
575 StabilityKind::NonExhaustive(x) => x,
576 _ => return,
577 };
578
579 let name = ds.name;
580 let name_str = rstr_tokenizer(ds.name.to_string());
581
582 let strings_const = &config.const_idents.strings;
583
584 let discriminants = ds
585 .variants
586 .iter()
587 .map(|x| x.discriminant)
588 .collect::<Vec<Option<&'a syn::Expr>>>();
589
590 let discriminant_tokens = config
591 .repr
592 .tokenize_discriminant_slice(discriminants.iter().cloned(), ct);
593
594 let discriminant_type = match &opt_type_ident {
595 Some(x) => x,
596 None => unreachable!(),
597 };
598
599 let vn_start = variant_names_start_len.start;
600 let vn_len = variant_names_start_len.len;
601
602 let nonexhaustive_marker = this.nonexhaustive_marker;
603 let enum_storage = this.enum_storage;
604
605 let mut start_discrs = Vec::new();
606 let mut end_discrs = Vec::new();
607 if !discriminants.is_empty() {
608 let mut first_index = 0;
609
610 for (mut i, discr) in discriminants[1..].iter().cloned().enumerate() {
611 i += 1;
612 if discr.is_some() {
613 start_discrs.push(first_index);
614 end_discrs.push(i - 1);
615 first_index = i;
616 }
617 }
618
619 start_discrs.push(first_index);
620 end_discrs.push(discriminants.len() - 1);
621 }
622
623 let generics_header =
624 GenParamsIn::with_after_types(ds.generics, InWhat::ImplHeader, &ct.und_storage);
625
626 let generics_use = GenParamsIn::new(ds.generics, InWhat::ImplHeader);
627
628 let default_interface = &this.default_interface;
629
630 let (impl_generics, ty_generics, where_clause) = ds.generics.split_for_impl();
631
632 let preds = where_clause.as_ref().map(|w| &w.predicates);
633
634 quote!(
635
636 unsafe impl #impl_generics __sabi_re::GetStaticEquivalent_ for #name #ty_generics
637 where
638 #nonexhaustive_marker <Self,#enum_storage> :
639 __sabi_re::GetStaticEquivalent_,
640 #preds
641 {
642 type StaticEquivalent=__sabi_re::GetStaticEquivalent<
643 #nonexhaustive_marker <Self,#enum_storage>
644 >;
645 }
646
647 unsafe impl #impl_generics __sabi_re::GetEnumInfo for #name #ty_generics
648 #where_clause
649 {
650 type Discriminant=#discriminant_type;
651
652 type DefaultStorage=#enum_storage;
653
654 type DefaultInterface=#default_interface;
655
656 const ENUM_INFO:&'static __sabi_re::EnumInfo=
657 &__sabi_re::EnumInfo::_for_derive(
658 #name_str,
659 #strings_const,
660 ::abi_stable::type_layout::StartLen::new(#vn_start,#vn_len),
661 );
662
663 const DISCRIMINANTS: &'static[#discriminant_type]=
664 #discriminant_tokens;
665
666 fn is_valid_discriminant(discriminant:#discriminant_type)->bool{
667 #(
668 (
669 <Self as __sabi_re::GetEnumInfo>::DISCRIMINANTS[#start_discrs]
670 <= discriminant &&
671 discriminant <=
672 <Self as __sabi_re::GetEnumInfo>::DISCRIMINANTS[#end_discrs]
673 )||
674 )*
675 false
676 }
677 }
678
679
680 unsafe impl<#generics_header>
681 __sabi_re::NonExhaustiveMarker<__Storage>
682 for #name <#generics_use>
683 #where_clause
684 {
685 type Marker = #nonexhaustive_marker<Self,__Storage>;
686 }
687
688
689 )
690 .to_tokens(ts);
691
692 let self_type: syn::Type;
693 let self_type_buf: Vec<&syn::Type>;
694 let assert_nonexh = if this.assert_nonexh.is_empty() && ds.generics.params.is_empty() {
695 let name = ds.name;
696 self_type = syn::parse_quote!(#name);
697 self_type_buf = vec![&self_type];
698 &self_type_buf
699 } else {
700 &this.assert_nonexh
701 };
702
703 if !assert_nonexh.is_empty() {
704 let assertions = assert_nonexh.iter().cloned();
705 let assertions_str = assert_nonexh
706 .iter()
707 .map(|x| x.to_token_stream().to_string());
708 let enum_storage_str = enum_storage.to_string();
709 quote!(
710 #(
711 const _: () = ::abi_stable::pmr::assert_correct_storage::<#assertions, #enum_storage>(
712 ::abi_stable::pmr::AssertCsArgs{
713 enum_ty: #assertions_str,
714 storage_ty: #enum_storage_str,
715 }
716 );
717 )*
718 )
719 .to_tokens(ts);
720 }
721
722 match &this.enum_interface {
723 Some(EnumInterface::New(NewEnumInterface { impld, unimpld })) => {
724 let enum_interface = parse_str_as_ident(&format!("{}_Interface", name));
725
726 let priv_assocty = private_associated_type();
727
728 let impld_a = impld.iter();
729 let impld_b = impld.iter();
730
731 let unimpld_a = unimpld.iter();
732 let unimpld_b = unimpld.iter();
733
734 let const_ident =
735 parse_str_as_ident(&format!("_impl_InterfaceType_constant_{}", name,));
736
737 quote!(
738 const #const_ident:()={
739 use abi_stable::{
740 InterfaceType,
741 type_level::{
742 impl_enum::{Implemented,Unimplemented},
743 trait_marker,
744 },
745 };
746 impl InterfaceType for #enum_interface {
747 #( type #impld_a=Implemented<trait_marker::#impld_b>; )*
748 #( type #unimpld_a=Unimplemented<trait_marker::#unimpld_b>; )*
749 type #priv_assocty=();
750 }
751 };
752 )
753 .to_tokens(ts);
754 }
755 Some(EnumInterface::Old { .. }) => {}
756 None => {}
757 }
758 }))
759}