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