Skip to main content

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                bounds = ::smallvec::SmallVec::new()smallvec![];
42                if has_derive_copy
43                    && !params
44                        .iter()
45                        .any(|param| #[allow(non_exhaustive_omitted_patterns)] match param.kind {
    ast::GenericParamKind::Type { .. } => true,
    _ => false,
}matches!(param.kind, ast::GenericParamKind::Type { .. }))
46                {
47                    is_simple = true;
48                    substructure =
49                        combine_substructure(|c, s, sub| cs_clone_simple(c, s, sub, false));
50                } else {
51                    is_simple = false;
52                    substructure = combine_substructure(cs_clone);
53                }
54            }
55            ItemKind::Union(..) => {
56                bounds = {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(Path(generic::ty::Path::new({
                        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                                [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(),
                    [Path(generic::ty::Path::new({
                                        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                                                [sym::marker, sym::Copy]))
                                    }))])))
    }
}smallvec![Path(path_std!(marker::Copy))];
57                is_simple = true;
58                substructure = combine_substructure(|c, s, sub| cs_clone_simple(c, s, sub, true));
59            }
60            _ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on wrong item kind"),
61        },
62
63        _ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
64    }
65
66    // If the clone method is just copying the value, also mark the type as
67    // `TrivialClone` to allow some library optimizations.
68    if is_simple {
69        let trivial_def = TraitDef {
70            span,
71            path: generic::ty::Path::new({
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [sym::clone, sym::TrivialClone]))
    })path_std!(clone::TrivialClone),
72            skip_path_as_bound: false,
73            needs_copy_as_bound_if_packed: true,
74            additional_bounds: bounds.clone(),
75            supports_unions: true,
76            methods: SmallVec::new(),
77            associated_types: SmallVec::new(),
78            is_const,
79            safety: Safety::Unsafe(DUMMY_SP),
80            // `TrivialClone` is not part of an API guarantee, so it shouldn't
81            // appear in rustdoc output.
82            document: false,
83        };
84
85        trivial_def.expand_ext(cx, mitem, item, push, true);
86    }
87
88    let trait_def = TraitDef {
89        span,
90        path: generic::ty::Path::new({
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [sym::clone, sym::Clone]))
    })path_std!(clone::Clone),
91        skip_path_as_bound: false,
92        needs_copy_as_bound_if_packed: true,
93        additional_bounds: bounds,
94        supports_unions: true,
95        methods: {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(MethodDef {
                name: sym::clone,
                generics: Bounds::empty(),
                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: Bounds::empty(),
                                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 {
96            name: sym::clone,
97            generics: Bounds::empty(),
98            explicit_self: true,
99            nonself_args: SmallVec::new(),
100            ret_ty: Self_,
101            attributes: thin_vec![cx.attr_word(sym::inline, span)],
102            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
103            combine_substructure: substructure,
104        }],
105        associated_types: SmallVec::new(),
106        is_const,
107        safety: Safety::Default,
108        document: true,
109    };
110
111    trait_def.expand_ext(cx, mitem, item, push, is_simple)
112}
113
114fn cs_clone_simple(
115    cx: &ExtCtxt<'_>,
116    trait_span: Span,
117    substr: &Substructure<'_>,
118    is_union: bool,
119) -> BlockOrExpr {
120    let mut stmts = ThinVec::new();
121    let mut seen_type_names = FxHashSet::default();
122    let mut process_variant = |variant: &VariantData| {
123        for field in variant.fields() {
124            // This basic redundancy checking only prevents duplication of
125            // assertions like `AssertParamIsClone<Foo>` where the type is a
126            // simple name. That's enough to get a lot of cases, though.
127            if let Some(name) = field.ty.kind.is_simple_path()
128                && !seen_type_names.insert(name)
129            {
130                // Already produced an assertion for this type.
131                // Anonymous structs or unions must be eliminated as they cannot be
132                // type parameters.
133            } else {
134                // let _: AssertParamIsClone<FieldTy>;
135                super::assert_ty_bounds(
136                    cx,
137                    &mut stmts,
138                    field.ty.clone(),
139                    field.span,
140                    &[sym::clone, sym::AssertParamIsClone],
141                );
142            }
143        }
144    };
145
146    if is_union {
147        // Just a single assertion for unions, that the union impls `Copy`.
148        // let _: AssertParamIsCopy<Self>;
149        let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper)));
150        super::assert_ty_bounds(
151            cx,
152            &mut stmts,
153            self_ty,
154            trait_span,
155            &[sym::clone, sym::AssertParamIsCopy],
156        );
157    } else {
158        match *substr.fields {
159            StaticStruct(vdata, ..) => {
160                process_variant(vdata);
161            }
162            StaticEnum(enum_def, ..) => {
163                for variant in &enum_def.variants {
164                    process_variant(&variant.data);
165                }
166            }
167            _ => cx.dcx().span_bug(trait_span, "unexpected substructure in simple `derive(Clone)`"),
168        }
169    }
170    BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span))))
171}
172
173fn cs_clone(cx: &ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
174    let ctor_path;
175    let all_fields;
176    let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
177    let subcall = |cx: &ExtCtxt<'_>, field: &FieldInfo| {
178        let args = {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(field.self_expr.clone());
    vec
}thin_vec![field.self_expr.clone()];
179        cx.expr_call_global(field.span, fn_path.clone(), args)
180    };
181
182    let vdata;
183    match substr.fields {
184        Struct(vdata_, 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(),
        [substr.type_ident]))vec![substr.type_ident]);
186            all_fields = af;
187            vdata = *vdata_;
188        }
189        EnumMatching(.., variant, af) => {
190            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]);
191            all_fields = af;
192            vdata = &variant.data;
193        }
194        EnumDiscr(..) | AllFieldlessEnum(..) => {
195            cx.dcx().span_bug(trait_span, "enum discriminants in `derive(Clone)`")
196        }
197        StaticEnum(..) | StaticStruct(..) => {
198            cx.dcx().span_bug(trait_span, "associated function in `derive(Clone)`")
199        }
200    }
201
202    let expr = match *vdata {
203        VariantData::Struct { .. } => {
204            let fields = all_fields
205                .iter()
206                .map(|field| {
207                    let Some(ident) = field.name else {
208                        cx.dcx().span_bug(
209                            trait_span,
210                            "unnamed field in normal struct in `derive(Clone)`",
211                        );
212                    };
213                    let call = subcall(cx, field);
214                    cx.field_imm(field.span, ident, call)
215                })
216                .collect::<ThinVec<_>>();
217
218            cx.expr_struct(trait_span, ctor_path, fields)
219        }
220        VariantData::Tuple(..) => {
221            let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
222            let path = cx.expr_path(ctor_path);
223            cx.expr_call(trait_span, path, subcalls)
224        }
225        VariantData::Unit(..) => cx.expr_path(ctor_path),
226    };
227    BlockOrExpr::new_expr(expr)
228}