]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | use crate::deriving::generic::ty::*; |
dfeec247 XL |
2 | use crate::deriving::generic::*; |
3 | use crate::deriving::path_std; | |
064997fb FG |
4 | use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; |
5 | use rustc_data_structures::fx::FxHashSet; | |
dfeec247 | 6 | use rustc_expand::base::{Annotatable, ExtCtxt}; |
064997fb | 7 | use rustc_span::symbol::{kw, sym, Ident}; |
dfeec247 | 8 | use rustc_span::Span; |
9ffffee4 | 9 | use thin_vec::{thin_vec, ThinVec}; |
223e47cc | 10 | |
dfeec247 XL |
11 | pub fn expand_deriving_clone( |
12 | cx: &mut ExtCtxt<'_>, | |
13 | span: Span, | |
14 | mitem: &MetaItem, | |
15 | item: &Annotatable, | |
16 | push: &mut dyn FnMut(Annotatable), | |
487cf647 | 17 | is_const: bool, |
dfeec247 | 18 | ) { |
064997fb FG |
19 | // The simple form is `fn clone(&self) -> Self { *self }`, possibly with |
20 | // some additional `AssertParamIsClone` assertions. | |
a7813a04 | 21 | // |
064997fb | 22 | // We can use the simple form if either of the following are true. |
9c376795 | 23 | // - The type derives Copy and there are no generic parameters. (If we |
064997fb FG |
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.) | |
a7813a04 | 32 | let bounds; |
a7813a04 | 33 | let substructure; |
064997fb | 34 | let is_simple; |
487cf647 FG |
35 | match item { |
36 | Annotatable::Item(annitem) => match &annitem.kind { | |
37 | ItemKind::Struct(_, Generics { params, .. }) | |
38 | | ItemKind::Enum(_, Generics { params, .. }) => { | |
136023e0 XL |
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 | |
5869c6ff XL |
42 | && !params |
43 | .iter() | |
44 | .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) | |
dfeec247 XL |
45 | { |
46 | bounds = vec![]; | |
064997fb | 47 | is_simple = true; |
a7813a04 | 48 | substructure = combine_substructure(Box::new(|c, s, sub| { |
064997fb | 49 | cs_clone_simple("Clone", c, s, sub, false) |
a7813a04 | 50 | })); |
dfeec247 | 51 | } else { |
a7813a04 | 52 | bounds = vec![]; |
064997fb | 53 | is_simple = false; |
dfeec247 XL |
54 | substructure = |
55 | combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub))); | |
a7813a04 XL |
56 | } |
57 | } | |
dfeec247 | 58 | ItemKind::Union(..) => { |
064997fb FG |
59 | bounds = vec![Path(path_std!(marker::Copy))]; |
60 | is_simple = true; | |
dfeec247 | 61 | substructure = combine_substructure(Box::new(|c, s, sub| { |
064997fb | 62 | cs_clone_simple("Clone", c, s, sub, true) |
dfeec247 XL |
63 | })); |
64 | } | |
064997fb | 65 | _ => cx.span_bug(span, "`#[derive(Clone)]` on wrong item kind"), |
dfeec247 | 66 | }, |
a7813a04 | 67 | |
416331ca | 68 | _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"), |
a7813a04 XL |
69 | } |
70 | ||
487cf647 | 71 | let attrs = thin_vec![cx.attr_word(sym::inline, span)]; |
970d7e83 | 72 | let trait_def = TraitDef { |
3b2f2976 | 73 | span, |
3dfed10e | 74 | path: path_std!(clone::Clone), |
2b03887a | 75 | skip_path_as_bound: false, |
9ffffee4 | 76 | needs_copy_as_bound_if_packed: true, |
a7813a04 | 77 | additional_bounds: bounds, |
9e0c209e | 78 | supports_unions: true, |
5bcae85e | 79 | methods: vec![MethodDef { |
3dfed10e XL |
80 | name: sym::clone, |
81 | generics: Bounds::empty(), | |
064997fb FG |
82 | explicit_self: true, |
83 | nonself_args: Vec::new(), | |
dfeec247 XL |
84 | ret_ty: Self_, |
85 | attributes: attrs, | |
9c376795 | 86 | fieldless_variants_strategy: FieldlessVariantsStrategy::Default, |
dfeec247 XL |
87 | combine_substructure: substructure, |
88 | }], | |
85aaf69f | 89 | associated_types: Vec::new(), |
487cf647 | 90 | is_const, |
223e47cc LB |
91 | }; |
92 | ||
064997fb | 93 | trait_def.expand_ext(cx, mitem, item, push, is_simple) |
9e0c209e SL |
94 | } |
95 | ||
064997fb | 96 | fn cs_clone_simple( |
dfeec247 XL |
97 | name: &str, |
98 | cx: &mut ExtCtxt<'_>, | |
99 | trait_span: Span, | |
100 | substr: &Substructure<'_>, | |
101 | is_union: bool, | |
064997fb | 102 | ) -> BlockOrExpr { |
9ffffee4 | 103 | let mut stmts = ThinVec::new(); |
064997fb FG |
104 | let mut seen_type_names = FxHashSet::default(); |
105 | let mut process_variant = |variant: &VariantData| { | |
9e0c209e | 106 | for field in variant.fields() { |
064997fb FG |
107 | // This basic redundancy checking only prevents duplication of |
108 | // assertions like `AssertParamIsClone<Foo>` where the type is a | |
109 | // simple name. That's enough to get a lot of cases, though. | |
110 | if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) { | |
111 | // Already produced an assertion for this type. | |
112 | } else { | |
113 | // let _: AssertParamIsClone<FieldTy>; | |
114 | super::assert_ty_bounds( | |
115 | cx, | |
116 | &mut stmts, | |
117 | field.ty.clone(), | |
118 | field.span, | |
119 | &[sym::clone, sym::AssertParamIsClone], | |
120 | ); | |
121 | } | |
9e0c209e | 122 | } |
064997fb | 123 | }; |
9e0c209e | 124 | |
9e0c209e | 125 | if is_union { |
064997fb | 126 | // Just a single assertion for unions, that the union impls `Copy`. |
9e0c209e | 127 | // let _: AssertParamIsCopy<Self>; |
f9f354fc | 128 | let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); |
064997fb FG |
129 | super::assert_ty_bounds( |
130 | cx, | |
131 | &mut stmts, | |
132 | self_ty, | |
133 | trait_span, | |
134 | &[sym::clone, sym::AssertParamIsCopy], | |
135 | ); | |
9e0c209e SL |
136 | } else { |
137 | match *substr.fields { | |
138 | StaticStruct(vdata, ..) => { | |
064997fb | 139 | process_variant(vdata); |
9e0c209e SL |
140 | } |
141 | StaticEnum(enum_def, ..) => { | |
142 | for variant in &enum_def.variants { | |
064997fb | 143 | process_variant(&variant.data); |
9e0c209e SL |
144 | } |
145 | } | |
dfeec247 XL |
146 | _ => cx.span_bug( |
147 | trait_span, | |
064997fb | 148 | &format!("unexpected substructure in simple `derive({})`", name), |
dfeec247 | 149 | ), |
9e0c209e SL |
150 | } |
151 | } | |
064997fb | 152 | BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span)))) |
223e47cc LB |
153 | } |
154 | ||
dfeec247 XL |
155 | fn cs_clone( |
156 | name: &str, | |
157 | cx: &mut ExtCtxt<'_>, | |
158 | trait_span: Span, | |
159 | substr: &Substructure<'_>, | |
064997fb | 160 | ) -> BlockOrExpr { |
1a4d82fc | 161 | let ctor_path; |
970d7e83 | 162 | let all_fields; |
dc9dc135 | 163 | let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); |
064997fb | 164 | let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| { |
9ffffee4 | 165 | let args = thin_vec![field.self_expr.clone()]; |
9e0c209e | 166 | cx.expr_call_global(field.span, fn_path.clone(), args) |
1a4d82fc | 167 | }; |
970d7e83 | 168 | |
7453a54e | 169 | let vdata; |
487cf647 FG |
170 | match substr.fields { |
171 | Struct(vdata_, af) => { | |
1a4d82fc | 172 | ctor_path = cx.path(trait_span, vec![substr.type_ident]); |
970d7e83 | 173 | all_fields = af; |
487cf647 | 174 | vdata = *vdata_; |
223e47cc | 175 | } |
487cf647 | 176 | EnumMatching(.., variant, af) => { |
e1599b0c | 177 | ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); |
970d7e83 | 178 | all_fields = af; |
e1599b0c | 179 | vdata = &variant.data; |
5bcae85e | 180 | } |
9c376795 FG |
181 | EnumTag(..) | AllFieldlessEnum(..) => { |
182 | cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)) | |
183 | } | |
1a4d82fc | 184 | StaticEnum(..) | StaticStruct(..) => { |
e74abb32 | 185 | cx.span_bug(trait_span, &format!("associated function in `derive({})`", name)) |
1a4d82fc | 186 | } |
223e47cc LB |
187 | } |
188 | ||
064997fb | 189 | let expr = match *vdata { |
9e0c209e | 190 | VariantData::Struct(..) => { |
dfeec247 XL |
191 | let fields = all_fields |
192 | .iter() | |
9e0c209e | 193 | .map(|field| { |
5e7ed085 FG |
194 | let Some(ident) = field.name else { |
195 | cx.span_bug( | |
dfeec247 XL |
196 | trait_span, |
197 | &format!("unnamed field in normal struct in `derive({})`", name,), | |
5e7ed085 | 198 | ); |
9e0c209e SL |
199 | }; |
200 | let call = subcall(cx, field); | |
201 | cx.field_imm(field.span, ident, call) | |
202 | }) | |
9ffffee4 | 203 | .collect::<ThinVec<_>>(); |
a7813a04 | 204 | |
9e0c209e SL |
205 | cx.expr_struct(trait_span, ctor_path, fields) |
206 | } | |
207 | VariantData::Tuple(..) => { | |
208 | let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); | |
209 | let path = cx.expr_path(ctor_path); | |
210 | cx.expr_call(trait_span, path, subcalls) | |
7453a54e | 211 | } |
9e0c209e | 212 | VariantData::Unit(..) => cx.expr_path(ctor_path), |
064997fb FG |
213 | }; |
214 | BlockOrExpr::new_expr(expr) | |
223e47cc | 215 | } |