1use std::iter::once;
178use std::ops::Not;
179use std::{iter, vec};
180
181pub(crate) use Substructure::*;
182pub(crate) use rustc_ast as ast;
183use rustc_ast::token::{IdentKind, LitKind, Token, TokenKind};
184use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenTree};
185use rustc_ast::{
186 AttrArgs, DelimArgs, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Safety, SelfKind,
187 VariantData,
188};
189use rustc_attr_ir::{Attribute, AttributeKind, ReprPacked};
190use rustc_attr_parsing::AttributeParser;
191use rustc_expand::base::ExtCtxt;
192use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, respan, sym};
193pub(crate) use smallvec::{SmallVec, smallvec};
194use thin_vec::{ThinVec, thin_vec};
195use ty::{Ref, Self_, Ty};
196
197use crate::{deriving, diagnostics};
198
199pub(crate) mod ty;
200
201pub(crate) struct TraitDef<'a> {
202 pub span: Span,
204
205 pub path: ast::Path,
207
208 pub skip_path_as_bound: bool,
210
211 pub needs_copy_as_bound_if_packed: bool,
213
214 pub additional_bounds: SmallVec<[ast::Path; 1]>,
217
218 pub supports_unions: bool,
220
221 pub methods: SmallVec<[MethodDef<'a>; 1]>,
222
223 pub associated_types: SmallVec<[(Ident, Ty); 1]>,
224
225 pub is_const: bool,
226
227 pub safety: Safety,
229
230 pub document: bool,
232}
233
234pub(crate) struct MethodDef<'a> {
235 pub name: Symbol,
237 pub generics: Generics,
239
240 pub explicit_self: bool,
242
243 pub nonself_args: SmallVec<[(Ty, Symbol); 1]>,
245
246 pub ret_ty: Ty,
248
249 pub attributes: ast::AttrVec,
250
251 pub fieldless_variants_strategy: FieldlessVariantsStrategy,
252
253 pub combine_substructure: CombineSubstructureFunc<'a>,
254}
255
256#[derive(#[automatically_derived]
impl ::core::marker::StructuralPartialEq for FieldlessVariantsStrategy { }
#[automatically_derived]
impl ::core::cmp::PartialEq for FieldlessVariantsStrategy {
#[inline]
fn eq(&self, other: &Self) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
258pub(crate) enum FieldlessVariantsStrategy {
259 Unify,
263 Default,
266 SpecializeIfAllVariantsFieldless,
270}
271
272pub(crate) struct FieldInfo {
274 pub span: Span,
275 pub name: Option<Ident>,
278 pub self_expr: Box<Expr>,
281 pub other_selflike_expr: Option<Box<Expr>>,
284 pub maybe_scalar: bool,
285}
286
287pub(crate) enum Substructure<'a> {
289 Struct(&'a ast::VariantData, Vec<FieldInfo>),
291
292 AllFieldlessEnum(&'a ast::EnumDef),
296
297 EnumMatching(&'a ast::Variant, Vec<FieldInfo>),
301
302 EnumDiscr(FieldInfo, Option<Box<Expr>>),
306
307 StaticStruct(&'a ast::VariantData),
309
310 StaticEnum(&'a ast::EnumDef),
312}
313
314pub(crate) type CombineSubstructureFunc<'a> =
317 Box<dyn Fn(&ExtCtxt<'_>, Span, Substructure<'_>) -> BlockOrExpr + 'a>;
318
319pub(crate) fn combine_substructure<'a>(
320 f: impl Fn(&ExtCtxt<'_>, Span, Substructure<'_>) -> BlockOrExpr + 'a,
321) -> CombineSubstructureFunc<'a> {
322 Box::new(f)
323}
324
325struct TypeParameter {
326 bound_generic_params: ThinVec<ast::GenericParam>,
327 ty: Box<ast::Ty>,
328}
329
330pub(crate) struct BlockOrExpr(ThinVec<ast::Stmt>, Option<Box<Expr>>);
337
338impl BlockOrExpr {
339 pub(crate) fn new_stmts(stmts: ThinVec<ast::Stmt>) -> BlockOrExpr {
340 BlockOrExpr(stmts, None)
341 }
342
343 pub(crate) fn new_expr(expr: Box<Expr>) -> BlockOrExpr {
344 BlockOrExpr(ThinVec::new(), Some(expr))
345 }
346
347 pub(crate) fn new_mixed(stmts: ThinVec<ast::Stmt>, expr: Option<Box<Expr>>) -> BlockOrExpr {
348 BlockOrExpr(stmts, expr)
349 }
350
351 fn into_block(mut self, cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Block> {
353 if let Some(expr) = self.1 {
354 self.0.push(cx.stmt_expr(expr));
355 }
356 cx.block(span, self.0)
357 }
358
359 fn into_expr(self, cx: &ExtCtxt<'_>, span: Span) -> Box<Expr> {
361 if self.0.is_empty() {
362 match self.1 {
363 None => cx.expr_block(cx.block(span, ThinVec::new())),
364 Some(expr) => expr,
365 }
366 } else if let [stmt] = self.0.as_slice()
367 && let ast::StmtKind::Expr(expr) = &stmt.kind
368 && self.1.is_none()
369 {
370 expr.clone()
372 } else {
373 cx.expr_block(self.into_block(cx, span))
375 }
376 }
377}
378
379fn find_type_parameters(
384 ty: &ast::Ty,
385 ty_param_names: &[Symbol],
386 cx: &ExtCtxt<'_>,
387) -> Vec<TypeParameter> {
388 use rustc_ast::visit;
389
390 struct Visitor<'a, 'b> {
391 cx: &'a ExtCtxt<'b>,
392 ty_param_names: &'a [Symbol],
393 bound_generic_params_stack: ThinVec<ast::GenericParam>,
394 type_params: Vec<TypeParameter>,
395 }
396
397 impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> {
398 fn visit_ty(&mut self, ty: &'a ast::Ty) {
399 let stack_len = self.bound_generic_params_stack.len();
400 if let ast::TyKind::FnPtr(fn_ptr) = &ty.kind
401 && !fn_ptr.generic_params.is_empty()
402 {
403 self.bound_generic_params_stack.extend(fn_ptr.generic_params.iter().cloned());
406 }
407
408 if let ast::TyKind::Path(_, path) = &ty.kind
409 && let Some(segment) = path.segments.first()
410 && self.ty_param_names.contains(&segment.ident.name)
411 {
412 self.type_params.push(TypeParameter {
413 bound_generic_params: self.bound_generic_params_stack.clone(),
414 ty: Box::new(ty.clone()),
415 });
416 }
417
418 visit::walk_ty(self, ty);
419 self.bound_generic_params_stack.truncate(stack_len);
420 }
421
422 fn visit_poly_trait_ref(&mut self, trait_ref: &'a ast::PolyTraitRef) {
424 let stack_len = self.bound_generic_params_stack.len();
425 self.bound_generic_params_stack.extend(trait_ref.bound_generic_params.iter().cloned());
426
427 visit::walk_poly_trait_ref(self, trait_ref);
428
429 self.bound_generic_params_stack.truncate(stack_len);
430 }
431
432 fn visit_mac_call(&mut self, mac: &ast::MacCall) {
433 self.cx.dcx().emit_err(diagnostics::DeriveMacroCall { span: mac.span() });
434 }
435 }
436
437 let mut visitor = Visitor {
438 cx,
439 ty_param_names,
440 bound_generic_params_stack: ThinVec::new(),
441 type_params: Vec::new(),
442 };
443 visit::Visitor::visit_ty(&mut visitor, ty);
444
445 visitor.type_params
446}
447
448impl<'a> TraitDef<'a> {
449 pub(crate) fn expand(
450 self,
451 cx: &ExtCtxt<'_>,
452 item: &'a ast::Item,
453 push: &mut dyn FnMut(Box<ast::Item>),
454 ) {
455 self.expand_ext(cx, item, push, false);
456 }
457
458 pub(crate) fn expand_ext(
459 self,
460 cx: &ExtCtxt<'_>,
461 item: &'a ast::Item,
462 push: &mut dyn FnMut(Box<ast::Item>),
463 from_scratch: bool,
464 ) {
465 let is_packed = #[allow(non_exhaustive_omitted_patterns)] match AttributeParser::parse_limited_sym(cx.sess,
&item.attrs, &[sym::repr]) {
Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if
reprs.iter().any(|(x, _)|
#[allow(non_exhaustive_omitted_patterns)] match x {
ReprPacked(..) => true,
_ => false,
}) => true,
_ => false,
}matches!(
466 AttributeParser::parse_limited_sym(cx.sess, &item.attrs, &[sym::repr]),
467 Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
468 );
469
470 let mut newitem = match &item.kind {
471 ast::ItemKind::Struct(ident, generics, struct_def) => {
472 self.expand_struct_def(cx, struct_def, *ident, generics, from_scratch, is_packed)
473 }
474 ast::ItemKind::Enum(ident, generics, enum_def) => {
475 if is_packed {
480 return;
481 }
482 self.expand_enum_def(cx, enum_def, *ident, generics, from_scratch)
483 }
484 ast::ItemKind::Union(ident, generics, struct_def) => {
485 if self.supports_unions {
486 self.expand_struct_def(
487 cx,
488 struct_def,
489 *ident,
490 generics,
491 from_scratch,
492 is_packed,
493 )
494 } else {
495 cx.dcx().emit_err(diagnostics::DeriveUnion { span: self.span });
496 return;
497 }
498 }
499 _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
500 };
501 newitem.attrs.extend(
504 item.attrs
505 .iter()
506 .filter(|a| {
507 a.has_any_name(&[
508 sym::allow,
509 sym::warn,
510 sym::deny,
511 sym::forbid,
512 sym::stable,
513 sym::unstable,
514 ])
515 })
516 .cloned(),
517 );
518 push(newitem);
519 }
520
521 fn create_derived_impl(
557 &self,
558 cx: &ExtCtxt<'_>,
559 type_ident: Ident,
560 generics: &Generics,
561 field_tys: impl Iterator<Item = &'a ast::Ty>,
562 methods: impl Iterator<Item = Box<ast::AssocItem>>,
563 is_packed: bool,
564 ) -> Box<ast::Item> {
565 let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| {
567 Box::new(ast::AssocItem {
568 id: ast::DUMMY_NODE_ID,
569 span: self.span,
570 vis: ast::Visibility {
571 span: self.span.shrink_to_lo(),
572 kind: ast::VisibilityKind::Inherited,
573 },
574 attrs: ast::AttrVec::new(),
575 kind: ast::AssocItemKind::Type(Box::new(ast::TyAlias {
576 defaultness: ast::Defaultness::Implicit,
577 ident,
578 generics: Generics::default(),
579 after_where_clause: ast::WhereClause::default(),
580 bounds: ThinVec::new(),
581 ty: Some(type_def.to_ty(cx, self.span)),
582 })),
583 tokens: None,
584 })
585 });
586
587 let mut where_clause = ast::WhereClause::default();
588 where_clause.span = generics.where_clause.span;
589 let ctxt = self.span.ctxt();
590 let span = generics.span.with_ctxt(ctxt);
591
592 let params: ThinVec<_> = generics
594 .params
595 .iter()
596 .map(|param| match ¶m.kind {
597 GenericParamKind::Lifetime => param.clone(),
598 GenericParamKind::Type { .. } => {
599 let span = param.ident.span.with_ctxt(ctxt);
602 let bounds: ThinVec<_> = self
603 .additional_bounds
604 .iter()
605 .map(|p| cx.trait_bound(ast::Path { span, ..p.clone() }, self.is_const))
606 .chain(
607 self.skip_path_as_bound.not().then(|| {
609 let mut trait_path = self.path.clone();
610 trait_path.span = span;
611 cx.trait_bound(trait_path, self.is_const)
612 }),
613 )
614 .chain({
615 if is_packed && self.needs_copy_as_bound_if_packed {
617 let p = generic::ty::new_path(cx, span, { &[sym::marker, sym::Copy] }, &[])deriving::path_std!(cx, span, marker::Copy);
618 Some(cx.trait_bound(p, self.is_const))
619 } else {
620 None
621 }
622 })
623 .chain(
624 param.bounds.iter().cloned(),
626 )
627 .collect();
628
629 cx.typaram(span, param.ident, bounds, None)
630 }
631 GenericParamKind::Const { ty, span, .. } => {
632 let const_nodefault_kind = GenericParamKind::Const {
633 ty: ty.clone(),
634 span: span.with_ctxt(ctxt),
635
636 default: None,
638 };
639 let mut param_clone = param.clone();
640 param_clone.kind = const_nodefault_kind;
641 param_clone
642 }
643 })
644 .map(|mut param| {
645 param.attrs.clear();
648 param
649 })
650 .collect();
651
652 where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
654 ast::WherePredicate {
655 attrs: clause.attrs.clone(),
656 kind: clause.kind.clone(),
657 id: ast::DUMMY_NODE_ID,
658 span: clause.span.with_ctxt(ctxt),
659 is_placeholder: false,
660 }
661 }));
662
663 let ty_param_names: Vec<Symbol> = params
664 .iter()
665 .filter(|param| #[allow(non_exhaustive_omitted_patterns)] match param.kind {
ast::GenericParamKind::Type { .. } => true,
_ => false,
}matches!(param.kind, ast::GenericParamKind::Type { .. }))
666 .map(|ty_param| ty_param.ident.name)
667 .collect();
668
669 if !ty_param_names.is_empty() {
670 for field_ty in field_tys {
671 let field_ty_params = find_type_parameters(field_ty, &ty_param_names, cx);
672
673 for field_ty_param in field_ty_params {
674 if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind
676 && let [sole_segment] = &*p.segments
677 && ty_param_names.contains(&sole_segment.ident.name)
678 {
679 continue;
680 }
681 let mut bounds: ThinVec<_> = self
682 .additional_bounds
683 .iter()
684 .map(|p| cx.trait_bound(p.clone(), self.is_const))
685 .collect();
686
687 if !self.skip_path_as_bound {
689 bounds.push(cx.trait_bound(self.path.clone(), self.is_const));
690 }
691
692 if is_packed && self.needs_copy_as_bound_if_packed {
694 let p = generic::ty::new_path(cx, self.span, { &[sym::marker, sym::Copy] }, &[])deriving::path_std!(cx, self.span, marker::Copy);
695 bounds.push(cx.trait_bound(p, self.is_const));
696 }
697
698 if !bounds.is_empty() {
699 let predicate = ast::WhereBoundPredicate {
700 bound_generic_params: field_ty_param.bound_generic_params,
701 bounded_ty: field_ty_param.ty,
702 bounds,
703 };
704
705 let kind = ast::WherePredicateKind::BoundPredicate(predicate);
706 let predicate = ast::WherePredicate {
707 attrs: ThinVec::new(),
708 kind,
709 id: ast::DUMMY_NODE_ID,
710 span: self.span,
711 is_placeholder: false,
712 };
713 where_clause.predicates.push(predicate);
714 }
715 }
716 }
717 }
718
719 let trait_generics = Generics { params, where_clause, span };
720
721 let trait_ref = cx.trait_ref(self.path.clone());
723
724 let self_params: Vec<_> = generics
725 .params
726 .iter()
727 .map(|param| match param.kind {
728 GenericParamKind::Lifetime => {
729 GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident))
730 }
731 GenericParamKind::Type { .. } => {
732 GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
733 }
734 GenericParamKind::Const { .. } => {
735 GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident))
736 }
737 })
738 .collect();
739
740 let path =
742 cx.path_all(type_ident.span.with_ctxt(ctxt), false, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[type_ident]))vec![type_ident], self_params);
743 let self_type = cx.ty_path(path);
744 let rustc_const_unstable =
745 cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span));
746
747 let mut attrs = {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::automatically_derived, self.span));
vec
}thin_vec![cx.attr_word(sym::automatically_derived, self.span),];
748
749 if self.is_const && cx.ecfg.features.staged_api() {
753 attrs.push(
755 cx.attr_nested(
756 rustc_ast::AttrItem {
757 unsafety: Safety::Default,
758 path: rustc_const_unstable,
759 args: AttrArgs::Delimited(DelimArgs {
760 dspan: DelimSpan::from_single(self.span),
761 delim: rustc_ast::token::Delimiter::Parenthesis,
762 tokens: [
763 TokenKind::Ident(sym::feature, IdentKind::Normal),
764 TokenKind::Eq,
765 TokenKind::lit(LitKind::Str, sym::derive_const, None),
766 TokenKind::Comma,
767 TokenKind::Ident(sym::issue, IdentKind::Normal),
768 TokenKind::Eq,
769 TokenKind::lit(LitKind::Str, sym::derive_const_issue, None),
770 ]
771 .into_iter()
772 .map(|kind| {
773 TokenTree::Token(Token { kind, span: self.span }, Spacing::Alone)
774 })
775 .collect(),
776 }),
777 span: self.span,
778 },
779 self.span,
780 ),
781 )
782 }
783
784 if !self.document {
785 attrs.push(cx.attr_nested_word(sym::doc, sym::hidden, self.span));
786 }
787
788 cx.item(
789 self.span,
790 attrs,
791 ast::ItemKind::Impl(ast::Impl {
792 generics: trait_generics,
793 of_trait: Some(Box::new(ast::TraitImplHeader {
794 safety: self.safety,
795 polarity: ast::ImplPolarity::Positive,
796 defaultness: ast::Defaultness::Implicit,
797 trait_ref,
798 })),
799 constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No },
800 self_ty: self_type,
801 items: methods.chain(associated_types).collect(),
802 }),
803 )
804 }
805
806 fn expand_struct_def(
807 &self,
808 cx: &ExtCtxt<'_>,
809 struct_def: &'a VariantData,
810 type_ident: Ident,
811 generics: &Generics,
812 from_scratch: bool,
813 is_packed: bool,
814 ) -> Box<ast::Item> {
815 let field_tys = struct_def.fields().iter().map(|field| &*field.ty);
816
817 let methods = self.methods.iter().filter_map(|method_def| {
818 let body = if from_scratch || method_def.is_static() {
819 method_def.call_substructure_method(cx, self, StaticStruct(struct_def))
820 } else {
821 method_def.expand_struct_method_body(cx, self, struct_def, is_packed)
822 };
823
824 method_def.create_method(cx, self, body)
825 });
826
827 self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed)
828 }
829
830 fn expand_enum_def(
831 &self,
832 cx: &ExtCtxt<'_>,
833 enum_def: &'a EnumDef,
834 type_ident: Ident,
835 generics: &Generics,
836 from_scratch: bool,
837 ) -> Box<ast::Item> {
838 let field_tys = enum_def
839 .variants
840 .iter()
841 .flat_map(|variant| variant.data.fields())
842 .map(|field| &*field.ty);
843
844 let methods = self.methods.iter().filter_map(|method_def| {
845 let body = if from_scratch || method_def.is_static() {
846 method_def.call_substructure_method(cx, self, StaticEnum(enum_def))
847 } else {
848 method_def.expand_enum_method_body(cx, self, enum_def, type_ident)
849 };
850
851 method_def.create_method(cx, self, body)
852 });
853
854 let is_packed = false; self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed)
856 }
857}
858
859impl<'a> MethodDef<'a> {
860 fn call_substructure_method(
861 &self,
862 cx: &ExtCtxt<'_>,
863 trait_: &TraitDef<'_>,
864 substructure: Substructure<'_>,
865 ) -> BlockOrExpr {
866 (self.combine_substructure)(cx, trait_.span, substructure)
867 }
868
869 fn is_static(&self) -> bool {
870 !self.explicit_self
871 }
872
873 fn get_selflike_args(&self, cx: &ExtCtxt<'_>, trait_: &TraitDef<'_>) -> ThinVec<Box<Expr>> {
876 if !self.explicit_self {
::core::panicking::panic("assertion failed: self.explicit_self")
};assert!(self.explicit_self);
877
878 let span = trait_.span;
879
880 once(cx.expr_self(span))
881 .chain(self.nonself_args.iter().filter_map(|(ty, name)| match ty {
882 Ref(Self_, _) => Some(cx.expr_ident(span, Ident::new(*name, span))),
883 _ => None,
884 }))
885 .collect()
886 }
887
888 fn create_method(
889 &self,
890 cx: &ExtCtxt<'_>,
891 trait_: &TraitDef<'_>,
892 body: BlockOrExpr,
893 ) -> Option<Box<ast::AssocItem>> {
894 if body.0.is_empty() && body.1.is_none() && self.name == sym::assert_fields_are_eq {
896 return None;
897 }
898 let span = trait_.span;
899 let fn_generics = self.generics.clone();
901
902 let self_arg = self.explicit_self.then(|| {
903 let ident = Ident::new(kw::SelfLower, span);
904 ast::Param::from_self(
905 ast::AttrVec::default(),
906 respan(span, SelfKind::Region(None, ast::Mutability::Not)),
907 ident,
908 )
909 });
910 let args = self_arg
911 .into_iter()
912 .chain(self.nonself_args.iter().map(|(ty, name)| {
913 let ast_ty = ty.to_ty(cx, span);
914 let ident = Ident::new(*name, span);
915 cx.param(span, ident, ast_ty)
916 }))
917 .collect();
918
919 let ret_type = if let Ty::Unit = &self.ret_ty {
920 ast::FnRetTy::Default(span)
921 } else {
922 ast::FnRetTy::Ty(self.ret_ty.to_ty(cx, span))
923 };
924
925 let method_ident = Ident::new(self.name, span);
926 let fn_decl = cx.fn_decl(args, ret_type);
927 let body_block = body.into_block(cx, span);
928
929 let trait_lo_sp = span.shrink_to_lo();
930
931 let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span };
932 let defaultness = ast::Defaultness::Implicit;
933
934 Some(Box::new(ast::AssocItem {
936 id: ast::DUMMY_NODE_ID,
937 attrs: self.attributes.clone(),
938 span,
939 vis: ast::Visibility { span: trait_lo_sp, kind: ast::VisibilityKind::Inherited },
940 kind: ast::AssocItemKind::Fn(Box::new(ast::Fn {
941 defaultness,
942 sig,
943 ident: method_ident,
944 generics: fn_generics,
945 contract: None,
946 body: Some(body_block),
947 define_opaque: None,
948 eii_impl: None,
949 })),
950 tokens: None,
951 }))
952 }
953
954 fn expand_struct_method_body<'b>(
990 &self,
991 cx: &ExtCtxt<'_>,
992 trait_: &TraitDef<'b>,
993 struct_def: &'b VariantData,
994 is_packed: bool,
995 ) -> BlockOrExpr {
996 let selflike_args = self.get_selflike_args(cx, trait_);
997
998 let selflike_fields =
999 trait_.create_struct_field_access_fields(cx, &selflike_args, struct_def, is_packed);
1000 self.call_substructure_method(cx, trait_, Struct(struct_def, selflike_fields))
1001 }
1002
1003 fn expand_enum_method_body<'b>(
1039 &self,
1040 cx: &ExtCtxt<'_>,
1041 trait_: &TraitDef<'b>,
1042 enum_def: &'b EnumDef,
1043 type_ident: Ident,
1044 ) -> BlockOrExpr {
1045 let span = trait_.span;
1046 let variants = &enum_def.variants;
1047
1048 let unify_fieldless_variants =
1050 self.fieldless_variants_strategy == FieldlessVariantsStrategy::Unify;
1051
1052 if variants.is_empty() {
1056 let match_arg = cx.expr_deref(span, cx.expr_self(span));
1057 let match_arms = ThinVec::new();
1058 let expr = cx.expr_match(span, match_arg, match_arms);
1059 return BlockOrExpr(ThinVec::new(), Some(expr));
1060 }
1061
1062 let selflike_args = self.get_selflike_args(cx, trait_);
1063
1064 let prefixes = iter::once("__self".to_string())
1065 .chain((1..selflike_args.len()).map(|arg_count| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("__arg{0}", arg_count))
})format!("__arg{arg_count}")))
1066 .collect::<Vec<String>>();
1067
1068 let get_discr_pieces = || {
1077 let discr_idents = prefixes
1078 .iter()
1079 .map(|name| Ident::from_str_and_span(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}_discr", name))
})format!("{name}_discr"), span));
1080
1081 let mut discr_exprs =
1082 discr_idents.clone().map(|ident| cx.expr_addr_of(span, cx.expr_ident(span, ident)));
1083
1084 let self_expr = discr_exprs.next().unwrap();
1085 let other_selflike_expr = discr_exprs.next();
1086 if true {
if !discr_exprs.next().is_none() {
::core::panicking::panic("assertion failed: discr_exprs.next().is_none()")
};
};debug_assert!(discr_exprs.next().is_none());
1087
1088 let discr_field =
1089 FieldInfo { span, name: None, self_expr, other_selflike_expr, maybe_scalar: true };
1090
1091 let discr_let_stmts: ThinVec<_> = iter::zip(discr_idents, &selflike_args)
1092 .map(|(ident, selflike_arg)| {
1093 let variant_value = deriving::call_intrinsic(
1094 cx,
1095 span,
1096 sym::discriminant_value,
1097 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(selflike_arg.clone());
vec
}thin_vec![selflike_arg.clone()],
1098 );
1099 cx.stmt_let(span, false, ident, variant_value)
1100 })
1101 .collect();
1102
1103 (discr_field, discr_let_stmts)
1104 };
1105
1106 let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty());
1109 if all_fieldless {
1110 if variants.len() > 1 {
1111 match self.fieldless_variants_strategy {
1112 FieldlessVariantsStrategy::Unify => {
1113 let (discr_field, mut discr_let_stmts) = get_discr_pieces();
1117 let mut discr_check =
1118 self.call_substructure_method(cx, trait_, EnumDiscr(discr_field, None));
1119 discr_let_stmts.append(&mut discr_check.0);
1120 return BlockOrExpr(discr_let_stmts, discr_check.1);
1121 }
1122 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless => {
1123 return self.call_substructure_method(
1124 cx,
1125 trait_,
1126 AllFieldlessEnum(enum_def),
1127 );
1128 }
1129 FieldlessVariantsStrategy::Default => (),
1130 }
1131 } else if let [variant] = variants.as_slice() {
1132 return self.call_substructure_method(
1135 cx,
1136 trait_,
1137 EnumMatching(variant, Vec::new()),
1138 );
1139 }
1140 }
1141
1142 let mut match_arms: ThinVec<ast::Arm> = variants
1148 .iter()
1149 .filter(|&v| !(unify_fieldless_variants && v.data.fields().is_empty()))
1150 .map(|variant| {
1151 let fields = trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes);
1155
1156 let sp = variant.span.with_ctxt(trait_.span.ctxt());
1157 let variant_path = cx.path(sp, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[type_ident, variant.ident]))vec![type_ident, variant.ident]);
1158 let mut subpats =
1159 trait_.create_struct_patterns(cx, variant_path, &variant.data, &prefixes);
1160
1161 let single_pat = if subpats.len() == 1 {
1163 subpats.pop().unwrap()
1164 } else {
1165 cx.pat_tuple(span, subpats)
1166 };
1167
1168 let substructure = EnumMatching(variant, fields);
1177 let arm_expr =
1178 self.call_substructure_method(cx, trait_, substructure).into_expr(cx, span);
1179
1180 cx.arm(span, single_pat, arm_expr)
1181 })
1182 .collect();
1183
1184 let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
1186 let default = match first_fieldless {
1187 Some(v) if unify_fieldless_variants => {
1188 Some(
1192 self.call_substructure_method(cx, trait_, EnumMatching(v, Vec::new()))
1193 .into_expr(cx, span),
1194 )
1195 }
1196 _ if variants.len() > 1 && selflike_args.len() > 1 => {
1197 Some(deriving::call_unreachable(cx, span))
1201 }
1202 _ => None,
1203 };
1204 if let Some(arm) = default {
1205 match_arms.push(cx.arm(span, cx.pat_wild(span), arm));
1206 }
1207
1208 let get_match_expr = |mut selflike_args: ThinVec<Box<Expr>>| {
1217 let match_arg = if selflike_args.len() == 1 {
1218 selflike_args.pop().unwrap()
1219 } else {
1220 cx.expr(span, ast::ExprKind::Tup(selflike_args))
1221 };
1222 cx.expr_match(span, match_arg, match_arms)
1223 };
1224
1225 if unify_fieldless_variants && variants.len() > 1 {
1229 let (discr_field, mut discr_let_stmts) = get_discr_pieces();
1230
1231 let mut discr_check_plus_match = self.call_substructure_method(
1233 cx,
1234 trait_,
1235 EnumDiscr(discr_field, Some(get_match_expr(selflike_args))),
1236 );
1237 discr_let_stmts.append(&mut discr_check_plus_match.0);
1238 BlockOrExpr(discr_let_stmts, discr_check_plus_match.1)
1239 } else {
1240 BlockOrExpr(ThinVec::new(), Some(get_match_expr(selflike_args)))
1241 }
1242 }
1243}
1244
1245impl<'a> TraitDef<'a> {
1247 fn create_struct_patterns(
1248 &self,
1249 cx: &ExtCtxt<'_>,
1250 struct_path: ast::Path,
1251 struct_def: &'a VariantData,
1252 prefixes: &[String],
1253 ) -> ThinVec<ast::Pat> {
1254 prefixes
1255 .iter()
1256 .map(|prefix| {
1257 let pieces_iter =
1258 struct_def.fields().iter().enumerate().map(|(i, struct_field)| {
1259 let sp = struct_field.span.with_ctxt(self.span.ctxt());
1260 let ident = self.mk_pattern_ident(prefix, i);
1261 let path = ident.with_span_pos(sp);
1262 (struct_field.ident, cx.pat_ident(path.span, path))
1263 });
1264
1265 let struct_path = struct_path.clone();
1266 match *struct_def {
1267 VariantData::Struct { .. } => {
1268 let field_pats = pieces_iter
1269 .map(|(ident, pat)| ast::PatField {
1270 ident: ident
1271 .expect("a braced struct with unnamed fields in `derive`"),
1272 is_shorthand: false,
1273 attrs: ast::AttrVec::new(),
1274 id: ast::DUMMY_NODE_ID,
1275 span: pat.span.with_ctxt(self.span.ctxt()),
1276 pat: Box::new(pat),
1277 is_placeholder: false,
1278 })
1279 .collect();
1280 cx.pat_struct(self.span, struct_path, field_pats)
1281 }
1282 VariantData::Tuple(..) => {
1283 let subpats = pieces_iter.map(|(_, subpat)| subpat).collect();
1284 cx.pat_tuple_struct(self.span, struct_path, subpats)
1285 }
1286 VariantData::Unit(..) => cx.pat_path(self.span, struct_path),
1287 }
1288 })
1289 .collect()
1290 }
1291
1292 fn create_fields<F>(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec<FieldInfo>
1293 where
1294 F: Fn(usize, &ast::FieldDef, Span) -> Vec<Box<ast::Expr>>,
1295 {
1296 struct_def
1297 .fields()
1298 .iter()
1299 .enumerate()
1300 .map(|(i, struct_field)| {
1301 let span = struct_field.span.with_ctxt(self.span.ctxt());
1304 let mut exprs: Vec<_> = mk_exprs(i, struct_field, span);
1305 let self_expr = exprs.remove(0);
1306 if true {
if !(exprs.len() <= 1) {
::core::panicking::panic("assertion failed: exprs.len() <= 1")
};
};debug_assert!(exprs.len() <= 1);
1307 FieldInfo {
1308 span,
1309 name: struct_field.ident,
1310 self_expr,
1311 other_selflike_expr: exprs.pop(),
1312 maybe_scalar: struct_field.ty.peel_refs().kind.maybe_scalar(),
1313 }
1314 })
1315 .collect()
1316 }
1317
1318 fn mk_pattern_ident(&self, prefix: &str, i: usize) -> Ident {
1319 Ident::from_str_and_span(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}_{1}", prefix, i))
})format!("{prefix}_{i}"), self.span)
1320 }
1321
1322 fn create_struct_pattern_fields(
1323 &self,
1324 cx: &ExtCtxt<'_>,
1325 struct_def: &'a VariantData,
1326 prefixes: &[String],
1327 ) -> Vec<FieldInfo> {
1328 self.create_fields(struct_def, |i, _struct_field, sp| {
1329 prefixes
1330 .iter()
1331 .map(|prefix| {
1332 let ident = self.mk_pattern_ident(prefix, i);
1333 cx.expr_path(cx.path_ident(sp, ident))
1334 })
1335 .collect()
1336 })
1337 }
1338
1339 fn create_struct_field_access_fields(
1340 &self,
1341 cx: &ExtCtxt<'_>,
1342 selflike_args: &[Box<Expr>],
1343 struct_def: &'a VariantData,
1344 is_packed: bool,
1345 ) -> Vec<FieldInfo> {
1346 self.create_fields(struct_def, |i, struct_field, sp| {
1347 selflike_args
1348 .iter()
1349 .map(|selflike_arg| {
1350 let mut field_expr = cx.expr(
1355 sp,
1356 ast::ExprKind::Field(
1357 selflike_arg.clone(),
1358 struct_field.ident.unwrap_or_else(|| {
1359 Ident::from_str_and_span(&i.to_string(), struct_field.span)
1360 }),
1361 ),
1362 );
1363 if is_packed {
1364 field_expr = cx.expr_block(
1367 cx.block(struct_field.span, {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.stmt_expr(field_expr));
vec
}thin_vec![cx.stmt_expr(field_expr)]),
1368 );
1369 }
1370 cx.expr_addr_of(sp, field_expr)
1371 })
1372 .collect()
1373 })
1374 }
1375}
1376
1377pub(crate) fn cs_foldr(
1380 cx: &ExtCtxt<'_>,
1381 trait_span: Span,
1382 substructure: Substructure<'_>,
1383 single: impl Fn(FieldInfo) -> Box<Expr>,
1386 combine: impl Fn(Span, Box<Expr>, Box<Expr>) -> Box<Expr>,
1389 fieldless: impl Fn() -> Box<Expr>,
1391) -> Box<Expr> {
1392 match substructure {
1393 EnumMatching(.., all_fields) | Struct(_, all_fields) => {
1394 let mut fields = all_fields.into_iter();
1395 let base_field = fields.next_back();
1396
1397 let Some(base_field) = base_field else {
1398 return fieldless();
1399 };
1400
1401 let base_expr = single(base_field);
1402
1403 let op = |old, field: FieldInfo| {
1404 let span = field.span;
1405 let new = single(field);
1406 combine(span, old, new)
1407 };
1408
1409 fields.rfold(base_expr, op)
1410 }
1411 EnumDiscr(discr_field, match_expr) => {
1412 let discr_check_expr = single(discr_field);
1413 if let Some(match_expr) = match_expr {
1414 combine(trait_span, match_expr, discr_check_expr)
1415 } else {
1416 discr_check_expr
1417 }
1418 }
1419 _ => cx.dcx().span_bug(trait_span, "unexpected substructure in `derive`"),
1420 }
1421}