From 0fb03c5071a8f28ab5011fae2f994d0ef21774d2 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Sat, 22 Aug 2015 07:27:23 +0100 Subject: [PATCH] Implement `clone_from` via `#[derive(Clone)]`, fixes #13281 --- src/libsyntax/ext/deriving/clone.rs | 41 +++++++++++- src/libsyntax/ext/deriving/generic/mod.rs | 82 +++++++++++++---------- src/libsyntax/ext/deriving/generic/ty.rs | 20 +++++- 3 files changed, 102 insertions(+), 41 deletions(-) diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index 9261c0162c7a5..448bbb7d73984 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use ast; use ast::{MetaItem, Expr}; use codemap::Span; use ext::base::{ExtCtxt, Annotatable}; @@ -38,11 +39,23 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, explicit_self: borrowed_explicit_self(), args: Vec::new(), ret_ty: Self_, - attributes: attrs, + attributes: attrs.clone(), is_unsafe: false, combine_substructure: combine_substructure(Box::new(|c, s, sub| { cs_clone("Clone", c, s, sub) })), + }, + MethodDef { + name: "clone_from", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_mut_explicit_self(), + args: vec![borrowed_self()], + ret_ty: nil_ty(), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_clone_from("Clone", c, s, sub) + })), } ), associated_types: Vec::new(), @@ -111,3 +124,29 @@ fn cs_clone( } } } + +fn cs_clone_from( + name: &str, + cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P { + + cs_same_method( + |cx, span, exprs| { + cx.expr_block(cx.block(span, exprs.into_iter().map(|expr| { + cx.stmt_expr(expr) + }).collect(), None)) + }, + Box::new(|cx, span, (self_args, _), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, &format!("not exactly 2 arguments in `derive({})`", name)) + } else { + let clone_path = cx.std_path(&["clone", "Clone", "clone"]); + let args = vec![cx.expr_addr_of(span, self_args[1].clone())]; + + let rhs = cx.expr_call_global(span, clone_path, args); + let lhs = self_args[0].clone(); + cx.expr(span, ast::ExprAssign(lhs, rhs)) + } + }), + cx, span, substr) +} diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index f8f63e94ee574..777b9b1b8de96 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -155,7 +155,7 @@ //! //! ```{.text} //! EnumNonMatchingCollapsed( -//! vec![, ], +//! vec![, ], //! &[, ], //! &[, ]) //! ``` @@ -203,7 +203,6 @@ use ext::build::AstBuilder; use codemap::{self, DUMMY_SP}; use codemap::Span; use diagnostic::SpanHandler; -use fold::MoveMap; use owned_slice::OwnedSlice; use parse::token::InternedString; use parse::token::special_idents; @@ -267,7 +266,7 @@ pub struct Substructure<'a> { /// ident of the method pub method_ident: Ident, /// dereferenced access to any `Self_` or `Ptr(Self_, _)` arguments - pub self_args: &'a [P], + pub self_args: &'a [(P, ast::Mutability)], /// verbatim access to any other arguments pub nonself_args: &'a [P], pub fields: &'a SubstructureFields<'a> @@ -311,7 +310,7 @@ pub enum SubstructureFields<'a> { /// variants for the enum itself, and the third component is a list of /// `Ident`s bound to the variant index values for each of the actual /// input `Self` arguments. - EnumNonMatchingCollapsed(Vec, &'a [P], &'a [Ident]), + EnumNonMatchingCollapsed(Vec>, &'a [P], &'a [Ident]), /// A static method where `Self` is a struct. StaticStruct(&'a ast::StructDef, StaticFields), @@ -332,7 +331,7 @@ pub type CombineSubstructureFunc<'a> = /// holding the variant index value for each of the `Self` arguments. The /// last argument is all the non-`Self` args of the method being derived. pub type EnumNonMatchCollapsedFunc<'a> = - Box]) -> P + 'a>; + Box], &[Ident]), &[P]) -> P + 'a>; pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>) -> RefCell> { @@ -764,7 +763,7 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt, trait_: &TraitDef, type_ident: Ident, - self_args: &[P], + self_args: &[(P, ast::Mutability)], nonself_args: &[P], fields: &SubstructureFields) -> P { @@ -798,7 +797,7 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef, type_ident: Ident, generics: &Generics) - -> (ast::ExplicitSelf, Vec>, Vec>, Vec<(Ident, P)>) { + -> (ast::ExplicitSelf, Vec<(P, ast::Mutability)>, Vec>, Vec<(Ident, P)>) { let mut self_args = Vec::new(); let mut nonself_args = Vec::new(); @@ -807,10 +806,10 @@ impl<'a> MethodDef<'a> { let ast_explicit_self = match self.explicit_self { Some(ref self_ptr) => { - let (self_expr, explicit_self) = + let (self_expr, mutability, explicit_self) = ty::get_explicit_self(cx, trait_.span, self_ptr); - self_args.push(self_expr); + self_args.push((self_expr, mutability)); nonstatic = true; explicit_self @@ -829,10 +828,13 @@ impl<'a> MethodDef<'a> { // for static methods, just treat any Self // arguments as a normal arg Self_ if nonstatic => { - self_args.push(arg_expr); + self_args.push((arg_expr, ast::MutImmutable)); } - Ptr(ref ty, _) if **ty == Self_ && nonstatic => { - self_args.push(cx.expr_deref(trait_.span, arg_expr)) + Ptr(ref ty, ref ty_ptr) if **ty == Self_ && nonstatic => { + let mutability = match ty_ptr { + &ty::Borrowed(_, m) | &ty::Raw(m) => m + }; + self_args.push((cx.expr_deref(trait_.span, arg_expr), mutability)) } _ => { nonself_args.push(arg_expr); @@ -921,7 +923,7 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'b>, struct_def: &'b StructDef, type_ident: Ident, - self_args: &[P], + self_args: &[(P, ast::Mutability)], nonself_args: &[P]) -> P { @@ -936,7 +938,7 @@ impl<'a> MethodDef<'a> { struct_def, &format!("__self_{}", i), - ast::MutImmutable); + self_args[i].1); patterns.push(pat); raw_fields.push(ident_expr); } @@ -978,7 +980,7 @@ impl<'a> MethodDef<'a> { // make a series of nested matches, to destructure the // structs. This is actually right-to-left, but it shouldn't // matter. - for (arg_expr, pat) in self_args.iter().zip(patterns) { + for (&(ref arg_expr, _), ref pat) in self_args.iter().zip(patterns) { body = cx.expr_match(trait_.span, arg_expr.clone(), vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) } @@ -990,7 +992,7 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef, struct_def: &StructDef, type_ident: Ident, - self_args: &[P], + self_args: &[(P, ast::Mutability)], nonself_args: &[P]) -> P { let summary = trait_.summarise_struct(cx, struct_def); @@ -1037,7 +1039,7 @@ impl<'a> MethodDef<'a> { enum_def: &'b EnumDef, type_attrs: &[ast::Attribute], type_ident: Ident, - self_args: Vec>, + self_args: Vec<(P, ast::Mutability)>, nonself_args: &[P]) -> P { self.build_enum_match_tuple( @@ -1087,31 +1089,27 @@ impl<'a> MethodDef<'a> { enum_def: &'b EnumDef, type_attrs: &[ast::Attribute], type_ident: Ident, - self_args: Vec>, + self_args: Vec<(P, ast::Mutability)>, nonself_args: &[P]) -> P { let sp = trait_.span; let variants = &enum_def.variants; let self_arg_names = self_args.iter().enumerate() - .map(|(arg_count, _self_arg)| { + .map(|(arg_count, &(_, mutability))| { if arg_count == 0 { - "__self".to_string() + ("__self".to_string(), mutability) } else { - format!("__arg_{}", arg_count) + (format!("__arg_{}", arg_count-1), mutability) } }) - .collect::>(); - - let self_arg_idents = self_arg_names.iter() - .map(|name|cx.ident_of(&name[..])) - .collect::>(); + .collect::>(); // The `vi_idents` will be bound, solely in the catch-all, to // a series of let statements mapping each self_arg to an int // value corresponding to its discriminant. let vi_idents: Vec = self_arg_names.iter() - .map(|name| { let vi_suffix = format!("{}_vi", &name[..]); + .map(|&(ref name, _)| { let vi_suffix = format!("{}_vi", &name[..]); cx.ident_of(&vi_suffix[..]) }) .collect::>(); @@ -1119,7 +1117,7 @@ impl<'a> MethodDef<'a> { // delegated expression that handles the catch-all case, // using `__variants_tuple` to drive logic if necessary. let catch_all_substructure = EnumNonMatchingCollapsed( - self_arg_idents, &variants[..], &vi_idents[..]); + self_args.iter().map(|&(ref expr, _)| expr.clone()).collect(), &variants[..], &vi_idents[..]); // These arms are of the form: // (Variant1, Variant1, ...) => Body1 @@ -1128,12 +1126,12 @@ impl<'a> MethodDef<'a> { // where each tuple has length = self_args.len() let mut match_arms: Vec = variants.iter().enumerate() .map(|(index, variant)| { - let mk_self_pat = |cx: &mut ExtCtxt, self_arg_name: &str| { + let mk_self_pat = |cx: &mut ExtCtxt, self_arg_name: &(String, ast::Mutability)| { let (p, idents) = trait_.create_enum_variant_pattern(cx, type_ident, &**variant, - self_arg_name, - ast::MutImmutable); - (cx.pat(sp, ast::PatRegion(p, ast::MutImmutable)), idents) + &self_arg_name.0[..], + self_arg_name.1); + (cx.pat(sp, ast::PatRegion(p, self_arg_name.1)), idents) }; // A single arm has form (&VariantK, &VariantK, ...) => BodyK @@ -1146,7 +1144,7 @@ impl<'a> MethodDef<'a> { idents }; for self_arg_name in &self_arg_names[1..] { - let (p, idents) = mk_self_pat(cx, &self_arg_name[..]); + let (p, idents) = mk_self_pat(cx, self_arg_name); subpats.push(p); self_pats_idents.push(idents); } @@ -1251,7 +1249,7 @@ impl<'a> MethodDef<'a> { find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs); let mut first_ident = None; - for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { + for (&ident, &(ref self_arg, _)) in vi_idents.iter().zip(&self_args) { let path = cx.std_path(&["intrinsics", "discriminant_value"]); let call = cx.expr_call_global( sp, path, vec![cx.expr_addr_of(sp, self_arg.clone())]); @@ -1303,7 +1301,12 @@ impl<'a> MethodDef<'a> { // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg)); + let borrowed_self_args = self_args.into_iter().map(|(self_arg, mutability)| { + match mutability { + ast::MutImmutable => cx.expr_addr_of(sp, self_arg), + ast::MutMutable => cx.expr_mut_addr_of(sp, self_arg), + } + }).collect(); let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args)); //Lastly we create an expression which branches on all discriminants being equal @@ -1381,7 +1384,12 @@ impl<'a> MethodDef<'a> { // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg)); + let borrowed_self_args = self_args.into_iter().map(|(self_arg, mutability)| { + match mutability { + ast::MutImmutable => cx.expr_addr_of(sp, self_arg), + ast::MutMutable => cx.expr_mut_addr_of(sp, self_arg), + } + }).collect(); let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args)); cx.expr_match(sp, match_arg, match_arms) } @@ -1392,7 +1400,7 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef, enum_def: &EnumDef, type_ident: Ident, - self_args: &[P], + self_args: &[(P, ast::Mutability)], nonself_args: &[P]) -> P { let summary = enum_def.variants.iter().map(|v| { diff --git a/src/libsyntax/ext/deriving/generic/ty.rs b/src/libsyntax/ext/deriving/generic/ty.rs index 9e8e68c0b8cce..7afd5132d4054 100644 --- a/src/libsyntax/ext/deriving/generic/ty.rs +++ b/src/libsyntax/ext/deriving/generic/ty.rs @@ -100,17 +100,29 @@ pub enum Ty<'a> { pub fn borrowed_ptrty<'r>() -> PtrTy<'r> { Borrowed(None, ast::MutImmutable) } +pub fn borrowed_mut_ptrty<'r>() -> PtrTy<'r> { + Borrowed(None, ast::MutMutable) +} pub fn borrowed<'r>(ty: Box>) -> Ty<'r> { Ptr(ty, borrowed_ptrty()) } +pub fn borrowed_mut<'r>(ty: Box>) -> Ty<'r> { + Ptr(ty, borrowed_mut_ptrty()) +} pub fn borrowed_explicit_self<'r>() -> Option>> { Some(Some(borrowed_ptrty())) } +pub fn borrowed_mut_explicit_self<'r>() -> Option>> { + Some(Some(borrowed_mut_ptrty())) +} pub fn borrowed_self<'r>() -> Ty<'r> { borrowed(Box::new(Self_)) } +pub fn borrowed_mut_self<'r>() -> Ty<'r> { + borrowed_mut(Box::new(Self_)) +} pub fn nil_ty<'r>() -> Ty<'r> { Tuple(Vec::new()) @@ -258,26 +270,28 @@ impl<'a> LifetimeBounds<'a> { } pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option) - -> (P, ast::ExplicitSelf) { + -> (P, ast::Mutability, ast::ExplicitSelf) { // this constructs a fresh `self` path, which will match the fresh `self` binding // created below. let self_path = cx.expr_self(span); match *self_ptr { None => { - (self_path, respan(span, ast::SelfValue(special_idents::self_))) + (self_path, ast::MutImmutable, respan(span, ast::SelfValue(special_idents::self_))) } Some(ref ptr) => { + let mutability; let self_ty = respan( span, match *ptr { Borrowed(ref lt, mutbl) => { + mutability = mutbl; let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name)); ast::SelfRegion(lt, mutbl, special_idents::self_) } Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition") }); let self_expr = cx.expr_deref(span, self_path); - (self_expr, self_ty) + (self_expr, mutability, self_ty) } } }