rustc_builtin_macros/deriving/
clone.rs

1use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, Safety, VariantData};
2use rustc_data_structures::fx::FxHashSet;
3use rustc_expand::base::{Annotatable, 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    mitem: &MetaItem,
15    item: &Annotatable,
16    push: &mut dyn FnMut(Annotatable),
17    is_const: bool,
18) {
19    // The simple form is `fn clone(&self) -> Self { *self }`, possibly with
20    // some additional `AssertParamIsClone` assertions.
21    //
22    // We can use the simple form if either of the following are true.
23    // - The type derives Copy and there are no generic parameters. (If we
24    //   used the simple form with generics, we'd have to bound the generics
25    //   with Clone + Copy, and then there'd be no Clone impl at all if the
26    //   user fills in something that is Clone but not Copy. After
27    //   specialization we can remove this no-generics limitation.)
28    // - The item is a union. (Unions with generic parameters still can derive
29    //   Clone because they require Copy for deriving, Clone alone is not
30    //   enough. Whether Clone is implemented for fields is irrelevant so we
31    //   don't assert it.)
32    let bounds;
33    let substructure;
34    let is_simple;
35    match item {
36        Annotatable::Item(annitem) => match &annitem.kind {
37            ItemKind::Struct(_, Generics { params, .. }, _)
38            | ItemKind::Enum(_, Generics { params, .. }, _) => {
39                let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
40                let has_derive_copy = cx.resolver.has_derive_copy(container_id);
41                if has_derive_copy
42                    && !params
43                        .iter()
44                        .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. }))
45                {
46                    bounds = vec![];
47                    is_simple = true;
48                    substructure = combine_substructure(Box::new(|c, s, sub| {
49                        cs_clone_simple("Clone", c, s, sub, false)
50                    }));
51                } else {
52                    bounds = vec![];
53                    is_simple = false;
54                    substructure =
55                        combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
56                }
57            }
58            ItemKind::Union(..) => {
59                bounds = vec![Path(path_std!(marker::Copy))];
60                is_simple = true;
61                substructure = combine_substructure(Box::new(|c, s, sub| {
62                    cs_clone_simple("Clone", c, s, sub, true)
63                }));
64            }
65            _ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on wrong item kind"),
66        },
67
68        _ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
69    }
70
71    // If the clone method is just copying the value, also mark the type as
72    // `TrivialClone` to allow some library optimizations.
73    if is_simple {
74        let trivial_def = TraitDef {
75            span,
76            path: path_std!(clone::TrivialClone),
77            skip_path_as_bound: false,
78            needs_copy_as_bound_if_packed: true,
79            additional_bounds: bounds.clone(),
80            supports_unions: true,
81            methods: Vec::new(),
82            associated_types: Vec::new(),
83            is_const,
84            is_staged_api_crate: cx.ecfg.features.staged_api(),
85            safety: Safety::Unsafe(DUMMY_SP),
86            // `TrivialClone` is not part of an API guarantee, so it shouldn't
87            // appear in rustdoc output.
88            document: false,
89        };
90
91        trivial_def.expand_ext(cx, mitem, item, push, true);
92    }
93
94    let trait_def = TraitDef {
95        span,
96        path: path_std!(clone::Clone),
97        skip_path_as_bound: false,
98        needs_copy_as_bound_if_packed: true,
99        additional_bounds: bounds,
100        supports_unions: true,
101        methods: vec![MethodDef {
102            name: sym::clone,
103            generics: Bounds::empty(),
104            explicit_self: true,
105            nonself_args: Vec::new(),
106            ret_ty: Self_,
107            attributes: thin_vec![cx.attr_word(sym::inline, span)],
108            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
109            combine_substructure: substructure,
110        }],
111        associated_types: Vec::new(),
112        is_const,
113        is_staged_api_crate: cx.ecfg.features.staged_api(),
114        safety: Safety::Default,
115        document: true,
116    };
117
118    trait_def.expand_ext(cx, mitem, item, push, is_simple)
119}
120
121fn cs_clone_simple(
122    name: &str,
123    cx: &ExtCtxt<'_>,
124    trait_span: Span,
125    substr: &Substructure<'_>,
126    is_union: bool,
127) -> BlockOrExpr {
128    let mut stmts = ThinVec::new();
129    let mut seen_type_names = FxHashSet::default();
130    let mut process_variant = |variant: &VariantData| {
131        for field in variant.fields() {
132            // This basic redundancy checking only prevents duplication of
133            // assertions like `AssertParamIsClone<Foo>` where the type is a
134            // simple name. That's enough to get a lot of cases, though.
135            if let Some(name) = field.ty.kind.is_simple_path()
136                && !seen_type_names.insert(name)
137            {
138                // Already produced an assertion for this type.
139                // Anonymous structs or unions must be eliminated as they cannot be
140                // type parameters.
141            } else {
142                // let _: AssertParamIsClone<FieldTy>;
143                super::assert_ty_bounds(
144                    cx,
145                    &mut stmts,
146                    field.ty.clone(),
147                    field.span,
148                    &[sym::clone, sym::AssertParamIsClone],
149                );
150            }
151        }
152    };
153
154    if is_union {
155        // Just a single assertion for unions, that the union impls `Copy`.
156        // let _: AssertParamIsCopy<Self>;
157        let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper)));
158        super::assert_ty_bounds(
159            cx,
160            &mut stmts,
161            self_ty,
162            trait_span,
163            &[sym::clone, sym::AssertParamIsCopy],
164        );
165    } else {
166        match *substr.fields {
167            StaticStruct(vdata, ..) => {
168                process_variant(vdata);
169            }
170            StaticEnum(enum_def, ..) => {
171                for variant in &enum_def.variants {
172                    process_variant(&variant.data);
173                }
174            }
175            _ => cx.dcx().span_bug(
176                trait_span,
177                format!("unexpected substructure in simple `derive({name})`"),
178            ),
179        }
180    }
181    BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span))))
182}
183
184fn cs_clone(
185    name: &str,
186    cx: &ExtCtxt<'_>,
187    trait_span: Span,
188    substr: &Substructure<'_>,
189) -> BlockOrExpr {
190    let ctor_path;
191    let all_fields;
192    let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
193    let subcall = |cx: &ExtCtxt<'_>, field: &FieldInfo| {
194        let args = thin_vec![field.self_expr.clone()];
195        cx.expr_call_global(field.span, fn_path.clone(), args)
196    };
197
198    let vdata;
199    match substr.fields {
200        Struct(vdata_, af) => {
201            ctor_path = cx.path(trait_span, vec![substr.type_ident]);
202            all_fields = af;
203            vdata = *vdata_;
204        }
205        EnumMatching(.., variant, af) => {
206            ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]);
207            all_fields = af;
208            vdata = &variant.data;
209        }
210        EnumDiscr(..) | AllFieldlessEnum(..) => {
211            cx.dcx().span_bug(trait_span, format!("enum discriminants in `derive({name})`",))
212        }
213        StaticEnum(..) | StaticStruct(..) => {
214            cx.dcx().span_bug(trait_span, format!("associated function in `derive({name})`"))
215        }
216    }
217
218    let expr = match *vdata {
219        VariantData::Struct { .. } => {
220            let fields = all_fields
221                .iter()
222                .map(|field| {
223                    let Some(ident) = field.name else {
224                        cx.dcx().span_bug(
225                            trait_span,
226                            format!("unnamed field in normal struct in `derive({name})`",),
227                        );
228                    };
229                    let call = subcall(cx, field);
230                    cx.field_imm(field.span, ident, call)
231                })
232                .collect::<ThinVec<_>>();
233
234            cx.expr_struct(trait_span, ctor_path, fields)
235        }
236        VariantData::Tuple(..) => {
237            let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
238            let path = cx.expr_path(ctor_path);
239            cx.expr_call(trait_span, path, subcalls)
240        }
241        VariantData::Unit(..) => cx.expr_path(ctor_path),
242    };
243    BlockOrExpr::new_expr(expr)
244}