1use crate::*;
2
3use crate::{
4 composite_collections::{SmallCompositeVec as CompositeVec, SmallStartLen as StartLen},
5 impl_interfacetype::impl_interfacetype_tokenizer,
6 lifetimes::LifetimeIndex,
7 literals_constructors::{rslice_tokenizer, rstr_tokenizer},
8};
9
10use as_derive_utils::{
11 datastructure::{DataStructure, DataVariant},
12 gen_params_in::{GenParamsIn, InWhat},
13 return_spanned_err, return_syn_err, to_stream,
14 to_token_fn::ToTokenFnMut,
15};
16
17use syn::Ident;
18
19use proc_macro2::{Span, TokenStream as TokenStream2};
20
21use core_extensions::{IteratorExt, SelfOps};
22
23#[doc(hidden)]
24pub mod reflection;
25
26mod attribute_parsing;
27
28mod common_tokens;
29
30mod generic_params;
31
32mod nonexhaustive;
33
34mod prefix_types;
35
36mod repr_attrs;
37
38mod tl_function;
39
40mod tl_field;
41
42mod tl_multi_tl;
43
44mod shared_vars;
45
46#[cfg(test)]
47mod tests;
48
49use self::{
50 attribute_parsing::{
51 parse_attrs_for_stable_abi, ASTypeParamBound, ConstIdents, LayoutConstructor,
52 StabilityKind, StableAbiOptions,
53 },
54 common_tokens::CommonTokens,
55 nonexhaustive::{tokenize_enum_info, tokenize_nonexhaustive_items},
56 prefix_types::{prefix_type_tokenizer, PrefixTypeTokens},
57 reflection::ModReflMode,
58 shared_vars::SharedVars,
59 tl_field::CompTLField,
60 tl_function::{CompTLFunction, VisitedFieldMap},
61 tl_multi_tl::TypeLayoutRange,
62};
63
64pub(crate) fn derive(mut data: DeriveInput) -> Result<TokenStream2, syn::Error> {
65 data.generics.make_where_clause();
66
67 let arenas = Arenas::default();
70 let arenas = &arenas;
71 let ctokens = CommonTokens::new(arenas);
72 let ctokens = &ctokens;
73 let ds = &DataStructure::new(&data);
74 let config = &parse_attrs_for_stable_abi(ds.attrs, ds, arenas)?;
75 let shared_vars = &mut SharedVars::new(arenas, &config.const_idents, ctokens);
76 let generics = ds.generics;
77 let name = ds.name;
78
79 let doc_hidden_attr = config.doc_hidden_attr;
80
81 let generic_params_tokens =
83 generic_params::GenericParams::new(ds, shared_vars, config, ctokens);
84
85 if generics.lifetimes().count() > LifetimeIndex::MAX_LIFETIME_PARAM + 1 {
86 return_syn_err!(
87 Span::call_site(),
88 "Cannot have more than {} lifetime parameter.",
89 LifetimeIndex::MAX_LIFETIME_PARAM + 1
90 );
91 }
92
93 let visited_fields = &VisitedFieldMap::new(ds, config, shared_vars, ctokens);
94 shared_vars.extract_errs()?;
95
96 let mono_type_layout = &Ident::new(&format!("_MONO_LAYOUT_{}", name), Span::call_site());
97
98 let (_, _, where_clause) = generics.split_for_impl();
99 let where_clause = (&where_clause.expect("BUG").predicates).into_iter();
100 let where_clause_b = where_clause.clone();
101
102 let ty_generics = GenParamsIn::new(generics, InWhat::ItemUse);
103
104 let impld_stable_abi_trait = match &config.kind {
105 StabilityKind::Value {
106 impl_prefix_stable_abi: false,
107 }
108 | StabilityKind::NonExhaustive { .. } => Ident::new("StableAbi", Span::call_site()),
109 StabilityKind::Value {
110 impl_prefix_stable_abi: true,
111 }
112 | StabilityKind::Prefix { .. } => Ident::new("PrefixStableAbi", Span::call_site()),
113 };
114
115 let impl_ty = match &config.kind {
117 StabilityKind::Value { .. } => quote!(#name <#ty_generics> ),
118 StabilityKind::Prefix(prefix) => {
119 let n = &prefix.prefix_fields_struct;
120 quote!(#n <#ty_generics> )
121 }
122 StabilityKind::NonExhaustive(nonexhaustive) => {
123 let marker = nonexhaustive.nonexhaustive_marker;
124 quote!(#marker < #name <#ty_generics> , __Storage > )
125 }
126 };
127
128 let mut prefix_type_trait_bound = None;
129 let mut prefix_bounds: &[_] = &[];
130
131 let size_align_for = match &config.kind {
133 StabilityKind::NonExhaustive(_) => {
134 quote!(__Storage)
135 }
136 StabilityKind::Prefix(prefix) => {
137 let prefix_fields_struct = prefix.prefix_fields_struct;
138
139 prefix_type_trait_bound = Some(quote!(
140 #name <#ty_generics>:__sabi_re::PrefixTypeTrait,
141 ));
142 prefix_bounds = &prefix.prefix_bounds;
143
144 quote!( #prefix_fields_struct <#ty_generics> )
145 }
146 StabilityKind::Value { .. } => quote!(Self),
147 };
148
149 let repr = config.repr;
150
151 let is_transparent = config.repr.is_repr_transparent();
152 let is_enum = ds.data_variant == DataVariant::Enum;
153 let prefix = match &config.kind {
154 StabilityKind::Prefix(prefix) => Some(prefix),
155 _ => None,
156 };
157 let nonexh_opt = match &config.kind {
158 StabilityKind::NonExhaustive(nonexhaustive) => Some(nonexhaustive),
159 _ => None,
160 };
161
162 let tags_const;
163 let tags_arg;
164 match &config.tags {
166 Some(tag) => {
167 tags_const = quote!( const __SABI_TAG: &'static __sabi_re::Tag = &#tag; );
168 tags_arg = quote!(Some(Self::__SABI_TAG));
169 }
170 None => {
171 tags_const = TokenStream2::new();
172 tags_arg = quote!(None);
173 }
174 }
175
176 let extra_checks_const;
177 let extra_checks_arg;
178 match &config.extra_checks {
179 Some(extra_checks) => {
180 extra_checks_const = quote!(
181 const __SABI_EXTRA_CHECKS:
182 &'static ::std::mem::ManuallyDrop<__sabi_re::StoredExtraChecks>
183 =
184 &std::mem::ManuallyDrop::new(
185 __sabi_re::StoredExtraChecks::from_const(
186 &#extra_checks,
187 __sabi_re::TD_Opaque,
188 )
189 );
190 );
191
192 extra_checks_arg = quote!(Some(Self::__SABI_EXTRA_CHECKS));
193 }
194 None => {
195 extra_checks_const = TokenStream2::new();
196 extra_checks_arg = quote!(None);
197 }
198 };
199
200 let variant_names_start_len = if is_enum {
201 let mut variant_names = String::new();
202 for variant in &ds.variants {
203 use std::fmt::Write;
204 let _ = write!(variant_names, "{};", variant.name);
205 }
206 shared_vars.push_str(&variant_names, None)
207 } else {
208 StartLen::EMPTY
209 };
210
211 let nonexhaustive_items = tokenize_nonexhaustive_items(ds, config, ctokens);
213
214 let nonexhaustive_tokens = tokenize_enum_info(ds, variant_names_start_len, config, ctokens)?;
216
217 let is_nonzero = if is_transparent && !visited_fields.map.is_empty() {
218 let visited_field = &visited_fields.map[0];
219
220 let is_opaque_field = visited_field.layout_ctor.is_opaque();
221 if visited_field.comp_field.is_function() {
222 quote!(__sabi_re::True)
223 } else if is_opaque_field {
224 quote!(__sabi_re::False)
225 } else {
226 let ty = visited_field.comp_field.type_(shared_vars);
227 quote!( <#ty as __StableAbi>::IsNonZeroType )
228 }
229 } else {
230 quote!(__sabi_re::False)
231 };
232
233 let ct = ctokens;
234 let mono_tl_data;
236 let generic_tl_data;
238
239 match (is_enum, prefix) {
240 (false, None) => {
241 mono_tl_data = {
242 let fields = fields_tokenizer(ds, visited_fields, ct);
243 match ds.data_variant {
244 DataVariant::Struct => quote!( __sabi_re::MonoTLData::derive_struct(#fields) ),
245 DataVariant::Union => quote!( __sabi_re::MonoTLData::derive_union(#fields) ),
246 DataVariant::Enum => unreachable!(),
247 }
248 };
249 generic_tl_data = {
250 match ds.data_variant {
251 DataVariant::Struct => quote!(__sabi_re::GenericTLData::Struct),
252 DataVariant::Union => quote!(__sabi_re::GenericTLData::Union),
253 DataVariant::Enum => unreachable!(),
254 }
255 };
256 }
257 (true, None) => {
258 let vn_sl = variant_names_start_len;
259 mono_tl_data = {
260 let mono_enum_tokenizer =
261 tokenize_mono_enum(ds, vn_sl, nonexh_opt, config, visited_fields, shared_vars);
262 quote!( __sabi_re::MonoTLData::Enum(#mono_enum_tokenizer) )
263 };
264 generic_tl_data = {
265 let generic_enum_tokenizer =
266 tokenize_generic_enum(ds, vn_sl, nonexh_opt, config, visited_fields, ct);
267 quote!( __sabi_re::GenericTLData::Enum(#generic_enum_tokenizer) )
268 };
269 }
270 (false, Some(prefix)) => {
271 if is_transparent {
272 return_spanned_err!(name, "repr(transparent) prefix types not supported")
273 }
274
275 mono_tl_data = {
276 let first_suffix_field = prefix.first_suffix_field.field_pos;
277 let fields = fields_tokenizer(ds, visited_fields, ct);
278 let prefix_field_conditionality_mask = prefix.prefix_field_conditionality_mask;
279 quote!(
280 __sabi_re::MonoTLData::prefix_type_derive(
281 #first_suffix_field,
282 #prefix_field_conditionality_mask,
283 #fields
284 )
285 )
286 };
287 generic_tl_data = {
288 quote!(
289 __sabi_re::GenericTLData::prefix_type_derive(
290 <#name <#ty_generics> as
291 __sabi_re::PrefixTypeTrait
292 >::PT_FIELD_ACCESSIBILITY,
293 )
294 )
295 };
296 }
297 (true, Some(_)) => {
298 return_spanned_err!(name, "enum prefix types not supported");
299 }
300 };
301
302 let lifetimes = &generics
303 .lifetimes()
304 .map(|x| &x.lifetime)
305 .collect::<Vec<_>>();
306 let type_params = &generics.type_params().map(|x| &x.ident).collect::<Vec<_>>();
307 let const_params = &generics
308 .const_params()
309 .map(|x| &x.ident)
310 .collect::<Vec<_>>();
311
312 let lifetimes_s = lifetimes.iter().map(|_| &ctokens.static_lt);
314 let type_params_s = ToTokenFnMut::new(|ts| {
315 let ct = ctokens;
316
317 for (ty_param, bounds) in config.type_param_bounds.iter() {
318 match bounds {
319 ASTypeParamBound::NoBound => {
320 ct.empty_tuple.to_tokens(ts);
321 }
322 ASTypeParamBound::GetStaticEquivalent | ASTypeParamBound::StableAbi => {
323 to_stream!(ts; ct.static_equivalent, ct.lt, ty_param, ct.gt);
324 }
325 }
326 ct.comma.to_tokens(ts);
327 }
328 });
329 let const_params_s = &const_params;
330
331 let static_struct_name = Ident::new(&format!("_static_{}", name), Span::call_site());
334
335 let item_info_const = Ident::new(&format!("_item_info_const_{}", name), Span::call_site());
336
337 let static_struct_decl = {
338 let const_param_name = generics.const_params().map(|c| &c.ident);
339 let const_param_type = generics.const_params().map(|c| &c.ty);
340
341 let lifetimes_a = lifetimes;
342
343 let type_params_a = type_params;
344
345 quote! {
346 #doc_hidden_attr
347 pub struct #static_struct_name<
348 #(#lifetimes_a,)*
349 #(#type_params_a:?Sized,)*
350 #(const #const_param_name:#const_param_type,)*
351 >(
352 #(& #lifetimes_a (),)*
353 extern "C" fn(#(&#type_params_a,)*)
354 );
355 }
356 };
357
358 let interfacetype_tokenizer =
361 impl_interfacetype_tokenizer(ds.name, ds.generics, config.impl_interfacetype.as_ref());
362
363 let stringified_name = rstr_tokenizer(name.to_string());
364
365 let mut stable_abi_bounded = Vec::new();
366 let mut static_equiv_bounded = Vec::new();
367
368 for (ident, bounds) in config.type_param_bounds.iter() {
369 let list = match bounds {
370 ASTypeParamBound::NoBound => None,
371 ASTypeParamBound::GetStaticEquivalent => Some(&mut static_equiv_bounded),
372 ASTypeParamBound::StableAbi => Some(&mut stable_abi_bounded),
373 };
374 if let Some(list) = list {
375 list.push(ident);
376 }
377 }
378
379 let stable_abi_bounded = &stable_abi_bounded;
380 let static_equiv_bounded = &static_equiv_bounded;
381
382 let extra_bounds = &config.extra_bounds;
383
384 let PrefixTypeTokens {
385 prefixref_types,
386 prefixref_impls,
387 } = prefix_type_tokenizer(mono_type_layout, ds, config, ctokens)?;
388
389 let mod_refl_mode = match config.mod_refl_mode {
390 ModReflMode::Module => quote!(__ModReflMode::Module),
391 ModReflMode::Opaque => quote!(__ModReflMode::Opaque),
392 ModReflMode::DelegateDeref(field_index) => {
393 quote!(
394 __ModReflMode::DelegateDeref{
395 phantom_field_index:#field_index
396 }
397 )
398 }
399 };
400
401 let phantom_field_tys = config.phantom_fields.iter().map(|x| x.1);
402
403 let phantom_fields = config
406 .phantom_fields
407 .iter()
408 .map(|(name, ty)| {
409 CompTLField::from_expanded_std_field(
410 name,
411 std::iter::empty(),
412 shared_vars.push_type(LayoutConstructor::Regular, ty),
413 shared_vars,
414 )
415 })
416 .collect::<Vec<CompTLField>>();
417 let phantom_fields = rslice_tokenizer(&phantom_fields);
418
419 let storage_opt = nonexh_opt.map(|_| &ctokens.und_storage);
421 let generics_header =
422 GenParamsIn::with_after_types(ds.generics, InWhat::ImplHeader, storage_opt);
423
424 shared_vars.extract_errs()?;
425
426 let mono_shared_vars_tokenizer = shared_vars.mono_shared_vars_tokenizer();
427
428 let strings_const = &config.const_idents.strings;
429 let strings = shared_vars.strings().piped(rstr_tokenizer);
430
431 let shared_vars_tokenizer = shared_vars.shared_vars_tokenizer(mono_type_layout);
432
433 let shared_where_preds = quote!(
436 #(#where_clause_b,)*
437 #(#stable_abi_bounded:__StableAbi,)*
438 #(#static_equiv_bounded:__GetStaticEquivalent_,)*
439 #(#extra_bounds,)*
440 #(#prefix_bounds,)*
441 #prefix_type_trait_bound
442 );
443
444 let stable_abi_where_preds = shared_where_preds.clone().mutated(|ts| {
445 ts.extend(quote!(
446 #(#phantom_field_tys:__StableAbi,)*
447 ))
448 });
449
450 let prefix_ref_impls = if let StabilityKind::Prefix(prefix) = &config.kind {
451 let prefix_ref = &prefix.prefix_ref;
452 let prefix_fields_struct = &prefix.prefix_fields_struct;
453 let lifetimes_s = lifetimes_s.clone();
454
455 quote!(
456 unsafe impl<#generics_header> __sabi_re::GetStaticEquivalent_
457 for #prefix_ref <#ty_generics>
458 where
459 #shared_where_preds
460 {
461 type StaticEquivalent =
462 __sabi_re::PrefixRef<
463 #static_struct_name <
464 #(#lifetimes_s,)*
465 #type_params_s
466 #({#const_params_s}),*
467 >
468 >;
469 }
470
471 unsafe impl<#generics_header> __sabi_re::StableAbi for #prefix_ref <#ty_generics>
472 where
473 #stable_abi_where_preds
474 {
475 type IsNonZeroType = __sabi_re::True;
476
477 const LAYOUT: &'static __sabi_re::TypeLayout =
478 <__sabi_re::PrefixRef<#prefix_fields_struct <#ty_generics>>
479 as __sabi_re::StableAbi
480 >::LAYOUT;
481 }
482 )
483 } else {
484 TokenStream2::new()
485 };
486
487 quote!(
488 #prefixref_types
489
490 #nonexhaustive_items
491
492 const _: () = {
493 use ::abi_stable;
494
495 #[allow(unused_imports)]
496 use ::abi_stable::pmr::{
497 self as __sabi_re,
498 renamed::*,
499 };
500
501 #prefixref_impls
502
503 const #item_info_const: abi_stable::type_layout::ItemInfo=
504 abi_stable::make_item_info!();
505
506 const #strings_const: ::abi_stable::std_types::RStr<'static>=#strings;
507
508
509 #static_struct_decl
510
511 #nonexhaustive_tokens
512
513 #interfacetype_tokenizer
514
515 #prefix_ref_impls
516
517 unsafe impl <#generics_header> __GetStaticEquivalent_ for #impl_ty
518 where
519 #shared_where_preds
520 {
521 type StaticEquivalent=#static_struct_name <
522 #(#lifetimes_s,)*
523 #type_params_s
524 #({#const_params_s}),*
525 >;
526 }
527
528 #[doc(hidden)]
529 const #mono_type_layout:&'static __sabi_re::MonoTypeLayout=
530 &__sabi_re::MonoTypeLayout::from_derive(
531 __sabi_re::_private_MonoTypeLayoutDerive{
532 name: #stringified_name,
533 item_info: #item_info_const,
534 data: #mono_tl_data,
535 generics: #generic_params_tokens,
536 mod_refl_mode:#mod_refl_mode,
537 repr_attr:#repr,
538 phantom_fields:#phantom_fields,
539 shared_vars: #mono_shared_vars_tokenizer,
540 }
541 );
542
543 impl <#generics_header> #impl_ty
544 where
545 #stable_abi_where_preds
546 {
547 #shared_vars_tokenizer
548
549 #extra_checks_const
550
551 #tags_const
552 }
553
554 unsafe impl <#generics_header> __sabi_re::#impld_stable_abi_trait for #impl_ty
555 where
556 #stable_abi_where_preds
557
558 {
559 type IsNonZeroType=#is_nonzero;
560
561 const LAYOUT: &'static __sabi_re::TypeLayout = {
562 &__sabi_re::TypeLayout::from_derive::<#size_align_for>(
563 __sabi_re::_private_TypeLayoutDerive {
564 shared_vars: Self::__SABI_SHARED_VARS,
565 mono:#mono_type_layout,
566 abi_consts: Self::ABI_CONSTS,
567 data:#generic_tl_data,
568 tag: #tags_arg,
569 extra_checks: #extra_checks_arg,
570 }
571 )
572 };
573 }
574
575 };
576 )
577 .observe(|tokens| {
578 if config.debug_print {
580 panic!("\n\n\n{}\n\n\n", tokens);
581 }
582 })
583 .piped(Ok)
584}
585
586fn tokenize_mono_enum<'a>(
588 ds: &'a DataStructure<'a>,
589 variant_names_start_len: StartLen,
590 _nonexhaustive_opt: Option<&'a nonexhaustive::NonExhaustive<'a>>,
591 _config: &'a StableAbiOptions<'a>,
592 visited_fields: &'a VisitedFieldMap<'a>,
593 shared_vars: &mut SharedVars<'a>,
594) -> impl ToTokens + 'a {
595 let ct = shared_vars.ctokens();
596
597 ToTokenFnMut::new(move |ts| {
598 let variant_names_start_len = variant_names_start_len.tokenizer(ct.as_ref());
599
600 let variant_lengths = ds.variants.iter().map(|x| {
601 assert!(
602 x.fields.len() < 256,
603 "variant '{}' has more than 255 fields.",
604 x.name
605 );
606 x.fields.len() as u8
607 });
608
609 let fields = fields_tokenizer(ds, visited_fields, ct);
610
611 quote!(
612 __sabi_re::MonoTLEnum::new(
613 #variant_names_start_len,
614 abi_stable::rslice![#( #variant_lengths ),*],
615 #fields,
616 )
617 )
618 .to_tokens(ts);
619 })
620}
621
622fn tokenize_generic_enum<'a>(
624 ds: &'a DataStructure<'a>,
625 _variant_names_start_len: StartLen,
626 nonexhaustive_opt: Option<&'a nonexhaustive::NonExhaustive<'a>>,
627 config: &'a StableAbiOptions<'a>,
628 _visited_fields: &'a VisitedFieldMap<'a>,
629 ct: &'a CommonTokens<'a>,
630) -> impl ToTokens + 'a {
631 ToTokenFnMut::new(move |ts| {
632 let is_exhaustive = match nonexhaustive_opt {
633 Some(_) => {
634 let name = ds.name;
635
636 let ty_generics = GenParamsIn::new(ds.generics, InWhat::ItemUse);
637 quote!(nonexhaustive(
639 &__sabi_re::MakeTLNonExhaustive::< #name <#ty_generics> >::NEW
640 ))
641 }
642 None => quote!(exhaustive()),
643 };
644
645 let discriminants = ds.variants.iter().map(|x| x.discriminant);
646 let discriminants = config.repr.tokenize_discriminant_exprs(discriminants, ct);
647
648 quote!(
649 __sabi_re::GenericTLEnum::new(
650 __IsExhaustive::#is_exhaustive,
651 #discriminants,
652 )
653 )
654 .to_tokens(ts);
655 })
656}
657
658fn fields_tokenizer<'a>(
660 ds: &'a DataStructure<'a>,
661 visited_fields: &'a VisitedFieldMap<'a>,
662 ctokens: &'a CommonTokens<'a>,
663) -> impl ToTokens + 'a {
664 ToTokenFnMut::new(move |ts| {
665 to_stream!(ts;ctokens.comp_tl_fields,ctokens.colon2,ctokens.new);
666 ctokens.paren.surround(ts, |ts| {
667 fields_tokenizer_inner(ds, visited_fields, ctokens, ts);
668 });
669 })
670}
671
672fn fields_tokenizer_inner<'a>(
673 ds: &'a DataStructure<'a>,
674 visited_fields: &'a VisitedFieldMap<'a>,
675 ct: &'a CommonTokens<'a>,
676 ts: &mut TokenStream2,
677) {
678 let iter = visited_fields.map.iter().map(|field| field.comp_field);
679 rslice_tokenizer(iter).to_tokens(ts);
680
681 ct.comma.to_tokens(ts);
682
683 if visited_fields.fn_ptr_count == 0 {
684 ct.none.to_tokens(ts);
685 } else {
686 to_stream!(ts;ct.some);
687 ct.paren.surround(ts, |ts| {
688 ct.and_.to_tokens(ts);
689 tokenize_tl_functions(ds, visited_fields, ct, ts);
690 });
691 }
692 to_stream! {ts; ct.comma };
693}
694
695fn tokenize_tl_functions<'a>(
697 ds: &'a DataStructure<'a>,
698 visited_fields: &'a VisitedFieldMap<'a>,
699 _ct: &'a CommonTokens<'a>,
700 ts: &mut TokenStream2,
701) {
702 let mut functions =
703 CompositeVec::<&'a CompTLFunction>::with_capacity(visited_fields.fn_ptr_count);
704 let mut field_fn_ranges = Vec::<StartLen>::with_capacity(ds.field_count);
705
706 visited_fields
707 .map
708 .iter()
709 .map(|field| functions.extend(&field.functions))
710 .extending(&mut field_fn_ranges);
711
712 let functions = functions.into_inner();
713
714 let field_fn_ranges = field_fn_ranges.into_iter().map(|sl| sl.to_u32());
715
716 quote!({
717 const TLF_A: &[__CompTLFunction] = &[#(#functions),*];
718 const TLF_B: __TLFunctions = __TLFunctions::new(
719 __sabi_re::RSlice::from_slice(TLF_A),
720 abi_stable::rslice![#(#field_fn_ranges),*],
721 );
722 TLF_B
723 })
724 .to_tokens(ts);
725}