Skip to content

Commit

Permalink
assert_clone in shallow #[derive(Copy,Clone)]
Browse files Browse the repository at this point in the history
  • Loading branch information
durka committed Apr 18, 2016
1 parent 0cf4bc5 commit 6712c02
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 69 deletions.
9 changes: 9 additions & 0 deletions src/libcore/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: Clone + ?Sized>(_: &T) {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized> Clone for &'a T {
/// Returns a shallow copy of the reference.
Expand Down
87 changes: 55 additions & 32 deletions src/libsyntax_ext/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
}
}
}
Expand Down Expand Up @@ -101,10 +108,14 @@ fn cs_shallow_clone(cx: &mut ExtCtxt, trait_span: Span) -> P<Expr> {
fn cs_deep_clone(
name: &str,
cx: &mut ExtCtxt, trait_span: Span,
substr: &Substructure) -> P<Expr> {
substr: &Substructure,
mode: Mode) -> P<Expr> {
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())];

Expand Down Expand Up @@ -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::<Vec<_>>();

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::<Vec<_>>();

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)
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/cmp/eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/cmp/ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/cmp/partial_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax_ext/deriving/cmp/partial_ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/decodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_(
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/encodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_(
Expand Down
35 changes: 25 additions & 10 deletions src/libsyntax_ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RefCell<EncloseFunc<'a>>>,

/// Arguments other than the self argument
pub args: Vec<Ty<'a>>,
Expand Down Expand Up @@ -331,6 +331,9 @@ pub enum SubstructureFields<'a> {
pub type CombineSubstructureFunc<'a> =
Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>;

pub type EncloseFunc<'a> =
Box<FnMut(&mut ExtCtxt, Span, P<Expr>) -> P<Expr> + '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
Expand All @@ -344,6 +347,13 @@ pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
RefCell::new(f)
}

pub fn enclose<'a, F>(f: F)
-> Option<RefCell<EncloseFunc<'a>>>
where F: FnMut(&mut ExtCtxt, Span, P<Expr>) -> P<Expr> + 'a
{
Some(RefCell::new(Box::new(f)))
}

/// This method helps to extract all the type parameters referenced from a
/// type. For a type parameter `<T>`, it looks for either a `TyPath` that
/// is not global and starts with `T`, or a `TyQPath`.
Expand Down Expand Up @@ -861,7 +871,14 @@ impl<'a> MethodDef<'a> {
abi: Abi,
explicit_self: ast::ExplicitSelf,
arg_types: Vec<(Ident, P<ast::Ty>)> ,
body: P<Expr>) -> ast::ImplItem {
mut body: P<Expr>) -> 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);

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_ext/deriving/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![],
Expand Down
2 changes: 1 addition & 1 deletion src/test/auxiliary/custom_derive_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/test/auxiliary/custom_derive_plugin_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
17 changes: 3 additions & 14 deletions src/test/run-make/expand-derive/Makefile
Original file line number Diff line number Diff line change
@@ -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
11 changes: 10 additions & 1 deletion src/test/run-make/expand-derive/input_pp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down

0 comments on commit 6712c02

Please sign in to comment.