From 6712c0213db76d95bdc2eafdb0a8597857fdbe2f Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Fri, 8 Apr 2016 14:35:05 -0400 Subject: [PATCH] assert_clone in shallow #[derive(Copy,Clone)] --- src/libcore/clone.rs | 9 ++ src/libsyntax_ext/deriving/clone.rs | 87 ++++++++++++------- src/libsyntax_ext/deriving/cmp/eq.rs | 2 +- src/libsyntax_ext/deriving/cmp/ord.rs | 2 +- src/libsyntax_ext/deriving/cmp/partial_eq.rs | 2 +- src/libsyntax_ext/deriving/cmp/partial_ord.rs | 4 +- src/libsyntax_ext/deriving/debug.rs | 2 +- src/libsyntax_ext/deriving/decodable.rs | 2 +- src/libsyntax_ext/deriving/default.rs | 2 +- src/libsyntax_ext/deriving/encodable.rs | 2 +- src/libsyntax_ext/deriving/generic/mod.rs | 35 +++++--- src/libsyntax_ext/deriving/hash.rs | 2 +- src/test/auxiliary/custom_derive_plugin.rs | 2 +- .../auxiliary/custom_derive_plugin_attr.rs | 2 +- src/test/run-make/expand-derive/Makefile | 17 +--- src/test/run-make/expand-derive/input_pp.rs | 11 ++- 16 files changed, 114 insertions(+), 69 deletions(-) diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index a793502e58d37..541e16bf22684 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -75,6 +75,15 @@ pub trait Clone : Sized { } } +// FIXME(aburka): this method is used solely by #[deriving] to +// assert that every component of a type implements Clone. +// +// This should never be called by user code. +#[doc(hidden)] +#[inline(always)] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn assert_receiver_is_clone(_: &T) {} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T: ?Sized> Clone for &'a T { /// Returns a shallow copy of the reference. diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index c4ff53c85ab2e..6a04a406919d3 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -19,6 +19,8 @@ use syntax::ext::build::AstBuilder; use syntax::parse::token::InternedString; use syntax::ptr::P; +enum Mode { Assert, Clone } + pub fn expand_deriving_clone(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, @@ -39,25 +41,30 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, let substructure; let nested_match; match *item { - Annotatable::Item(ref item) => { - match item.node { + Annotatable::Item(ref annitem) => { + match annitem.node { ItemKind::Struct(_, Generics { ref ty_params, .. }) | ItemKind::Enum(_, Generics { ref ty_params, .. }) - if ty_params.is_empty() && attr::contains_name(&item.attrs, "derive_Copy") => { + if ty_params.is_empty() + && attr::contains_name(&annitem.attrs, "derive_Copy") => { bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; - substructure = combine_substructure(Box::new(|c, s, _| { - cs_shallow_clone(c, s) + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_deep_clone("Clone", c, s, sub, Mode::Assert) })); - nested_match = false; + nested_match = enclose(|c, s, sub| { + let inner = cs_shallow_clone(c, s); + c.expr_block(c.block_all(s, vec![c.stmt_expr(sub)], Some(inner))) + //^ FIXME(aburka): this generates an extra set of {} braces + }); } _ => { bounds = vec![]; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_deep_clone("Clone", c, s, sub) + cs_deep_clone("Clone", c, s, sub, Mode::Clone) })); - nested_match = true; + nested_match = None; } } } @@ -101,10 +108,14 @@ fn cs_shallow_clone(cx: &mut ExtCtxt, trait_span: Span) -> P { fn cs_deep_clone( name: &str, cx: &mut ExtCtxt, trait_span: Span, - substr: &Substructure) -> P { + substr: &Substructure, + mode: Mode) -> P { let ctor_path; let all_fields; - let fn_path = cx.std_path(&["clone", "Clone", "clone"]); + let fn_path = match mode { + Mode::Assert => cx.std_path(&["clone", "assert_receiver_is_clone"]), + Mode::Clone => cx.std_path(&["clone", "Clone", "clone"]), + }; let subcall = |field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; @@ -134,29 +145,41 @@ fn cs_deep_clone( } } - 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)) - } - }; - cx.field_imm(field.span, ident, subcall(field)) - }).collect::>(); - - cx.expr_struct(trait_span, ctor_path, fields) + match mode { + Mode::Assert => { + cx.expr_block(cx.block(trait_span, + all_fields.iter() + .map(subcall) + .map(|e| cx.stmt_expr(e)) + .collect(), + None)) } - VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(subcall).collect(); - let path = cx.expr_path(ctor_path); - cx.expr_call(trait_span, path, subcalls) - } - VariantData::Unit(..) => { - cx.expr_path(ctor_path) + Mode::Clone => { + 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)) + } + }; + cx.field_imm(field.span, ident, subcall(field)) + }).collect::>(); + + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(subcall).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) + } + VariantData::Unit(..) => { + cx.expr_path(ctor_path) + } + } } } } diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs index 1fb2cefa3f0ba..15194742c4ebc 100644 --- a/src/libsyntax_ext/deriving/cmp/eq.rs +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -58,7 +58,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, name: "assert_receiver_is_total_eq", generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(), ret_ty: nil_ty(), attributes: attrs, diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs index a93ade5633399..5ce7f3d556239 100644 --- a/src/libsyntax_ext/deriving/cmp/ord.rs +++ b/src/libsyntax_ext/deriving/cmp/ord.rs @@ -38,7 +38,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt, name: "cmp", generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(borrowed_self()), ret_ty: Literal(path_std!(cx, core::cmp::Ordering)), attributes: attrs, diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs index dd8b71383df1b..4213289818400 100644 --- a/src/libsyntax_ext/deriving/cmp/partial_eq.rs +++ b/src/libsyntax_ext/deriving/cmp/partial_eq.rs @@ -69,7 +69,7 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt, name: $name, generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(borrowed_self()), ret_ty: Literal(path_local!(bool)), attributes: attrs, diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs index 2f70332d7aa04..07c95b269ba31 100644 --- a/src/libsyntax_ext/deriving/cmp/partial_ord.rs +++ b/src/libsyntax_ext/deriving/cmp/partial_ord.rs @@ -34,7 +34,7 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt, name: $name, generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(borrowed_self()), ret_ty: Literal(path_local!(bool)), attributes: attrs, @@ -59,7 +59,7 @@ pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt, name: "partial_cmp", generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec![borrowed_self()], ret_ty: ret_ty, attributes: attrs, diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs index 907c9568a3d53..3b91f1742fc49 100644 --- a/src/libsyntax_ext/deriving/debug.rs +++ b/src/libsyntax_ext/deriving/debug.rs @@ -41,7 +41,7 @@ pub fn expand_deriving_debug(cx: &mut ExtCtxt, name: "fmt", generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(fmtr), ret_ty: Literal(path_std!(cx, core::fmt::Result)), attributes: Vec::new(), diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs index 4b50eec080b88..d0625188ea102 100644 --- a/src/libsyntax_ext/deriving/decodable.rs +++ b/src/libsyntax_ext/deriving/decodable.rs @@ -73,7 +73,7 @@ fn expand_deriving_decodable_imp(cx: &mut ExtCtxt, vec![Path::new_(vec!(krate, "Decoder"), None, vec!(), true)])] }, explicit_self: None, - nested_match: true, + nested_match: None, args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))), Borrowed(None, Mutability::Mutable))), ret_ty: Literal(Path::new_( diff --git a/src/libsyntax_ext/deriving/default.rs b/src/libsyntax_ext/deriving/default.rs index d3381c9fc4857..2fdb231524024 100644 --- a/src/libsyntax_ext/deriving/default.rs +++ b/src/libsyntax_ext/deriving/default.rs @@ -38,7 +38,7 @@ pub fn expand_deriving_default(cx: &mut ExtCtxt, name: "default", generics: LifetimeBounds::empty(), explicit_self: None, - nested_match: true, + nested_match: None, args: Vec::new(), ret_ty: Self_, attributes: attrs, diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs index 4fdfe778777e9..37de90dae46fe 100644 --- a/src/libsyntax_ext/deriving/encodable.rs +++ b/src/libsyntax_ext/deriving/encodable.rs @@ -149,7 +149,7 @@ fn expand_deriving_encodable_imp(cx: &mut ExtCtxt, vec![Path::new_(vec![krate, "Encoder"], None, vec!(), true)])] }, explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))), Borrowed(None, Mutability::Mutable))), ret_ty: Literal(Path::new_( diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 94d97b370ee76..75065469ac3e6 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -249,7 +249,7 @@ pub struct MethodDef<'a> { /// Whether the function structure is a nested `match` statement with the result of /// combine_substructe() inside, or just the result of combine_substructure() - pub nested_match: bool, + pub nested_match: Option>>, /// Arguments other than the self argument pub args: Vec>, @@ -331,6 +331,9 @@ pub enum SubstructureFields<'a> { pub type CombineSubstructureFunc<'a> = Box P + 'a>; +pub type EncloseFunc<'a> = + Box) -> P + 'a>; + /// Deal with non-matching enum variants. The tuple is a list of /// identifiers (one for each `Self` argument, which could be any of the /// variants since they have been collapsed together) and the identifiers @@ -344,6 +347,13 @@ pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>) RefCell::new(f) } +pub fn enclose<'a, F>(f: F) + -> Option>> + where F: FnMut(&mut ExtCtxt, Span, P) -> P + 'a +{ + Some(RefCell::new(Box::new(f))) +} + /// This method helps to extract all the type parameters referenced from a /// type. For a type parameter ``, it looks for either a `TyPath` that /// is not global and starts with `T`, or a `TyQPath`. @@ -861,7 +871,14 @@ impl<'a> MethodDef<'a> { abi: Abi, explicit_self: ast::ExplicitSelf, arg_types: Vec<(Ident, P)> , - body: P) -> ast::ImplItem { + mut body: P) -> ast::ImplItem { + + if self.nested_match.is_some() { + let mut f = self.nested_match.as_ref().unwrap().borrow_mut(); + let f: &mut _ = &mut *f; + body = f(cx, trait_.span, body); + } + // create the generics that aren't for Self let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics); @@ -988,14 +1005,12 @@ impl<'a> MethodDef<'a> { nonself_args, &Struct(struct_def, fields)); - if self.nested_match { - // 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) { - body = cx.expr_match(trait_.span, arg_expr.clone(), - vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) - } + // 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) { + body = cx.expr_match(trait_.span, arg_expr.clone(), + vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) } body diff --git a/src/libsyntax_ext/deriving/hash.rs b/src/libsyntax_ext/deriving/hash.rs index eff134f96a137..a972230264af9 100644 --- a/src/libsyntax_ext/deriving/hash.rs +++ b/src/libsyntax_ext/deriving/hash.rs @@ -47,7 +47,7 @@ pub fn expand_deriving_hash(cx: &mut ExtCtxt, vec![path_std!(cx, core::hash::Hasher)])], }, explicit_self: borrowed_explicit_self(), - nested_match: true, + nested_match: None, args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, Mutability::Mutable))), ret_ty: nil_ty(), attributes: vec![], diff --git a/src/test/auxiliary/custom_derive_plugin.rs b/src/test/auxiliary/custom_derive_plugin.rs index 3f4a0dd45bb50..b948d5d925022 100644 --- a/src/test/auxiliary/custom_derive_plugin.rs +++ b/src/test/auxiliary/custom_derive_plugin.rs @@ -55,7 +55,7 @@ fn expand(cx: &mut ExtCtxt, generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), args: vec![], - nested_match: true, + nested_match: None, ret_ty: Literal(Path::new_local("isize")), attributes: vec![], is_unsafe: false, diff --git a/src/test/auxiliary/custom_derive_plugin_attr.rs b/src/test/auxiliary/custom_derive_plugin_attr.rs index 1a605fa30e3b6..a429b954e8d45 100644 --- a/src/test/auxiliary/custom_derive_plugin_attr.rs +++ b/src/test/auxiliary/custom_derive_plugin_attr.rs @@ -57,7 +57,7 @@ fn expand(cx: &mut ExtCtxt, generics: LifetimeBounds::empty(), explicit_self: borrowed_explicit_self(), args: vec![], - nested_match: true, + nested_match: None, ret_ty: Literal(Path::new_local("isize")), attributes: vec![], is_unsafe: false, diff --git a/src/test/run-make/expand-derive/Makefile b/src/test/run-make/expand-derive/Makefile index 7ce1e79f83d7a..f90ebd7daf006 100644 --- a/src/test/run-make/expand-derive/Makefile +++ b/src/test/run-make/expand-derive/Makefile @@ -1,21 +1,10 @@ -include ../tools.mk -REPLACEMENT := s/[0-9][0-9]*\#[0-9][0-9]*//g - all: # pretty-expand both input.rs and input_pp.rs $(RUSTC) -o $(TMPDIR)/input.pretty -Z unstable-options \ - --pretty expanded,hygiene input.rs + --pretty expanded input.rs $(RUSTC) -o $(TMPDIR)/input_pp.pretty -Z unstable-options \ - --pretty expanded,hygiene input_pp.rs - - # the name/ctxt numbers are very internals-dependent and thus - # change relatively frequently, and testing for their exact values - # them will fail annoyingly, so we just remove them - # - # (These need to be out-of-place because OSX/BSD & GNU sed - # differ.) - sed "$(REPLACEMENT)" $(TMPDIR)/input.pretty > $(TMPDIR)/input.replaced - sed "$(REPLACEMENT)" $(TMPDIR)/input_pp.pretty > $(TMPDIR)/input_pp.replaced + --pretty expanded input_pp.rs - diff -u $(TMPDIR)/input.replaced $(TMPDIR)/input_pp.replaced + diff -u $(TMPDIR)/input.pretty $(TMPDIR)/input_pp.pretty diff --git a/src/test/run-make/expand-derive/input_pp.rs b/src/test/run-make/expand-derive/input_pp.rs index 2cd6975828374..5f66eb8c0c3eb 100644 --- a/src/test/run-make/expand-derive/input_pp.rs +++ b/src/test/run-make/expand-derive/input_pp.rs @@ -19,7 +19,16 @@ struct A { #[allow(unused_qualifications)] impl ::clone::Clone for A { #[inline] - fn clone(&self) -> A { *self } + fn clone(&self) -> A { + { + match *self { + A { a: ref __self_0_0 } => { + ::clone::assert_receiver_is_clone(&(*__self_0_0)); + } + }; + *self + } + } } #[automatically_derived] #[allow(unused_qualifications)]