Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement specialised clone_from via #[derive(Clone)] #27939

Closed
wants to merge 9 commits into from
42 changes: 41 additions & 1 deletion src/libsyntax/ext/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -111,3 +124,30 @@ fn cs_clone(
}
}
}

fn cs_clone_from(
name: &str,
cx: &mut ExtCtxt, span: Span,
substr: &Substructure) -> P<Expr> {

let fn_path = cx.std_path(&["clone", "Clone", "clone_from"]);
cs_call_global(
|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, fn_path, ast::MutMutable)
}
134 changes: 97 additions & 37 deletions src/libsyntax/ext/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
//!
//! ```{.text}
//! EnumNonMatchingCollapsed(
//! vec![<ident of self>, <ident of __arg_1>],
//! vec![<ast::Expr for self>, <ast::Expr for __arg_1>],
//! &[<ast::Variant for C0>, <ast::Variant for C1>],
//! &[<ident for self index value>, <ident of __arg_1 index value>])
//! ```
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<Expr>],
pub self_args: &'a [(P<Expr>, ast::Mutability)],
/// verbatim access to any other arguments
pub nonself_args: &'a [P<Expr>],
pub fields: &'a SubstructureFields<'a>
Expand Down Expand Up @@ -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<Ident>, &'a [P<ast::Variant>], &'a [Ident]),
EnumNonMatchingCollapsed(Vec<P<ast::Expr>>, &'a [P<ast::Variant>], &'a [Ident]),

/// A static method where `Self` is a struct.
StaticStruct(&'a ast::StructDef, StaticFields),
Expand All @@ -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<FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>;
Box<FnMut(&mut ExtCtxt, Span, (&[P<Expr>], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>;

pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
-> RefCell<CombineSubstructureFunc<'a>> {
Expand Down Expand Up @@ -764,7 +763,7 @@ impl<'a> MethodDef<'a> {
cx: &mut ExtCtxt,
trait_: &TraitDef,
type_ident: Ident,
self_args: &[P<Expr>],
self_args: &[(P<Expr>, ast::Mutability)],
nonself_args: &[P<Expr>],
fields: &SubstructureFields)
-> P<Expr> {
Expand Down Expand Up @@ -798,7 +797,8 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef,
type_ident: Ident,
generics: &Generics)
-> (ast::ExplicitSelf, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {
-> (ast::ExplicitSelf, Vec<(P<Expr>, ast::Mutability)>,
Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {

let mut self_args = Vec::new();
let mut nonself_args = Vec::new();
Expand All @@ -807,10 +807,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
Expand All @@ -829,10 +829,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);
Expand Down Expand Up @@ -921,7 +924,7 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef<'b>,
struct_def: &'b StructDef,
type_ident: Ident,
self_args: &[P<Expr>],
self_args: &[(P<Expr>, ast::Mutability)],
nonself_args: &[P<Expr>])
-> P<Expr> {

Expand All @@ -936,7 +939,7 @@ impl<'a> MethodDef<'a> {
struct_def,
&format!("__self_{}",
i),
ast::MutImmutable);
self_args[i].1);
patterns.push(pat);
raw_fields.push(ident_expr);
}
Expand Down Expand Up @@ -978,7 +981,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) ))
}
Expand All @@ -990,7 +993,7 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef,
struct_def: &StructDef,
type_ident: Ident,
self_args: &[P<Expr>],
self_args: &[(P<Expr>, ast::Mutability)],
nonself_args: &[P<Expr>])
-> P<Expr> {
let summary = trait_.summarise_struct(cx, struct_def);
Expand Down Expand Up @@ -1037,7 +1040,7 @@ impl<'a> MethodDef<'a> {
enum_def: &'b EnumDef,
type_attrs: &[ast::Attribute],
type_ident: Ident,
self_args: Vec<P<Expr>>,
self_args: Vec<(P<Expr>, ast::Mutability)>,
nonself_args: &[P<Expr>])
-> P<Expr> {
self.build_enum_match_tuple(
Expand Down Expand Up @@ -1087,39 +1090,38 @@ impl<'a> MethodDef<'a> {
enum_def: &'b EnumDef,
type_attrs: &[ast::Attribute],
type_ident: Ident,
self_args: Vec<P<Expr>>,
self_args: Vec<(P<Expr>, ast::Mutability)>,
nonself_args: &[P<Expr>]) -> P<Expr> {

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

let self_arg_idents = self_arg_names.iter()
.map(|name|cx.ident_of(&name[..]))
.collect::<Vec<ast::Ident>>();
.collect::<Vec<(String, ast::Mutability)>>();

// 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<ast::Ident> = 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::<Vec<ast::Ident>>();

// Builds, via callback to call_substructure_method, the
// 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
Expand All @@ -1128,12 +1130,12 @@ impl<'a> MethodDef<'a> {
// where each tuple has length = self_args.len()
let mut match_arms: Vec<ast::Arm> = 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
Expand All @@ -1146,7 +1148,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);
}
Expand Down Expand Up @@ -1251,7 +1253,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())]);
Expand Down Expand Up @@ -1303,7 +1305,9 @@ 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)| {
cs_addr_of(cx, sp, self_arg, mutability)
}).collect();
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));

//Lastly we create an expression which branches on all discriminants being equal
Expand Down Expand Up @@ -1381,7 +1385,9 @@ 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)| {
cs_addr_of(cx, sp, self_arg, mutability)
}).collect();
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
cx.expr_match(sp, match_arg, match_arms)
}
Expand All @@ -1392,7 +1398,7 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef,
enum_def: &EnumDef,
type_ident: Ident,
self_args: &[P<Expr>],
self_args: &[(P<Expr>, ast::Mutability)],
nonself_args: &[P<Expr>])
-> P<Expr> {
let summary = enum_def.variants.iter().map(|v| {
Expand Down Expand Up @@ -1659,6 +1665,51 @@ pub fn cs_same_method<F>(f: F,
}
}

/// Call a global function on all the fields, and then
/// process the collected results. i.e.
///
/// ```
/// f(cx, span, vec![path::method(self_1, __arg_1_1, __arg_2_1),
/// path::method(self_2, __arg_1_2, __arg_2_2)])
/// ```
#[inline]
pub fn cs_call_global<F>(f: F,
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure,
path: Vec<Ident>,
mutability: ast::Mutability)
-> P<Expr> where
F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
// call path::method(self_n, other_1_n, other_2_n, ...)
let called = all_fields.iter().map(|field| {
// Start with self_n
let mut args = vec![cs_addr_of(cx, field.span, field.self_.clone(), mutability)];
// Extend with other_n_m
args.extend(field.other.iter().map(|e| {
cx.expr_addr_of(field.span, e.clone())
}));

cx.expr_call_global(field.span,
path.clone(),
args)
}).collect();

f(cx, trait_span, called)
},
EnumNonMatchingCollapsed(ref all_self_args, _, tuple) =>
enum_nonmatch_f(cx, trait_span, (&all_self_args[..], tuple),
substructure.nonself_args),
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, "static function in `derive`")
}
}
}

/// Fold together the results of calling the derived method on all the
/// fields. `use_foldl` controls whether this is done left-to-right
/// (`true`) or right-to-left (`false`).
Expand Down Expand Up @@ -1728,3 +1779,12 @@ pub fn cs_and(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
enum_nonmatch_f,
cx, span, substructure)
}

/// Mutably/immutabily take the address of an expression
#[inline]
pub fn cs_addr_of(cx: &mut ExtCtxt, span: Span, expr: P<Expr>, mutbl: ast::Mutability) -> P<Expr> {
match mutbl {
ast::MutImmutable => cx.expr_addr_of(span, expr),
ast::MutMutable => cx.expr_mut_addr_of(span, expr),
}
}
Loading