rustc_builtin_macros/deriving/
clone.rs1use rustc_ast::{self as ast, Generics, ItemKind, Safety, VariantData};
2use rustc_data_structures::fx::FxHashSet;
3use rustc_expand::base::ExtCtxt;
4use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
5use thin_vec::{ThinVec, thin_vec};
6
7use crate::deriving::generic::ty::*;
8use crate::deriving::generic::*;
9use crate::deriving::path_std;
10
11pub(crate) fn expand_deriving_clone(
12 cx: &ExtCtxt<'_>,
13 span: Span,
14 item: &ast::Item,
15 push: &mut dyn FnMut(Box<ast::Item>),
16 is_const: bool,
17) {
18 let bounds;
32 let substructure;
33 let is_simple;
34 match &item.kind {
35 ItemKind::Struct(_, Generics { params, .. }, _)
36 | ItemKind::Enum(_, Generics { params, .. }, _) => {
37 let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
38 let has_derive_copy = cx.resolver.has_derive_copy(container_id);
39 bounds = ::smallvec::SmallVec::new()smallvec![];
40 if has_derive_copy
41 && !params
42 .iter()
43 .any(|param| #[allow(non_exhaustive_omitted_patterns)] match param.kind {
ast::GenericParamKind::Type { .. } => true,
_ => false,
}matches!(param.kind, ast::GenericParamKind::Type { .. }))
44 {
45 is_simple = true;
46 substructure = combine_substructure(|c, s, sub| cs_clone_simple(c, s, sub, false));
47 } else {
48 is_simple = false;
49 substructure = combine_substructure(cs_clone);
50 }
51 }
52 ItemKind::Union(..) => {
53 bounds = {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(generic::ty::new_path(cx, span,
{ &[sym::marker, sym::Copy] }, &[]));
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[generic::ty::new_path(cx, span,
{ &[sym::marker, sym::Copy] }, &[])])))
}
}smallvec![path_std!(cx, span, marker::Copy)];
54 is_simple = true;
55 substructure = combine_substructure(|c, s, sub| cs_clone_simple(c, s, sub, true));
56 }
57 _ => cx.dcx().span_bug(span, "`derive(Clone)` on wrong item kind"),
58 }
59
60 if is_simple {
63 let trivial_def = TraitDef {
64 span,
65 path: generic::ty::new_path(cx, span, { &[sym::clone, sym::TrivialClone] }, &[])path_std!(cx, span, clone::TrivialClone),
66 skip_path_as_bound: false,
67 needs_copy_as_bound_if_packed: true,
68 additional_bounds: bounds.clone(),
69 supports_unions: true,
70 methods: SmallVec::new(),
71 associated_types: SmallVec::new(),
72 is_const,
73 safety: Safety::Unsafe(DUMMY_SP),
74 document: false,
77 };
78
79 trivial_def.expand(cx, item, push);
80 }
81
82 let trait_def = TraitDef {
83 span,
84 path: generic::ty::new_path(cx, span, { &[sym::clone, sym::Clone] }, &[])path_std!(cx, span, clone::Clone),
85 skip_path_as_bound: false,
86 needs_copy_as_bound_if_packed: true,
87 additional_bounds: bounds,
88 supports_unions: true,
89 methods: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(MethodDef {
name: sym::clone,
generics: cx.empty_generics(span),
explicit_self: true,
nonself_args: SmallVec::new(),
ret_ty: Self_,
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::inline, span));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: substructure,
});
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[MethodDef {
name: sym::clone,
generics: cx.empty_generics(span),
explicit_self: true,
nonself_args: SmallVec::new(),
ret_ty: Self_,
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::inline, span));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: substructure,
}])))
}
}smallvec![MethodDef {
90 name: sym::clone,
91 generics: cx.empty_generics(span),
92 explicit_self: true,
93 nonself_args: SmallVec::new(),
94 ret_ty: Self_,
95 attributes: thin_vec![cx.attr_word(sym::inline, span)],
96 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
97 combine_substructure: substructure,
98 }],
99 associated_types: SmallVec::new(),
100 is_const,
101 safety: Safety::Default,
102 document: true,
103 };
104
105 trait_def.expand_ext(cx, item, push, is_simple)
106}
107
108fn cs_clone_simple(
109 cx: &ExtCtxt<'_>,
110 trait_span: Span,
111 substr: Substructure<'_>,
112 is_union: bool,
113) -> BlockOrExpr {
114 let mut stmts = ThinVec::new();
115 let mut seen_type_names = FxHashSet::default();
116 let mut process_variant = |variant: &VariantData| {
117 for field in variant.fields() {
118 if let Some(name) = field.ty.kind.is_simple_path()
122 && !seen_type_names.insert(name)
123 {
124 } else {
128 super::assert_ty_bounds(
130 cx,
131 &mut stmts,
132 field.ty.clone(),
133 field.span,
134 &[sym::clone, sym::AssertParamIsClone],
135 );
136 }
137 }
138 };
139
140 if is_union {
141 let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper)));
144 super::assert_ty_bounds(
145 cx,
146 &mut stmts,
147 self_ty,
148 trait_span,
149 &[sym::clone, sym::AssertParamIsCopy],
150 );
151 } else {
152 match substr {
153 StaticStruct(vdata, ..) => {
154 process_variant(vdata);
155 }
156 StaticEnum(enum_def, ..) => {
157 for variant in &enum_def.variants {
158 process_variant(&variant.data);
159 }
160 }
161 _ => cx.dcx().span_bug(trait_span, "unexpected substructure in simple `derive(Clone)`"),
162 }
163 }
164 BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span))))
165}
166
167fn cs_clone(cx: &ExtCtxt<'_>, trait_span: Span, substr: Substructure<'_>) -> BlockOrExpr {
168 let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
169 let subcall = |field: FieldInfo| {
170 let args = {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(field.self_expr);
vec
}thin_vec![field.self_expr];
171 cx.expr_call_global(field.span, fn_path.clone(), args)
172 };
173
174 let self_ident = Ident::new(kw::SelfUpper, trait_span);
175 let ctor_path;
176 let all_fields;
177 let vdata;
178 match substr {
179 Struct(vdata_, af) => {
180 ctor_path = cx.path(trait_span, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[self_ident]))vec![self_ident]);
181 all_fields = af;
182 vdata = vdata_;
183 }
184 EnumMatching(.., variant, af) => {
185 ctor_path = cx.path(trait_span, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[self_ident, variant.ident]))vec![self_ident, variant.ident]);
186 all_fields = af;
187 vdata = &variant.data;
188 }
189 _ => cx.dcx().span_bug(trait_span, "unexpected substructure in `derive(Clone)`"),
190 }
191
192 let expr = match *vdata {
193 VariantData::Struct { .. } => {
194 let fields = all_fields
195 .into_iter()
196 .map(|field| cx.field_imm(field.span, field.name.unwrap(), subcall(field)))
197 .collect::<ThinVec<_>>();
198
199 cx.expr_struct(trait_span, ctor_path, fields)
200 }
201 VariantData::Tuple(..) => {
202 let subcalls = all_fields.into_iter().map(subcall).collect();
203 let path = cx.expr_path(ctor_path);
204 cx.expr_call(trait_span, path, subcalls)
205 }
206 VariantData::Unit(..) => cx.expr_path(ctor_path),
207 };
208 BlockOrExpr::new_expr(expr)
209}