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