pub use self::StaticFields::*;
pub use self::SubstructureFields::*;
use self::StructType::*;
use std::cell::RefCell;
use std::collections::HashSet;
use std::vec;
use abi::Abi;
use abi;
use ast;
use ast::{EnumDef, Expr, Ident, Generics, StructDef};
use ast_util;
use attr;
use attr::AttrMetaMethods;
use ext::base::{ExtCtxt, Annotatable};
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::{intern, InternedString};
use parse::token::special_idents;
use ptr::P;
use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty};
pub mod ty;
pub struct TraitDef<'a> {
pub span: Span,
pub attributes: Vec<ast::Attribute>,
pub path: Path<'a>,
pub additional_bounds: Vec<Ty<'a>>,
pub generics: LifetimeBounds<'a>,
pub is_unsafe: bool,
pub methods: Vec<MethodDef<'a>>,
pub associated_types: Vec<(ast::Ident, Ty<'a>)>,
}
pub struct MethodDef<'a> {
pub name: &'a str,
pub generics: LifetimeBounds<'a>,
pub explicit_self: Option<Option<PtrTy<'a>>>,
pub args: Vec<Ty<'a>>,
pub ret_ty: Ty<'a>,
pub attributes: Vec<ast::Attribute>,
pub is_unsafe: bool,
pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
}
pub struct Substructure<'a> {
pub type_ident: Ident,
pub method_ident: Ident,
pub self_args: &'a [P<Expr>],
pub nonself_args: &'a [P<Expr>],
pub fields: &'a SubstructureFields<'a>
}
pub struct FieldInfo<'a> {
pub span: Span,
pub name: Option<Ident>,
pub self_: P<Expr>,
pub other: Vec<P<Expr>>,
pub attrs: &'a [ast::Attribute],
}
pub enum StaticFields {
Unnamed(Vec<Span>),
Named(Vec<(Ident, Span)>),
}
pub enum SubstructureFields<'a> {
Struct(Vec<FieldInfo<'a>>),
EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
EnumNonMatchingCollapsed(Vec<Ident>, &'a [P<ast::Variant>], &'a [Ident]),
StaticStruct(&'a ast::StructDef, StaticFields),
StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>),
}
pub type CombineSubstructureFunc<'a> =
Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>;
pub type EnumNonMatchCollapsedFunc<'a> =
Box<FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>;
pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
-> RefCell<CombineSubstructureFunc<'a>> {
RefCell::new(f)
}
fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> {
use visit;
struct Visitor<'a> {
ty_param_names: &'a [ast::Name],
types: Vec<P<ast::Ty>>,
}
impl<'a> visit::Visitor<'a> for Visitor<'a> {
fn visit_ty(&mut self, ty: &'a ast::Ty) {
match ty.node {
ast::TyPath(_, ref path) if !path.global => {
match path.segments.first() {
Some(segment) => {
if self.ty_param_names.contains(&segment.identifier.name) {
self.types.push(P(ty.clone()));
}
}
None => {}
}
}
_ => {}
}
visit::walk_ty(self, ty)
}
}
let mut visitor = Visitor {
ty_param_names: ty_param_names,
types: Vec::new(),
};
visit::Visitor::visit_ty(&mut visitor, ty);
visitor.types
}
impl<'a> TraitDef<'a> {
pub fn expand(&self,
cx: &mut ExtCtxt,
mitem: &ast::MetaItem,
item: &'a Annotatable,
push: &mut FnMut(Annotatable))
{
match *item {
Annotatable::Item(ref item) => {
let newitem = match item.node {
ast::ItemStruct(ref struct_def, ref generics) => {
self.expand_struct_def(cx,
&struct_def,
item.ident,
generics)
}
ast::ItemEnum(ref enum_def, ref generics) => {
self.expand_enum_def(cx,
enum_def,
&item.attrs,
item.ident,
generics)
}
_ => {
cx.span_err(mitem.span,
"`derive` may only be applied to structs and enums");
return;
}
};
let mut attrs = newitem.attrs.clone();
attrs.extend(item.attrs.iter().filter(|a| {
match &a.name()[..] {
"allow" | "warn" | "deny" | "forbid" => true,
_ => false,
}
}).cloned());
push(Annotatable::Item(P(ast::Item {
attrs: attrs,
..(*newitem).clone()
})))
}
_ => {
cx.span_err(mitem.span, "`derive` may only be applied to structs and enums");
}
}
}
fn create_derived_impl(&self,
cx: &mut ExtCtxt,
type_ident: Ident,
generics: &Generics,
field_tys: Vec<P<ast::Ty>>,
methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> {
let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| {
P(ast::ImplItem {
id: ast::DUMMY_NODE_ID,
span: self.span,
ident: ident,
vis: ast::Inherited,
attrs: Vec::new(),
node: ast::TypeImplItem(type_def.to_ty(cx,
self.span,
type_ident,
generics
)),
})
});
let Generics { mut lifetimes, ty_params, mut where_clause } =
self.generics.to_generics(cx, self.span, type_ident, generics);
let mut ty_params = ty_params.into_vec();
lifetimes.extend(generics.lifetimes.iter().cloned());
ty_params.extend(generics.ty_params.iter().map(|ty_param| {
let mut bounds: Vec<_> =
self.additional_bounds.iter().map(|p| {
cx.typarambound(p.to_path(cx, self.span,
type_ident, generics))
}).collect();
bounds.push(cx.typarambound(trait_path.clone()));
for declared_bound in ty_param.bounds.iter() {
bounds.push((*declared_bound).clone());
}
cx.typaram(self.span,
ty_param.ident,
OwnedSlice::from_vec(bounds),
None)
}));
where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
match *clause {
ast::WherePredicate::BoundPredicate(ref wb) => {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
span: self.span,
bound_lifetimes: wb.bound_lifetimes.clone(),
bounded_ty: wb.bounded_ty.clone(),
bounds: OwnedSlice::from_vec(wb.bounds.iter().cloned().collect())
})
}
ast::WherePredicate::RegionPredicate(ref rb) => {
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
span: self.span,
lifetime: rb.lifetime,
bounds: rb.bounds.iter().cloned().collect()
})
}
ast::WherePredicate::EqPredicate(ref we) => {
ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
id: ast::DUMMY_NODE_ID,
span: self.span,
path: we.path.clone(),
ty: we.ty.clone()
})
}
}
}));
if !ty_params.is_empty() {
let ty_param_names: Vec<ast::Name> = ty_params.iter()
.map(|ty_param| ty_param.ident.name)
.collect();
let mut processed_field_types = HashSet::new();
for field_ty in field_tys {
let tys = find_type_parameters(&*field_ty, &ty_param_names);
for ty in tys {
if let ast::TyPath(_, ref p) = ty.node {
if p.segments.len() == 1
&& ty_param_names.contains(&p.segments[0].identifier.name)
|| processed_field_types.contains(&p.segments) {
continue;
};
processed_field_types.insert(p.segments.clone());
}
let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| {
cx.typarambound(p.to_path(cx, self.span, type_ident, generics))
}).collect();
bounds.push(cx.typarambound(trait_path.clone()));
let predicate = ast::WhereBoundPredicate {
span: self.span,
bound_lifetimes: vec![],
bounded_ty: ty,
bounds: OwnedSlice::from_vec(bounds),
};
let predicate = ast::WherePredicate::BoundPredicate(predicate);
where_clause.predicates.push(predicate);
}
}
}
let trait_generics = Generics {
lifetimes: lifetimes,
ty_params: OwnedSlice::from_vec(ty_params),
where_clause: where_clause
};
let trait_ref = cx.trait_ref(trait_path);
let self_ty_params = generics.ty_params.map(|ty_param| {
cx.ty_ident(self.span, ty_param.ident)
});
let self_lifetimes: Vec<ast::Lifetime> =
generics.lifetimes
.iter()
.map(|ld| ld.lifetime)
.collect();
let self_type = cx.ty_path(
cx.path_all(self.span, false, vec!( type_ident ), self_lifetimes,
self_ty_params.into_vec(), Vec::new()));
let attr = cx.attribute(
self.span,
cx.meta_word(self.span,
InternedString::new("automatically_derived")));
attr::mark_used(&attr);
let opt_trait_ref = Some(trait_ref);
let ident = ast_util::impl_pretty_name(&opt_trait_ref, Some(&*self_type));
let unused_qual = cx.attribute(
self.span,
cx.meta_list(self.span,
InternedString::new("allow"),
vec![cx.meta_word(self.span,
InternedString::new("unused_qualifications"))]));
let mut a = vec![attr, unused_qual];
a.extend(self.attributes.iter().cloned());
let unsafety = if self.is_unsafe {
ast::Unsafety::Unsafe
} else {
ast::Unsafety::Normal
};
cx.item(
self.span,
ident,
a,
ast::ItemImpl(unsafety,
ast::ImplPolarity::Positive,
trait_generics,
opt_trait_ref,
self_type,
methods.into_iter().chain(associated_types).collect()))
}
fn expand_struct_def(&self,
cx: &mut ExtCtxt,
struct_def: &'a StructDef,
type_ident: Ident,
generics: &Generics) -> P<ast::Item> {
let field_tys: Vec<P<ast::Ty>> = struct_def.fields.iter()
.map(|field| field.node.ty.clone())
.collect();
let methods = self.methods.iter().map(|method_def| {
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() {
method_def.expand_static_struct_method_body(
cx,
self,
struct_def,
type_ident,
&self_args[..],
&nonself_args[..])
} else {
method_def.expand_struct_method_body(cx,
self,
struct_def,
type_ident,
&self_args[..],
&nonself_args[..])
};
method_def.create_method(cx,
self,
type_ident,
generics,
abi::Rust,
explicit_self,
tys,
body)
}).collect();
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
}
fn expand_enum_def(&self,
cx: &mut ExtCtxt,
enum_def: &'a EnumDef,
type_attrs: &[ast::Attribute],
type_ident: Ident,
generics: &Generics) -> P<ast::Item> {
let mut field_tys = Vec::new();
for variant in &enum_def.variants {
match variant.node.kind {
ast::VariantKind::TupleVariantKind(ref args) => {
field_tys.extend(args.iter()
.map(|arg| arg.ty.clone()));
}
ast::VariantKind::StructVariantKind(ref args) => {
field_tys.extend(args.fields.iter()
.map(|field| field.node.ty.clone()));
}
}
}
let methods = self.methods.iter().map(|method_def| {
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() {
method_def.expand_static_enum_method_body(
cx,
self,
enum_def,
type_ident,
&self_args[..],
&nonself_args[..])
} else {
method_def.expand_enum_method_body(cx,
self,
enum_def,
type_attrs,
type_ident,
self_args,
&nonself_args[..])
};
method_def.create_method(cx,
self,
type_ident,
generics,
abi::Rust,
explicit_self,
tys,
body)
}).collect();
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
}
}
fn find_repr_type_name(diagnostic: &SpanHandler,
type_attrs: &[ast::Attribute]) -> &'static str {
let mut repr_type_name = "i32";
for a in type_attrs {
for r in &attr::find_repr_attrs(diagnostic, a) {
repr_type_name = match *r {
attr::ReprAny | attr::ReprPacked | attr::ReprSimd => continue,
attr::ReprExtern => "i32",
attr::ReprInt(_, attr::SignedInt(ast::TyIs)) => "isize",
attr::ReprInt(_, attr::SignedInt(ast::TyI8)) => "i8",
attr::ReprInt(_, attr::SignedInt(ast::TyI16)) => "i16",
attr::ReprInt(_, attr::SignedInt(ast::TyI32)) => "i32",
attr::ReprInt(_, attr::SignedInt(ast::TyI64)) => "i64",
attr::ReprInt(_, attr::UnsignedInt(ast::TyUs)) => "usize",
attr::ReprInt(_, attr::UnsignedInt(ast::TyU8)) => "u8",
attr::ReprInt(_, attr::UnsignedInt(ast::TyU16)) => "u16",
attr::ReprInt(_, attr::UnsignedInt(ast::TyU32)) => "u32",
attr::ReprInt(_, attr::UnsignedInt(ast::TyU64)) => "u64",
}
}
}
repr_type_name
}
impl<'a> MethodDef<'a> {
fn call_substructure_method(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef,
type_ident: Ident,
self_args: &[P<Expr>],
nonself_args: &[P<Expr>],
fields: &SubstructureFields)
-> P<Expr> {
let substructure = Substructure {
type_ident: type_ident,
method_ident: cx.ident_of(self.name),
self_args: self_args,
nonself_args: nonself_args,
fields: fields
};
let mut f = self.combine_substructure.borrow_mut();
let f: &mut CombineSubstructureFunc = &mut *f;
f(cx, trait_.span, &substructure)
}
fn get_ret_ty(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef,
generics: &Generics,
type_ident: Ident)
-> P<ast::Ty> {
self.ret_ty.to_ty(cx, trait_.span, type_ident, generics)
}
fn is_static(&self) -> bool {
self.explicit_self.is_none()
}
fn split_self_nonself_args(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef,
type_ident: Ident,
generics: &Generics)
-> (ast::ExplicitSelf, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) {
let mut self_args = Vec::new();
let mut nonself_args = Vec::new();
let mut arg_tys = Vec::new();
let mut nonstatic = false;
let ast_explicit_self = match self.explicit_self {
Some(ref self_ptr) => {
let (self_expr, explicit_self) =
ty::get_explicit_self(cx, trait_.span, self_ptr);
self_args.push(self_expr);
nonstatic = true;
explicit_self
}
None => codemap::respan(trait_.span, ast::SelfStatic),
};
for (i, ty) in self.args.iter().enumerate() {
let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
let ident = cx.ident_of(&format!("__arg_{}", i));
arg_tys.push((ident, ast_ty));
let arg_expr = cx.expr_ident(trait_.span, ident);
match *ty {
Self_ if nonstatic => {
self_args.push(arg_expr);
}
Ptr(ref ty, _) if **ty == Self_ && nonstatic => {
self_args.push(cx.expr_deref(trait_.span, arg_expr))
}
_ => {
nonself_args.push(arg_expr);
}
}
}
(ast_explicit_self, self_args, nonself_args, arg_tys)
}
fn create_method(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef,
type_ident: Ident,
generics: &Generics,
abi: Abi,
explicit_self: ast::ExplicitSelf,
arg_types: Vec<(Ident, P<ast::Ty>)> ,
body: P<Expr>) -> P<ast::ImplItem> {
let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics);
let self_arg = match explicit_self.node {
ast::SelfStatic => None,
_ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, special_idents::self_))
};
let args = {
let args = arg_types.into_iter().map(|(name, ty)| {
cx.arg(trait_.span, name, ty)
});
self_arg.into_iter().chain(args).collect()
};
let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
let method_ident = cx.ident_of(self.name);
let fn_decl = cx.fn_decl(args, ret_type);
let body_block = cx.block_expr(body);
let unsafety = if self.is_unsafe {
ast::Unsafety::Unsafe
} else {
ast::Unsafety::Normal
};
P(ast::ImplItem {
id: ast::DUMMY_NODE_ID,
attrs: self.attributes.clone(),
span: trait_.span,
vis: ast::Inherited,
ident: method_ident,
node: ast::MethodImplItem(ast::MethodSig {
generics: fn_generics,
abi: abi,
explicit_self: explicit_self,
unsafety: unsafety,
constness: ast::Constness::NotConst,
decl: fn_decl
}, body_block)
})
}
fn expand_struct_method_body<'b>(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef<'b>,
struct_def: &'b StructDef,
type_ident: Ident,
self_args: &[P<Expr>],
nonself_args: &[P<Expr>])
-> P<Expr> {
let mut raw_fields = Vec::new(); let mut patterns = Vec::new();
for i in 0..self_args.len() {
let struct_path= cx.path(DUMMY_SP, vec!( type_ident ));
let (pat, ident_expr) =
trait_.create_struct_pattern(cx,
struct_path,
struct_def,
&format!("__self_{}",
i),
ast::MutImmutable);
patterns.push(pat);
raw_fields.push(ident_expr);
}
let fields = if !raw_fields.is_empty() {
let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter());
let first_field = raw_fields.next().unwrap();
let mut other_fields: Vec<vec::IntoIter<_>>
= raw_fields.collect();
first_field.map(|(span, opt_id, field, attrs)| {
FieldInfo {
span: span,
name: opt_id,
self_: field,
other: other_fields.iter_mut().map(|l| {
match l.next().unwrap() {
(_, _, ex, _) => ex
}
}).collect(),
attrs: attrs,
}
}).collect()
} else {
cx.span_bug(trait_.span,
"no self arguments to non-static method in generic \
`derive`")
};
let mut body = self.call_substructure_method(
cx,
trait_,
type_ident,
self_args,
nonself_args,
&Struct(fields));
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
}
fn expand_static_struct_method_body(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef,
struct_def: &StructDef,
type_ident: Ident,
self_args: &[P<Expr>],
nonself_args: &[P<Expr>])
-> P<Expr> {
let summary = trait_.summarise_struct(cx, struct_def);
self.call_substructure_method(cx,
trait_,
type_ident,
self_args, nonself_args,
&StaticStruct(struct_def, summary))
}
fn expand_enum_method_body<'b>(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef<'b>,
enum_def: &'b EnumDef,
type_attrs: &[ast::Attribute],
type_ident: Ident,
self_args: Vec<P<Expr>>,
nonself_args: &[P<Expr>])
-> P<Expr> {
self.build_enum_match_tuple(
cx, trait_, enum_def, type_attrs, type_ident, self_args, nonself_args)
}
fn build_enum_match_tuple<'b>(
&self,
cx: &mut ExtCtxt,
trait_: &TraitDef<'b>,
enum_def: &'b EnumDef,
type_attrs: &[ast::Attribute],
type_ident: Ident,
self_args: Vec<P<Expr>>,
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)| {
if arg_count == 0 {
"__self".to_string()
} else {
format!("__arg_{}", arg_count)
}
})
.collect::<Vec<String>>();
let self_arg_idents = self_arg_names.iter()
.map(|name|cx.ident_of(&name[..]))
.collect::<Vec<ast::Ident>>();
let vi_idents: Vec<ast::Ident> = self_arg_names.iter()
.map(|name| { let vi_suffix = format!("{}_vi", &name[..]);
cx.ident_of(&vi_suffix[..]) })
.collect::<Vec<ast::Ident>>();
let catch_all_substructure = EnumNonMatchingCollapsed(
self_arg_idents, &variants[..], &vi_idents[..]);
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 (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)
};
let mut subpats = Vec::with_capacity(self_arg_names.len());
let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1);
let first_self_pat_idents = {
let (p, idents) = mk_self_pat(cx, &self_arg_names[0]);
subpats.push(p);
idents
};
for self_arg_name in &self_arg_names[1..] {
let (p, idents) = mk_self_pat(cx, &self_arg_name[..]);
subpats.push(p);
self_pats_idents.push(idents);
}
let single_pat = cx.pat_tuple(sp, subpats);
let field_tuples = first_self_pat_idents.into_iter().enumerate()
.map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| {
let others = self_pats_idents.iter().map(|fields| {
let (_, _opt_ident, ref other_getter_expr, _) =
fields[field_index];
assert!(opt_ident == _opt_ident);
other_getter_expr.clone()
}).collect::<Vec<P<Expr>>>();
FieldInfo { span: sp,
name: opt_ident,
self_: self_getter_expr,
other: others,
attrs: attrs,
}
}).collect::<Vec<FieldInfo>>();
let substructure = EnumMatching(index,
&**variant,
field_tuples);
let arm_expr = self.call_substructure_method(
cx, trait_, type_ident, &self_args[..], nonself_args,
&substructure);
cx.arm(sp, vec![single_pat], arm_expr)
}).collect();
if variants.len() > 1 && self_args.len() > 1 {
let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new();
let mut discriminant_test = cx.expr_bool(sp, true);
let target_type_name =
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) {
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())]);
let variant_value = cx.expr_block(P(ast::Block {
stmts: vec![],
expr: Some(call),
id: ast::DUMMY_NODE_ID,
rules: ast::UnsafeBlock(ast::CompilerGenerated),
span: sp }));
let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name));
let variant_disr = cx.expr_cast(sp, variant_value, target_ty);
let let_stmt = cx.stmt_let(sp, false, ident, variant_disr);
index_let_stmts.push(let_stmt);
match first_ident {
Some(first) => {
let first_expr = cx.expr_ident(sp, first);
let id = cx.expr_ident(sp, ident);
let test = cx.expr_binary(sp, ast::BiEq, first_expr, id);
discriminant_test = cx.expr_binary(sp, ast::BiAnd, discriminant_test, test)
}
None => {
first_ident = Some(ident);
}
}
}
let arm_expr = self.call_substructure_method(
cx, trait_, type_ident, &self_args[..], nonself_args,
&catch_all_substructure);
let path = cx.std_path(&["intrinsics", "unreachable"]);
let call = cx.expr_call_global(
sp, path, vec![]);
let unreachable = cx.expr_block(P(ast::Block {
stmts: vec![],
expr: Some(call),
id: ast::DUMMY_NODE_ID,
rules: ast::UnsafeBlock(ast::CompilerGenerated),
span: sp }));
match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], unreachable));
let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
let all_match = cx.expr_match(sp, match_arg, match_arms);
let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr));
cx.expr_block(
cx.block_all(sp, index_let_stmts, Some(arm_expr)))
} else if variants.is_empty() {
cx.expr_unreachable(sp)
}
else {
let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg));
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
cx.expr_match(sp, match_arg, match_arms)
}
}
fn expand_static_enum_method_body(&self,
cx: &mut ExtCtxt,
trait_: &TraitDef,
enum_def: &EnumDef,
type_ident: Ident,
self_args: &[P<Expr>],
nonself_args: &[P<Expr>])
-> P<Expr> {
let summary = enum_def.variants.iter().map(|v| {
let ident = v.node.name;
let summary = match v.node.kind {
ast::TupleVariantKind(ref args) => {
Unnamed(args.iter().map(|va| trait_.set_expn_info(cx, va.ty.span)).collect())
}
ast::StructVariantKind(ref struct_def) => {
trait_.summarise_struct(cx, &**struct_def)
}
};
(ident, v.span, summary)
}).collect();
self.call_substructure_method(cx, trait_, type_ident,
self_args, nonself_args,
&StaticEnum(enum_def, summary))
}
}
#[derive(PartialEq)] enum StructType {
Unknown, Record, Tuple
}
impl<'a> TraitDef<'a> {
fn set_expn_info(&self,
cx: &mut ExtCtxt,
mut to_set: Span) -> Span {
let trait_name = match self.path.path.last() {
None => cx.span_bug(self.span, "trait with empty path in generic `derive`"),
Some(name) => *name
};
to_set.expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
call_site: to_set,
callee: codemap::NameAndSpan {
format: codemap::MacroAttribute(intern(&format!("derive({})", trait_name))),
span: Some(self.span),
allow_internal_unstable: false,
}
});
to_set
}
fn summarise_struct(&self,
cx: &mut ExtCtxt,
struct_def: &StructDef) -> StaticFields {
let mut named_idents = Vec::new();
let mut just_spans = Vec::new();
for field in struct_def.fields.iter(){
let sp = self.set_expn_info(cx, field.span);
match field.node.kind {
ast::NamedField(ident, _) => named_idents.push((ident, sp)),
ast::UnnamedField(..) => just_spans.push(sp),
}
}
match (just_spans.is_empty(), named_idents.is_empty()) {
(false, false) => cx.span_bug(self.span,
"a struct with named and unnamed \
fields in generic `derive`"),
(_, false) => Named(named_idents),
(_, _) => Unnamed(just_spans)
}
}
fn create_subpatterns(&self,
cx: &mut ExtCtxt,
field_paths: Vec<ast::SpannedIdent> ,
mutbl: ast::Mutability)
-> Vec<P<ast::Pat>> {
field_paths.iter().map(|path| {
cx.pat(path.span,
ast::PatIdent(ast::BindByRef(mutbl), (*path).clone(), None))
}).collect()
}
fn create_struct_pattern(&self,
cx: &mut ExtCtxt,
struct_path: ast::Path,
struct_def: &'a StructDef,
prefix: &str,
mutbl: ast::Mutability)
-> (P<ast::Pat>, Vec<(Span, Option<Ident>,
P<Expr>,
&'a [ast::Attribute])>) {
if struct_def.fields.is_empty() {
return (cx.pat_enum(self.span, struct_path, vec![]), vec![]);
}
let mut paths = Vec::new();
let mut ident_expr = Vec::new();
let mut struct_type = Unknown;
for (i, struct_field) in struct_def.fields.iter().enumerate() {
let sp = self.set_expn_info(cx, struct_field.span);
let opt_id = match struct_field.node.kind {
ast::NamedField(ident, _) if (struct_type == Unknown ||
struct_type == Record) => {
struct_type = Record;
Some(ident)
}
ast::UnnamedField(..) if (struct_type == Unknown ||
struct_type == Tuple) => {
struct_type = Tuple;
None
}
_ => {
cx.span_bug(sp, "a struct with named and unnamed fields in `derive`");
}
};
let ident = cx.ident_of(&format!("{}_{}", prefix, i));
paths.push(codemap::Spanned{span: sp, node: ident});
let val = cx.expr(
sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident)))));
ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..]));
}
let subpats = self.create_subpatterns(cx, paths, mutbl);
let pattern = if struct_type == Record {
let field_pats = subpats.into_iter().zip(&ident_expr)
.map(|(pat, &(_, id, _, _))| {
codemap::Spanned {
span: pat.span,
node: ast::FieldPat { ident: id.unwrap(), pat: pat, is_shorthand: false },
}
}).collect();
cx.pat_struct(self.span, struct_path, field_pats)
} else {
cx.pat_enum(self.span, struct_path, subpats)
};
(pattern, ident_expr)
}
fn create_enum_variant_pattern(&self,
cx: &mut ExtCtxt,
enum_ident: ast::Ident,
variant: &'a ast::Variant,
prefix: &str,
mutbl: ast::Mutability)
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
let variant_ident = variant.node.name;
let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]);
match variant.node.kind {
ast::TupleVariantKind(ref variant_args) => {
if variant_args.is_empty() {
return (cx.pat_enum(variant.span, variant_path, vec![]), vec![]);
}
let mut paths = Vec::new();
let mut ident_expr: Vec<(_, _, _, &'a [ast::Attribute])> = Vec::new();
for (i, va) in variant_args.iter().enumerate() {
let sp = self.set_expn_info(cx, va.ty.span);
let ident = cx.ident_of(&format!("{}_{}", prefix, i));
let path1 = codemap::Spanned{span: sp, node: ident};
paths.push(path1);
let expr_path = cx.expr_path(cx.path_ident(sp, ident));
let val = cx.expr(sp, ast::ExprParen(cx.expr_deref(sp, expr_path)));
ident_expr.push((sp, None, val, &[]));
}
let subpats = self.create_subpatterns(cx, paths, mutbl);
(cx.pat_enum(variant.span, variant_path, subpats),
ident_expr)
}
ast::StructVariantKind(ref struct_def) => {
self.create_struct_pattern(cx, variant_path, &**struct_def,
prefix, mutbl)
}
}
}
}
pub fn cs_fold<F>(use_foldl: bool,
mut f: F,
base: P<Expr>,
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
-> P<Expr> where
F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
if use_foldl {
all_fields.iter().fold(base, |old, field| {
f(cx,
field.span,
old,
field.self_.clone(),
&field.other)
})
} else {
all_fields.iter().rev().fold(base, |old, field| {
f(cx,
field.span,
old,
field.self_.clone(),
&field.other)
})
}
},
EnumNonMatchingCollapsed(ref all_args, _, tuple) =>
enum_nonmatch_f(cx, trait_span, (&all_args[..], tuple),
substructure.nonself_args),
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, "static function in `derive`")
}
}
}
#[inline]
pub fn cs_same_method<F>(f: F,
mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
-> P<Expr> where
F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
{
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
let called = all_fields.iter().map(|field| {
cx.expr_method_call(field.span,
field.self_.clone(),
substructure.method_ident,
field.other.iter()
.map(|e| cx.expr_addr_of(field.span, e.clone()))
.collect())
}).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`")
}
}
}
#[inline]
pub fn cs_same_method_fold<F>(use_foldl: bool,
mut f: F,
base: P<Expr>,
enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
-> P<Expr> where
F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>) -> P<Expr>,
{
cs_same_method(
|cx, span, vals| {
if use_foldl {
vals.into_iter().fold(base.clone(), |old, new| {
f(cx, span, old, new)
})
} else {
vals.into_iter().rev().fold(base.clone(), |old, new| {
f(cx, span, old, new)
})
}
},
enum_nonmatch_f,
cx, trait_span, substructure)
}
#[inline]
pub fn cs_binop(binop: ast::BinOp_, base: P<Expr>,
enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt, trait_span: Span,
substructure: &Substructure) -> P<Expr> {
cs_same_method_fold(
true, |cx, span, old, new| {
cx.expr_binary(span,
binop,
old, new)
},
base,
enum_nonmatch_f,
cx, trait_span, substructure)
}
#[inline]
pub fn cs_or(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt, span: Span,
substructure: &Substructure) -> P<Expr> {
cs_binop(ast::BiOr, cx.expr_bool(span, false),
enum_nonmatch_f,
cx, span, substructure)
}
#[inline]
pub fn cs_and(enum_nonmatch_f: EnumNonMatchCollapsedFunc,
cx: &mut ExtCtxt, span: Span,
substructure: &Substructure) -> P<Expr> {
cs_binop(ast::BiAnd, cx.expr_bool(span, true),
enum_nonmatch_f,
cx, span, substructure)
}