diff options
| author | Manish Goregaokar <manishsmail@gmail.com> | 2016-09-15 18:16:19 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-09-15 18:16:19 +0530 |
| commit | 726850170d3ec4606246ad90fcb08d1eee913c76 (patch) | |
| tree | fdffde4c348f7388797f45de4501e5a0ca91ba43 /src/libsyntax_ext | |
| parent | 16ff9e22cdb552fd10e6cee2eb22f0c5da6d7e79 (diff) | |
| parent | 62cb7510ac6285c93ec691198a92f910582d31a2 (diff) | |
| download | rust-726850170d3ec4606246ad90fcb08d1eee913c76.tar.gz rust-726850170d3ec4606246ad90fcb08d1eee913c76.zip | |
Rollup merge of #36384 - petrochenkov:derclone, r=alexcrichton
Improve shallow `Clone` deriving
`Copy` unions now support `#[derive(Clone)]`.
Less code is generated for `#[derive(Clone, Copy)]`.
+
Unions now support `#[derive(Eq)]`.
Less code is generated for `#[derive(Eq)]`.
---
Example of code reduction:
```
enum E {
A { a: u8, b: u16 },
B { c: [u8; 100] },
}
```
Before:
```
fn clone(&self) -> E {
match (&*self,) {
(&E::A { a: ref __self_0, b: ref __self_1 },) => {
::std::clone::assert_receiver_is_clone(&(*__self_0));
::std::clone::assert_receiver_is_clone(&(*__self_1));
*self
}
(&E::B { c: ref __self_0 },) => {
::std::clone::assert_receiver_is_clone(&(*__self_0));
*self
}
}
}
```
After:
```
fn clone(&self) -> E {
{
let _: ::std::clone::AssertParamIsClone<u8>;
let _: ::std::clone::AssertParamIsClone<u16>;
let _: ::std::clone::AssertParamIsClone<[u8; 100]>;
*self
}
}
```
All the matches are removed, bound assertions are more lightweight.
`let _: Checker<CheckMe>;`, unlike `checker(&check_me);`, doesn't have to be translated by rustc_trans and then inlined by LLVM, it doesn't even exist in MIR, this means faster compilation.
---
Union impls are generated like this:
```
union U {
a: u8,
b: u16,
c: [u8; 100],
}
```
```
fn clone(&self) -> U {
{
let _: ::std::clone::AssertParamIsCopy<Self>;
*self
}
}
```
Fixes https://github.com/rust-lang/rust/issues/36043
cc @durka
r? @alexcrichton
Diffstat (limited to 'src/libsyntax_ext')
| -rw-r--r-- | src/libsyntax_ext/deriving/clone.rs | 166 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/eq.rs | 55 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/generic/mod.rs | 27 |
3 files changed, 152 insertions, 96 deletions
diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index c7afaaf4796..d7bc2a6faee 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -11,20 +11,14 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData}; +use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData}; use syntax::attr; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; -use syntax::parse::token::InternedString; +use syntax::parse::token::{keywords, InternedString}; use syntax::ptr::P; use syntax_pos::Span; -#[derive(PartialEq)] -enum Mode { - Deep, - Shallow, -} - pub fn expand_deriving_clone(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, @@ -40,29 +34,38 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, // 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 unify_fieldless_variants; let substructure; + let is_shallow; match *item { 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(&annitem.attrs, "rustc_copy_clone_marker") => { - + if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") && + ty_params.is_empty() => { + bounds = vec![]; + is_shallow = true; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone_shallow("Clone", c, s, sub, false) + })); + } + ItemKind::Union(..) => { bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; - unify_fieldless_variants = true; + is_shallow = true; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Shallow) + cs_clone_shallow("Clone", c, s, sub, true) })); } - _ => { bounds = vec![]; - unify_fieldless_variants = false; + is_shallow = false; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Deep) + cs_clone("Clone", c, s, sub) })); } } @@ -80,7 +83,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, additional_bounds: bounds, generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "clone", generics: LifetimeBounds::empty(), @@ -89,37 +92,72 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, ret_ty: Self_, attributes: attrs, is_unsafe: false, - unify_fieldless_variants: unify_fieldless_variants, + unify_fieldless_variants: false, combine_substructure: substructure, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + 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 = super::allow_unstable(cx, span, "derive(Clone)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["clone", helper_name]), + vec![], vec![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, keywords::SelfType.ident())); + 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.node.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, - mode: Mode) + substr: &Substructure) -> P<Expr> { let ctor_path; let all_fields; - let fn_path = match mode { - Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]), - Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]), - }; + let fn_path = cx.std_path(&["clone", "Clone", "clone"]); let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; - - let span = if mode == Mode::Shallow { - // set the expn ID so we can call the unstable method - super::allow_unstable(cx, field.span, "derive(Clone)") - } else { - field.span - }; - cx.expr_call_global(span, fn_path.clone(), args) + cx.expr_call_global(field.span, fn_path.clone(), args) }; let vdata; @@ -145,43 +183,31 @@ fn cs_clone(name: &str, } } - match mode { - Mode::Shallow => { - let mut stmts = all_fields.iter().map(|f| { - let call = subcall(cx, f); - cx.stmt_expr(call) - }).collect::<Vec<_>>(); - stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); - cx.expr_block(cx.block(trait_span, stmts)) - } - Mode::Deep => { - 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<_>>(); + 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), - } + 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), } } diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs index 425a47a991b..fa0fb2492c5 100644 --- a/src/libsyntax_ext/deriving/cmp/eq.rs +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -11,7 +11,7 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, MetaItem}; +use syntax::ast::{self, Expr, MetaItem}; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::parse::token::InternedString; @@ -23,22 +23,6 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { - fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { - cs_same_method(|cx, span, exprs| { - // create `a.<method>(); b.<method>(); c.<method>(); ...` - // (where method is `assert_receiver_is_total_eq`) - let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect(); - let block = cx.block(span, stmts); - cx.expr_block(block) - }, - Box::new(|cx, sp, _, _| { - cx.span_bug(sp, "non matching enums in derive(Eq)?") - }), - cx, - span, - substr) - } - let inline = cx.meta_word(span, InternedString::new("inline")); let hidden = cx.meta_list_item_word(span, InternedString::new("hidden")); let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]); @@ -50,7 +34,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, additional_bounds: Vec::new(), generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "assert_receiver_is_total_eq", generics: LifetimeBounds::empty(), @@ -66,5 +50,38 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, true) +} + +fn cs_total_eq_assert(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> 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 = super::allow_unstable(cx, span, "derive(Eq)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["cmp", helper_name]), + vec![], vec![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: &ast::VariantData) { + for field in variant.fields() { + // let _: AssertParamIsEq<FieldTy>; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq"); + } + } + + let mut stmts = Vec::new(); + 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.node.data); + } + } + _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`") + } + cx.expr_block(cx.block(trait_span, stmts)) } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 600f5d335c4..339a6c477cc 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> { mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut FnMut(Annotatable)) { + self.expand_ext(cx, mitem, item, push, false); + } + + pub fn expand_ext(&self, + cx: &mut ExtCtxt, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut FnMut(Annotatable), + from_scratch: bool) { match *item { Annotatable::Item(ref item) => { let newitem = match item.node { ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch) } ast::ItemKind::Enum(ref enum_def, ref generics) => { - self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics) + self.expand_enum_def(cx, enum_def, &item.attrs, + item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, + generics, from_scratch) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt, struct_def: &'a VariantData, type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P<ast::Item> { let field_tys: Vec<P<ast::Ty>> = struct_def.fields() .iter() @@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_struct_method_body(cx, self, struct_def, @@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> { enum_def: &'a EnumDef, type_attrs: &[ast::Attribute], type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P<ast::Item> { let mut field_tys = Vec::new(); @@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_enum_method_body(cx, self, enum_def, |
