Improve shallow Clone deriving

This commit is contained in:
Vadim Petrochenkov
2016-08-26 19:23:42 +03:00
parent f1f40f850e
commit 2a2c9d38c7
10 changed files with 200 additions and 101 deletions

View File

@@ -106,10 +106,23 @@ pub trait Clone : Sized {
} }
} }
// FIXME(aburka): this method is used solely by #[derive] to // FIXME(aburka): these structs are used solely by #[derive] to
// assert that every component of a type implements Clone. // assert that every component of a type implements Clone or Copy.
// //
// This should never be called by user code. // These structs should never appear in user code.
#[doc(hidden)]
#[allow(missing_debug_implementations)]
#[unstable(feature = "derive_clone_copy",
reason = "deriving hack, should not be public",
issue = "0")]
pub struct AssertParamIsClone<T: Clone + ?Sized> { _field: ::marker::PhantomData<T> }
#[doc(hidden)]
#[allow(missing_debug_implementations)]
#[unstable(feature = "derive_clone_copy",
reason = "deriving hack, should not be public",
issue = "0")]
pub struct AssertParamIsCopy<T: Copy + ?Sized> { _field: ::marker::PhantomData<T> }
#[cfg(stage0)]
#[doc(hidden)] #[doc(hidden)]
#[inline(always)] #[inline(always)]
#[unstable(feature = "derive_clone_copy", #[unstable(feature = "derive_clone_copy",

View File

@@ -11,20 +11,14 @@
use deriving::generic::*; use deriving::generic::*;
use deriving::generic::ty::*; 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::attr;
use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder; use syntax::ext::build::AstBuilder;
use syntax::parse::token::InternedString; use syntax::parse::token::{keywords, InternedString};
use syntax::ptr::P; use syntax::ptr::P;
use syntax_pos::Span; use syntax_pos::Span;
#[derive(PartialEq)]
enum Mode {
Deep,
Shallow,
}
pub fn expand_deriving_clone(cx: &mut ExtCtxt, pub fn expand_deriving_clone(cx: &mut ExtCtxt,
span: Span, span: Span,
mitem: &MetaItem, 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 // 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 // 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. // 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 bounds;
let unify_fieldless_variants;
let substructure; let substructure;
let is_shallow;
match *item { match *item {
Annotatable::Item(ref annitem) => { Annotatable::Item(ref annitem) => {
match annitem.node { match annitem.node {
ItemKind::Struct(_, Generics { ref ty_params, .. }) | ItemKind::Struct(_, Generics { ref ty_params, .. }) |
ItemKind::Enum(_, Generics { ref ty_params, .. }) ItemKind::Enum(_, Generics { ref ty_params, .. })
if ty_params.is_empty() && if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") &&
attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => { ty_params.is_empty() => {
bounds = vec![];
bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; is_shallow = true;
unify_fieldless_variants = true;
substructure = combine_substructure(Box::new(|c, s, sub| { substructure = combine_substructure(Box::new(|c, s, sub| {
cs_clone("Clone", c, s, sub, Mode::Shallow) cs_clone_shallow("Clone", c, s, sub, false)
}));
}
ItemKind::Union(..) => {
bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
is_shallow = true;
substructure = combine_substructure(Box::new(|c, s, sub| {
cs_clone_shallow("Clone", c, s, sub, true)
})); }));
} }
_ => { _ => {
bounds = vec![]; bounds = vec![];
unify_fieldless_variants = false; is_shallow = false;
substructure = combine_substructure(Box::new(|c, s, sub| { 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, additional_bounds: bounds,
generics: LifetimeBounds::empty(), generics: LifetimeBounds::empty(),
is_unsafe: false, is_unsafe: false,
supports_unions: false, supports_unions: true,
methods: vec![MethodDef { methods: vec![MethodDef {
name: "clone", name: "clone",
generics: LifetimeBounds::empty(), generics: LifetimeBounds::empty(),
@@ -89,37 +92,85 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
ret_ty: Self_, ret_ty: Self_,
attributes: attrs, attributes: attrs,
is_unsafe: false, is_unsafe: false,
unify_fieldless_variants: unify_fieldless_variants, unify_fieldless_variants: false,
combine_substructure: substructure, combine_substructure: substructure,
}], }],
associated_types: Vec::new(), 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![]);
let local = P(ast::Local {
pat: cx.pat_wild(span),
ty: Some(cx.ty_path(assert_path)),
init: None,
id: ast::DUMMY_NODE_ID,
span: span,
attrs: ast::ThinVec::new(),
});
let stmt = ast::Stmt {
id: ast::DUMMY_NODE_ID,
node: ast::StmtKind::Local(local),
span: span,
};
stmts.push(stmt);
}
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, fn cs_clone(name: &str,
cx: &mut ExtCtxt, cx: &mut ExtCtxt,
trait_span: Span, trait_span: Span,
substr: &Substructure, substr: &Substructure)
mode: Mode)
-> P<Expr> { -> P<Expr> {
let ctor_path; let ctor_path;
let all_fields; let all_fields;
let fn_path = match mode { let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]),
Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]),
};
let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| { let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| {
let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
cx.expr_call_global(field.span, fn_path.clone(), args)
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)
}; };
let vdata; let vdata;
@@ -145,43 +196,31 @@ fn cs_clone(name: &str,
} }
} }
match mode { match *vdata {
Mode::Shallow => { VariantData::Struct(..) => {
let mut stmts = all_fields.iter().map(|f| { let fields = all_fields.iter()
let call = subcall(cx, f); .map(|field| {
cx.stmt_expr(call) let ident = match field.name {
}).collect::<Vec<_>>(); Some(i) => i,
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); None => {
cx.expr_block(cx.block(trait_span, stmts)) cx.span_bug(trait_span,
} &format!("unnamed field in normal struct in \
Mode::Deep => { `derive({})`",
match *vdata { name))
VariantData::Struct(..) => { }
let fields = all_fields.iter() };
.map(|field| { let call = subcall(cx, field);
let ident = match field.name { cx.field_imm(field.span, ident, call)
Some(i) => i, })
None => { .collect::<Vec<_>>();
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) 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),
}
} }
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),
} }
} }

View File

@@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> {
mitem: &ast::MetaItem, mitem: &ast::MetaItem,
item: &'a Annotatable, item: &'a Annotatable,
push: &mut FnMut(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 { match *item {
Annotatable::Item(ref item) => { Annotatable::Item(ref item) => {
let newitem = match item.node { let newitem = match item.node {
ast::ItemKind::Struct(ref struct_def, ref generics) => { 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) => { 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) => { ast::ItemKind::Union(ref struct_def, ref generics) => {
if self.supports_unions { 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 { } else {
cx.span_err(mitem.span, cx.span_err(mitem.span,
"this trait cannot be derived for unions"); "this trait cannot be derived for unions");
@@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> {
cx: &mut ExtCtxt, cx: &mut ExtCtxt,
struct_def: &'a VariantData, struct_def: &'a VariantData,
type_ident: Ident, type_ident: Ident,
generics: &Generics) generics: &Generics,
from_scratch: bool)
-> P<ast::Item> { -> P<ast::Item> {
let field_tys: Vec<P<ast::Ty>> = struct_def.fields() let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
.iter() .iter()
@@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> {
let (explicit_self, self_args, nonself_args, tys) = let (explicit_self, self_args, nonself_args, tys) =
method_def.split_self_nonself_args(cx, self, type_ident, generics); 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, method_def.expand_static_struct_method_body(cx,
self, self,
struct_def, struct_def,
@@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> {
enum_def: &'a EnumDef, enum_def: &'a EnumDef,
type_attrs: &[ast::Attribute], type_attrs: &[ast::Attribute],
type_ident: Ident, type_ident: Ident,
generics: &Generics) generics: &Generics,
from_scratch: bool)
-> P<ast::Item> { -> P<ast::Item> {
let mut field_tys = Vec::new(); let mut field_tys = Vec::new();
@@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> {
let (explicit_self, self_args, nonself_args, tys) = let (explicit_self, self_args, nonself_args, tys) =
method_def.split_self_nonself_args(cx, self, type_ident, generics); 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, method_def.expand_static_enum_method_body(cx,
self, self,
enum_def, enum_def,

View File

@@ -18,16 +18,12 @@ struct S {
b: u16, b: u16,
} }
#[derive(Clone, Copy)]
union U { union U {
s: S, s: S,
c: u32, c: u32,
} }
impl Clone for U {
fn clone(&self) -> Self { *self }
}
impl Copy for U {}
fn main() { fn main() {
unsafe { unsafe {
{ {

View File

@@ -12,16 +12,12 @@
#![feature(untagged_unions)] #![feature(untagged_unions)]
#[derive(Clone, Copy)]
union U { union U {
a: u8, a: u8,
b: u64, b: u64,
} }
impl Clone for U {
fn clone(&self) -> Self { *self }
}
impl Copy for U {}
fn main() { fn main() {
unsafe { unsafe {
let mut u = U { b: 0 }; let mut u = U { b: 0 };

View File

@@ -10,16 +10,16 @@
#![feature(untagged_unions)] #![feature(untagged_unions)]
#[derive(Clone)]
union U { union U {
a: u8 a: u8
} }
#[derive(Clone)]
union W { union W {
a: String a: String
} }
impl Clone for U { fn clone(&self) { panic!(); } }
impl Clone for W { fn clone(&self) { panic!(); } }
impl Copy for U {} // OK impl Copy for U {} // OK
impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type

View File

@@ -0,0 +1,41 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(untagged_unions)]
#[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied
union U1 {
a: u8,
}
#[derive(Clone)]
union U2 {
a: u8, // OK
}
impl Copy for U2 {}
#[derive(Clone, Copy)]
union U3 {
a: u8, // OK
}
#[derive(Clone, Copy)]
union U4<T> {
a: T, // OK
}
#[derive(Clone)]
struct CloneNoCopy;
fn main() {
let u = U4 { a: CloneNoCopy };
let w = u.clone(); //~ ERROR no method named `clone` found for type `U4<CloneNoCopy>`
}

View File

@@ -13,7 +13,6 @@
#![feature(untagged_unions)] #![feature(untagged_unions)]
#[derive( #[derive(
Clone, //~ ERROR this trait cannot be derived for unions
PartialEq, //~ ERROR this trait cannot be derived for unions PartialEq, //~ ERROR this trait cannot be derived for unions
Eq, //~ ERROR this trait cannot be derived for unions Eq, //~ ERROR this trait cannot be derived for unions
PartialOrd, //~ ERROR this trait cannot be derived for unions PartialOrd, //~ ERROR this trait cannot be derived for unions

View File

@@ -10,14 +10,14 @@
#![feature(untagged_unions)] #![feature(untagged_unions)]
#[derive(Copy)] #[derive(Clone, Copy)]
#[repr(C)] #[repr(C)]
struct LARGE_INTEGER_U { struct LARGE_INTEGER_U {
LowPart: u32, LowPart: u32,
HighPart: u32, HighPart: u32,
} }
#[derive(Copy)] #[derive(Clone, Copy)]
#[repr(C)] #[repr(C)]
union LARGE_INTEGER { union LARGE_INTEGER {
__unnamed__: LARGE_INTEGER_U, __unnamed__: LARGE_INTEGER_U,
@@ -25,9 +25,6 @@ union LARGE_INTEGER {
QuadPart: u64, QuadPart: u64,
} }
impl Clone for LARGE_INTEGER_U { fn clone(&self) -> Self { *self } }
impl Clone for LARGE_INTEGER { fn clone(&self) -> Self { *self } }
#[link(name = "rust_test_helpers")] #[link(name = "rust_test_helpers")]
extern "C" { extern "C" {
fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER; fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER;

View File

@@ -14,18 +14,23 @@
#[derive( #[derive(
Copy, Copy,
Clone,
)] )]
union U { union U {
a: u8, a: u8,
b: u16, b: u16,
} }
impl Clone for U { #[derive(Clone, Copy)]
fn clone(&self) -> Self { *self } union W<T> {
a: T,
} }
fn main() { fn main() {
let u = U { b: 0 }; let u = U { b: 0 };
let u1 = u; let u1 = u;
let u2 = u.clone(); let u2 = u.clone();
let w = W { a: 0 };
let w1 = w.clone();
} }