Skip to main content

rustc_builtin_macros/deriving/
clone.rs

1use 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    // The simple form is `fn clone(&self) -> Self { *self }`, possibly with
19    // some additional `AssertParamIsClone` assertions.
20    //
21    // We can use the simple form if either of the following are true.
22    // - The type derives Copy and there are no generic parameters. (If we
23    //   used the simple form with generics, we'd have to bound the generics
24    //   with Clone + Copy, and then there'd be no Clone impl at all if the
25    //   user fills in something that is Clone but not Copy. After
26    //   specialization we can remove this no-generics limitation.)
27    // - The item is a union. (Unions with generic parameters still can derive
28    //   Clone because they require Copy for deriving, Clone alone is not
29    //   enough. Whether Clone is implemented for fields is irrelevant so we
30    //   don't assert it.)
31    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 the clone method is just copying the value, also mark the type as
61    // `TrivialClone` to allow some library optimizations.
62    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            // `TrivialClone` is not part of an API guarantee, so it shouldn't
75            // appear in rustdoc output.
76            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            // This basic redundancy checking only prevents duplication of
119            // assertions like `AssertParamIsClone<Foo>` where the type is a
120            // simple name. That's enough to get a lot of cases, though.
121            if let Some(name) = field.ty.kind.is_simple_path()
122                && !seen_type_names.insert(name)
123            {
124                // Already produced an assertion for this type.
125                // Anonymous structs or unions must be eliminated as they cannot be
126                // type parameters.
127            } else {
128                // let _: AssertParamIsClone<FieldTy>;
129                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        // Just a single assertion for unions, that the union impls `Copy`.
142        // let _: AssertParamIsCopy<Self>;
143        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}