| use crate::deriving::path_std; |
| use crate::deriving::generic::*; |
| use crate::deriving::generic::ty::*; |
| |
| use syntax::ast::{self, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData}; |
| use syntax::ext::base::{Annotatable, ExtCtxt, SpecialDerives}; |
| use syntax::ptr::P; |
| use syntax::symbol::{kw, sym, Symbol}; |
| use syntax_pos::Span; |
| |
| pub fn expand_deriving_clone(cx: &mut ExtCtxt<'_>, |
| span: Span, |
| mitem: &MetaItem, |
| item: &Annotatable, |
| push: &mut dyn FnMut(Annotatable)) { |
| // check if we can use a short form |
| // |
| // the short form is `fn clone(&self) -> Self { *self }` |
| // |
| // we can use the short form if: |
| // - the item is Copy (unfortunately, all we can check is whether it's also deriving Copy) |
| // - there are no generic parameters (after specialization this limitation can be removed) |
| // if we used the short form with generics, we'd have to bound the generics with |
| // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something |
| // that is Clone but not Copy. and until specialization we can't write both impls. |
| // - the item is a union with Copy fields |
| // Unions with generic parameters still can derive Clone because they require Copy |
| // for deriving, Clone alone is not enough. |
| // Whever Clone is implemented for fields is irrelevant so we don't assert it. |
| let bounds; |
| let substructure; |
| let is_shallow; |
| match *item { |
| Annotatable::Item(ref annitem) => { |
| match annitem.node { |
| ItemKind::Struct(_, Generics { ref params, .. }) | |
| ItemKind::Enum(_, Generics { ref params, .. }) => { |
| let container_id = cx.current_expansion.id.expn_data().parent; |
| if cx.resolver.has_derives(container_id, SpecialDerives::COPY) && |
| !params.iter().any(|param| match param.kind { |
| ast::GenericParamKind::Type { .. } => true, |
| _ => false, |
| }) |
| { |
| bounds = vec![]; |
| is_shallow = true; |
| substructure = combine_substructure(Box::new(|c, s, sub| { |
| cs_clone_shallow("Clone", c, s, sub, false) |
| })); |
| } else { |
| bounds = vec![]; |
| is_shallow = false; |
| substructure = combine_substructure(Box::new(|c, s, sub| { |
| cs_clone("Clone", c, s, sub) |
| })); |
| } |
| } |
| ItemKind::Union(..) => { |
| bounds = vec![Literal(path_std!(cx, marker::Copy))]; |
| is_shallow = true; |
| substructure = combine_substructure(Box::new(|c, s, sub| { |
| cs_clone_shallow("Clone", c, s, sub, true) |
| })); |
| } |
| _ => { |
| bounds = vec![]; |
| is_shallow = false; |
| substructure = combine_substructure(Box::new(|c, s, sub| { |
| cs_clone("Clone", c, s, sub) |
| })); |
| } |
| } |
| } |
| |
| _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"), |
| } |
| |
| let inline = cx.meta_word(span, sym::inline); |
| let attrs = vec![cx.attribute(inline)]; |
| let trait_def = TraitDef { |
| span, |
| attributes: Vec::new(), |
| path: path_std!(cx, clone::Clone), |
| additional_bounds: bounds, |
| generics: LifetimeBounds::empty(), |
| is_unsafe: false, |
| supports_unions: true, |
| methods: vec![MethodDef { |
| name: "clone", |
| generics: LifetimeBounds::empty(), |
| explicit_self: borrowed_explicit_self(), |
| args: Vec::new(), |
| ret_ty: Self_, |
| attributes: attrs, |
| is_unsafe: false, |
| unify_fieldless_variants: false, |
| combine_substructure: substructure, |
| }], |
| associated_types: Vec::new(), |
| }; |
| |
| trait_def.expand_ext(cx, mitem, item, push, is_shallow) |
| } |
| |
| fn cs_clone_shallow(name: &str, |
| cx: &mut ExtCtxt<'_>, |
| trait_span: Span, |
| substr: &Substructure<'_>, |
| is_union: bool) |
| -> P<Expr> { |
| fn assert_ty_bounds(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, |
| ty: P<ast::Ty>, span: Span, helper_name: &str) { |
| // Generate statement `let _: helper_name<ty>;`, |
| // set the expn ID so we can use the unstable struct. |
| let span = cx.with_def_site_ctxt(span); |
| let assert_path = cx.path_all(span, true, |
| cx.std_path(&[sym::clone, Symbol::intern(helper_name)]), |
| vec![GenericArg::Type(ty)], vec![]); |
| stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); |
| } |
| fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) { |
| for field in variant.fields() { |
| // let _: AssertParamIsClone<FieldTy>; |
| assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone"); |
| } |
| } |
| |
| let mut stmts = Vec::new(); |
| if is_union { |
| // let _: AssertParamIsCopy<Self>; |
| let self_ty = |
| cx.ty_path(cx.path_ident(trait_span, ast::Ident::with_dummy_span(kw::SelfUpper))); |
| assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); |
| } else { |
| match *substr.fields { |
| StaticStruct(vdata, ..) => { |
| process_variant(cx, &mut stmts, vdata); |
| } |
| StaticEnum(enum_def, ..) => { |
| for variant in &enum_def.variants { |
| process_variant(cx, &mut stmts, &variant.data); |
| } |
| } |
| _ => cx.span_bug(trait_span, &format!("unexpected substructure in \ |
| shallow `derive({})`", name)) |
| } |
| } |
| stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); |
| cx.expr_block(cx.block(trait_span, stmts)) |
| } |
| |
| fn cs_clone(name: &str, |
| cx: &mut ExtCtxt<'_>, |
| trait_span: Span, |
| substr: &Substructure<'_>) |
| -> P<Expr> { |
| let ctor_path; |
| let all_fields; |
| let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); |
| let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| { |
| let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; |
| cx.expr_call_global(field.span, fn_path.clone(), args) |
| }; |
| |
| let vdata; |
| match *substr.fields { |
| Struct(vdata_, ref af) => { |
| ctor_path = cx.path(trait_span, vec![substr.type_ident]); |
| all_fields = af; |
| vdata = vdata_; |
| } |
| EnumMatching(.., variant, ref af) => { |
| ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.ident]); |
| all_fields = af; |
| vdata = &variant.data; |
| } |
| EnumNonMatchingCollapsed(..) => { |
| cx.span_bug(trait_span, |
| &format!("non-matching enum variants in \ |
| `derive({})`", |
| name)) |
| } |
| StaticEnum(..) | StaticStruct(..) => { |
| cx.span_bug(trait_span, &format!("static method in `derive({})`", name)) |
| } |
| } |
| |
| match *vdata { |
| VariantData::Struct(..) => { |
| let fields = all_fields.iter() |
| .map(|field| { |
| let ident = match field.name { |
| Some(i) => i, |
| None => { |
| cx.span_bug(trait_span, |
| &format!("unnamed field in normal struct in \ |
| `derive({})`", |
| name)) |
| } |
| }; |
| let call = subcall(cx, field); |
| cx.field_imm(field.span, ident, call) |
| }) |
| .collect::<Vec<_>>(); |
| |
| cx.expr_struct(trait_span, ctor_path, fields) |
| } |
| VariantData::Tuple(..) => { |
| let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); |
| let path = cx.expr_path(ctor_path); |
| cx.expr_call(trait_span, path, subcalls) |
| } |
| VariantData::Unit(..) => cx.expr_path(ctor_path), |
| } |
| } |