//! and lifetimes for methods.)
//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`)
//!
-//! The most important thing for implementers is the `Substructure` and
+//! The most important thing for implementors is the `Substructure` and
//! `SubstructureFields` objects. The latter groups 5 possibilities of the
//! arguments:
//!
pub use self::StaticFields::*;
pub use self::SubstructureFields::*;
-use self::StructType::*;
use std::cell::RefCell;
-use std::collections::HashSet;
+use std::iter;
use std::vec;
-use syntax::abi::Abi;
-use syntax::abi;
-use syntax::ast;
-use syntax::ast::{EnumDef, Expr, Ident, Generics, VariantData};
-use syntax::ast_util;
+use rustc_target::spec::abi::Abi;
+use syntax::ast::{self, BinOpKind, EnumDef, Expr, Generics, Ident, PatKind};
+use syntax::ast::{VariantData, GenericParamKind, GenericArg};
use syntax::attr;
-use syntax::attr::AttrMetaMethods;
-use syntax::ext::base::{ExtCtxt, Annotatable};
+use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
-use syntax::codemap::{self, DUMMY_SP};
-use syntax::codemap::Span;
-use syntax::errors::Handler;
+use syntax::codemap::{self, respan};
use syntax::util::move_map::MoveMap;
-use syntax::parse::token::{intern, InternedString};
-use syntax::parse::token::special_idents;
use syntax::ptr::P;
+use syntax::symbol::{Symbol, keywords};
+use syntax_pos::{DUMMY_SP, Span};
+use errors::Handler;
use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty};
+use deriving;
+
pub mod ty;
pub struct TraitDef<'a> {
/// Is it an `unsafe` trait?
pub is_unsafe: bool,
+ /// Can this trait be derived for unions?
+ pub supports_unions: bool,
+
pub methods: Vec<MethodDef<'a>>,
pub associated_types: Vec<(ast::Ident, Ty<'a>)>,
pub explicit_self: Option<Option<PtrTy<'a>>>,
/// Arguments other than the self argument
- pub args: Vec<Ty<'a>>,
+ pub args: Vec<(Ty<'a>, &'a str)>,
/// Return type
pub ret_ty: Ty<'a>,
// Is it an `unsafe fn`?
pub is_unsafe: bool,
+ /// Can we combine fieldless variants for enums into a single match arm?
+ pub unify_fieldless_variants: bool,
+
pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
}
pub self_args: &'a [P<Expr>],
/// verbatim access to any other arguments
pub nonself_args: &'a [P<Expr>],
- pub fields: &'a SubstructureFields<'a>
+ pub fields: &'a SubstructureFields<'a>,
}
/// Summary of the relevant parts of a struct/enum field.
/// Fields for a static method
pub enum StaticFields {
- /// Tuple structs/enum variants like this.
- Unnamed(Vec<Span>),
+ /// Tuple and unit structs/enum variants like this.
+ Unnamed(Vec<Span>, bool /*is tuple*/),
/// Normal structs/struct variants.
Named(Vec<(Ident, Span)>),
}
/// A summary of the possible sets of fields.
pub enum SubstructureFields<'a> {
- Struct(Vec<FieldInfo<'a>>),
- /// Matching variants of the enum: variant index, ast::Variant,
+ Struct(&'a ast::VariantData, Vec<FieldInfo<'a>>),
+ /// Matching variants of the enum: variant index, variant count, ast::Variant,
/// fields: the field name is only non-`None` in the case of a struct
/// variant.
- EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
+ EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo<'a>>),
/// Non-matching variants of the enum, but with all state hidden from
/// the consequent code. The first component holds `Ident`s for all of
/// 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<Ident>, &'a [ast::Variant], &'a [Ident]),
/// A static method where `Self` is a struct.
StaticStruct(&'a ast::VariantData, StaticFields),
/// Combine the values of all the fields together. The last argument is
/// all the fields of all the structures.
pub type CombineSubstructureFunc<'a> =
- Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>;
+ Box<dyn FnMut(&mut ExtCtxt, Span, &Substructure) -> 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
/// 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<dyn FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>;
pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>)
- -> RefCell<CombineSubstructureFunc<'a>> {
+ -> RefCell<CombineSubstructureFunc<'a>> {
RefCell::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`.
-fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> {
+fn find_type_parameters(ty: &ast::Ty,
+ ty_param_names: &[ast::Name],
+ span: Span,
+ cx: &ExtCtxt)
+ -> Vec<P<ast::Ty>> {
use syntax::visit;
- struct Visitor<'a> {
+ struct Visitor<'a, 'b: 'a> {
+ cx: &'a ExtCtxt<'b>,
+ span: Span,
ty_param_names: &'a [ast::Name],
types: Vec<P<ast::Ty>>,
}
- impl<'a> visit::Visitor<'a> for Visitor<'a> {
+ impl<'a, 'b> visit::Visitor<'a> for Visitor<'a, 'b> {
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 => {}
+ if let ast::TyKind::Path(_, ref path) = ty.node {
+ if let Some(segment) = path.segments.first() {
+ if self.ty_param_names.contains(&segment.ident.name) {
+ self.types.push(P(ty.clone()));
}
}
- _ => {}
}
visit::walk_ty(self, ty)
}
+
+ fn visit_mac(&mut self, mac: &ast::Mac) {
+ let span = mac.span.with_ctxt(self.span.ctxt());
+ self.cx.span_err(span, "`derive` cannot be used on items with type macros");
+ }
}
let mut visitor = Visitor {
- ty_param_names: ty_param_names,
+ ty_param_names,
types: Vec::new(),
+ span,
+ cx,
};
visit::Visitor::visit_ty(&mut visitor, ty);
}
impl<'a> TraitDef<'a> {
- pub fn expand(&self,
+ pub fn expand(self,
cx: &mut ExtCtxt,
mitem: &ast::MetaItem,
item: &'a Annotatable,
- push: &mut FnMut(Annotatable))
- {
+ push: &mut dyn 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 dyn FnMut(Annotatable),
+ from_scratch: bool) {
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)
+ let is_packed = item.attrs.iter().any(|attr| {
+ for r in attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr) {
+ if let attr::ReprPacked(_) = r {
+ return true;
+ }
}
- ast::ItemEnum(ref enum_def, ref generics) => {
- self.expand_enum_def(cx,
- enum_def,
- &item.attrs,
- item.ident,
- generics)
+ false
+ });
+ let has_no_type_params = match item.node {
+ ast::ItemKind::Struct(_, ref generics) |
+ ast::ItemKind::Enum(_, ref generics) |
+ ast::ItemKind::Union(_, ref generics) => {
+ !generics.params.iter().any(|param| match param.kind {
+ ast::GenericParamKind::Type { .. } => true,
+ _ => false,
+ })
}
_ => {
- cx.span_err(mitem.span,
- "`derive` may only be applied to structs and enums");
+ // Non-ADT derive is an error, but it should have been
+ // set earlier; see
+ // libsyntax/ext/expand.rs:MacroExpander::expand()
return;
}
};
+ let is_always_copy =
+ attr::contains_name(&item.attrs, "rustc_copy_clone_marker") &&
+ has_no_type_params;
+ let use_temporaries = is_packed && is_always_copy;
+
+ let newitem = match item.node {
+ ast::ItemKind::Struct(ref struct_def, ref generics) => {
+ self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch,
+ use_temporaries)
+ }
+ ast::ItemKind::Enum(ref enum_def, ref generics) => {
+ // We ignore `use_temporaries` here, because
+ // `repr(packed)` enums cause an error later on.
+ //
+ // This can only cause further compilation errors
+ // downstream in blatantly illegal code, so it
+ // is fine.
+ 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, from_scratch,
+ use_temporaries)
+ } else {
+ cx.span_err(mitem.span,
+ "this trait cannot be derived for unions");
+ return;
+ }
+ }
+ _ => unreachable!(),
+ };
// Keep the lint attributes of the previous item to control how the
// generated implementations are linted
let mut attrs = newitem.attrs.clone();
- attrs.extend(item.attrs.iter().filter(|a| {
- match &a.name()[..] {
- "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
- _ => false,
- }
- }).cloned());
- push(Annotatable::Item(P(ast::Item {
- attrs: attrs,
- ..(*newitem).clone()
- })))
+ attrs.extend(item.attrs
+ .iter()
+ .filter(|a| {
+ match &*a.name().as_str() {
+ "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => 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");
+ // Non-Item derive is an error, but it should have been
+ // set earlier; see
+ // libsyntax/ext/expand.rs:MacroExpander::expand()
+ return;
}
}
}
/// Given that we are deriving a trait `DerivedTrait` for a type like:
///
- /// ```ignore
+ /// ```ignore (only-for-syntax-highlight)
/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
/// a: A,
/// b: B::Item,
///
/// create an impl like:
///
- /// ```ignore
+ /// ```ignore (only-for-syntax-highlight)
/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
/// C: WhereTrait,
/// A: DerivedTrait + B1 + ... + BN,
type_ident: Ident,
generics: &Generics,
field_tys: Vec<P<ast::Ty>>,
- methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> {
+ methods: Vec<ast::ImplItem>)
+ -> P<ast::Item> {
let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
// Transform associated types from `deriving::ty::Ty` into `ast::ImplItem`
let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| {
- P(ast::ImplItem {
+ ast::ImplItem {
id: ast::DUMMY_NODE_ID,
span: self.span,
- ident: ident,
- vis: ast::Inherited,
+ ident,
+ vis: respan(self.span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+ defaultness: ast::Defaultness::Final,
attrs: Vec::new(),
- node: ast::ImplItemKind::Type(type_def.to_ty(cx,
- self.span,
- type_ident,
- generics
- )),
- })
+ generics: Generics::default(),
+ node: ast::ImplItemKind::Type(type_def.to_ty(cx, self.span, type_ident, generics)),
+ tokens: None,
+ }
});
- 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();
-
- // Copy the lifetimes
- lifetimes.extend(generics.lifetimes.iter().cloned());
-
- // Create the type parameters.
- ty_params.extend(generics.ty_params.iter().map(|ty_param| {
- // I don't think this can be moved out of the loop, since
- // a TyParamBound requires an ast id
- let mut bounds: Vec<_> =
- // extra restrictions on the generics parameters to the type being derived upon
- self.additional_bounds.iter().map(|p| {
- cx.typarambound(p.to_path(cx, self.span,
- type_ident, generics))
- }).collect();
-
- // require the current trait
- bounds.push(cx.typarambound(trait_path.clone()));
-
- // also add in any bounds from the declaration
- for declared_bound in ty_param.bounds.iter() {
- bounds.push((*declared_bound).clone());
+ let Generics { mut params, mut where_clause, span } = self.generics
+ .to_generics(cx, self.span, type_ident, generics);
+
+ // Create the generic parameters
+ params.extend(generics.params.iter().map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => param.clone(),
+ GenericParamKind::Type { .. } => {
+ // I don't think this can be moved out of the loop, since
+ // a GenericBound requires an ast id
+ let mut bounds: Vec<_> =
+ // extra restrictions on the generics parameters to the
+ // type being derived upon
+ self.additional_bounds.iter().map(|p| {
+ cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))
+ }).chain(
+ // require the current trait
+ iter::once(cx.trait_bound(trait_path.clone()))
+ ).chain(
+ // also add in any bounds from the declaration
+ param.bounds.iter().cloned()
+ ).collect();
+
+ cx.typaram(self.span, param.ident, vec![], bounds, None)
}
-
- cx.typaram(self.span,
- ty_param.ident,
- P::from_vec(bounds),
- None)
}));
// and similarly for where clauses
ast::WherePredicate::BoundPredicate(ref wb) => {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
span: self.span,
- bound_lifetimes: wb.bound_lifetimes.clone(),
+ bound_generic_params: wb.bound_generic_params.clone(),
bounded_ty: wb.bounded_ty.clone(),
- bounds: P::from_vec(wb.bounds.iter().cloned().collect())
+ bounds: 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()
+ 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()
+ lhs_ty: we.lhs_ty.clone(),
+ rhs_ty: we.rhs_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();
+ {
+ // Extra scope required here so ty_params goes out of scope before params is moved
- 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 we have already handled this type, skip it
- 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;
+ let mut ty_params = params.iter()
+ .filter_map(|param| match param.kind {
+ ast::GenericParamKind::Type { .. } => Some(param),
+ _ => None,
+ })
+ .peekable();
+
+ if ty_params.peek().is_some() {
+ let ty_param_names: Vec<ast::Name> = ty_params
+ .map(|ty_param| ty_param.ident.name)
+ .collect();
+
+ for field_ty in field_tys {
+ let tys = find_type_parameters(&field_ty, &ty_param_names, self.span, cx);
+
+ for ty in tys {
+ // if we have already handled this type, skip it
+ if let ast::TyKind::Path(_, ref p) = ty.node {
+ if p.segments.len() == 1 &&
+ ty_param_names.contains(&p.segments[0].ident.name) {
+ continue;
+ };
+ }
+ let mut bounds: Vec<_> = self.additional_bounds
+ .iter()
+ .map(|p| {
+ cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))
+ })
+ .collect();
+
+ // require the current trait
+ bounds.push(cx.trait_bound(trait_path.clone()));
+
+ let predicate = ast::WhereBoundPredicate {
+ span: self.span,
+ bound_generic_params: Vec::new(),
+ bounded_ty: ty,
+ bounds,
};
- 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();
-
- // require the current trait
- bounds.push(cx.typarambound(trait_path.clone()));
- let predicate = ast::WhereBoundPredicate {
- span: self.span,
- bound_lifetimes: vec![],
- bounded_ty: ty,
- bounds: P::from_vec(bounds),
- };
-
- let predicate = ast::WherePredicate::BoundPredicate(predicate);
- where_clause.predicates.push(predicate);
+ let predicate = ast::WherePredicate::BoundPredicate(predicate);
+ where_clause.predicates.push(predicate);
+ }
}
}
}
let trait_generics = Generics {
- lifetimes: lifetimes,
- ty_params: P::from_vec(ty_params),
- where_clause: where_clause
+ params,
+ where_clause,
+ span,
};
// Create the reference to the trait.
let trait_ref = cx.trait_ref(trait_path);
- // Create the type parameters on the `self` 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_params: Vec<_> = generics.params.iter().map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ GenericArg::Lifetime(cx.lifetime(self.span, param.ident))
+ }
+ GenericParamKind::Type { .. } => {
+ GenericArg::Type(cx.ty_ident(self.span, param.ident))
+ }
+ }).collect();
// Create the type of `self`.
- 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")));
+ let path = cx.path_all(self.span, false, vec![type_ident], self_params, vec![]);
+ let self_type = cx.ty_path(path);
+
+ let attr = cx.attribute(self.span,
+ cx.meta_word(self.span,
+ Symbol::intern("automatically_derived")));
// Just mark it now since we know that it'll end up used downstream
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 unused_qual = {
+ let word = cx.meta_list_item_word(self.span, Symbol::intern("unused_qualifications"));
+ cx.attribute(self.span, cx.meta_list(self.span, Symbol::intern("allow"), vec![word]))
+ };
+
let mut a = vec![attr, unused_qual];
a.extend(self.attributes.iter().cloned());
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()))
+ cx.item(self.span,
+ keywords::Invalid.ident(),
+ a,
+ ast::ItemKind::Impl(unsafety,
+ ast::ImplPolarity::Positive,
+ ast::Defaultness::Final,
+ 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 VariantData,
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())
+ generics: &Generics,
+ from_scratch: bool,
+ use_temporaries: bool)
+ -> P<ast::Item> {
+ let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
+ .iter()
+ .map(|field| field.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[..])
- };
+ 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 from_scratch || 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[..],
+ use_temporaries)
+ };
- method_def.create_method(cx,
- self,
- type_ident,
- generics,
- abi::Rust,
- explicit_self,
- tys,
- body)
- }).collect();
+ 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)
}
enum_def: &'a EnumDef,
type_attrs: &[ast::Attribute],
type_ident: Ident,
- generics: &Generics) -> P<ast::Item> {
+ generics: &Generics,
+ from_scratch: bool)
+ -> P<ast::Item> {
let mut field_tys = Vec::new();
for variant in &enum_def.variants {
- field_tys.extend(variant.node.data.fields().iter()
- .map(|field| field.node.ty.clone()));
+ field_tys.extend(variant.node
+ .data
+ .fields()
+ .iter()
+ .map(|field| field.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[..])
- };
+ 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 from_scratch || 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();
+ 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: &Handler,
- type_attrs: &[ast::Attribute]) -> &'static str {
- let mut repr_type_name = "i32";
+fn find_repr_type_name(diagnostic: &Handler, type_attrs: &[ast::Attribute]) -> &'static str {
+ let mut repr_type_name = "isize";
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",
+ attr::ReprPacked(_) | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
+ continue,
+
+ attr::ReprC => "i32",
+
+ attr::ReprInt(attr::SignedInt(ast::IntTy::Isize)) => "isize",
+ attr::ReprInt(attr::SignedInt(ast::IntTy::I8)) => "i8",
+ attr::ReprInt(attr::SignedInt(ast::IntTy::I16)) => "i16",
+ attr::ReprInt(attr::SignedInt(ast::IntTy::I32)) => "i32",
+ attr::ReprInt(attr::SignedInt(ast::IntTy::I64)) => "i64",
+ attr::ReprInt(attr::SignedInt(ast::IntTy::I128)) => "i128",
+
+ attr::ReprInt(attr::UnsignedInt(ast::UintTy::Usize)) => "usize",
+ attr::ReprInt(attr::UnsignedInt(ast::UintTy::U8)) => "u8",
+ attr::ReprInt(attr::UnsignedInt(ast::UintTy::U16)) => "u16",
+ attr::ReprInt(attr::UnsignedInt(ast::UintTy::U32)) => "u32",
+ attr::ReprInt(attr::UnsignedInt(ast::UintTy::U64)) => "u64",
+ attr::ReprInt(attr::UnsignedInt(ast::UintTy::U128)) => "u128",
}
}
}
self_args: &[P<Expr>],
nonself_args: &[P<Expr>],
fields: &SubstructureFields)
- -> P<Expr> {
+ -> P<Expr> {
let substructure = Substructure {
- type_ident: type_ident,
+ type_ident,
method_ident: cx.ident_of(self.name),
- self_args: self_args,
- nonself_args: nonself_args,
- fields: fields
+ self_args,
+ nonself_args,
+ fields,
};
let mut f = self.combine_substructure.borrow_mut();
let f: &mut CombineSubstructureFunc = &mut *f;
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>)>) {
+ fn split_self_nonself_args
+ (&self,
+ cx: &mut ExtCtxt,
+ trait_: &TraitDef,
+ type_ident: Ident,
+ generics: &Generics)
+ -> (Option<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);
+ let ast_explicit_self = self.explicit_self.as_ref().map(|self_ptr| {
+ let (self_expr, explicit_self) = ty::get_explicit_self(cx, trait_.span, self_ptr);
- self_args.push(self_expr);
- nonstatic = true;
+ self_args.push(self_expr);
+ nonstatic = true;
- explicit_self
- }
- None => codemap::respan(trait_.span, ast::SelfStatic),
- };
+ explicit_self
+ });
- for (i, ty) in self.args.iter().enumerate() {
+ for (ty, name) in self.args.iter() {
let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
- let ident = cx.ident_of(&format!("__arg_{}", i));
+ let ident = cx.ident_of(name).gensym();
arg_tys.push((ident, ast_ty));
let arg_expr = cx.expr_ident(trait_.span, ident);
match *ty {
// for static methods, just treat any Self
// arguments as a normal arg
- Self_ if nonstatic => {
+ Self_ if nonstatic => {
self_args.push(arg_expr);
}
- Ptr(ref ty, _) if **ty == Self_ && nonstatic => {
+ Ptr(ref ty, _) if (if let Self_ = **ty { true } else { false }) && nonstatic => {
self_args.push(cx.expr_deref(trait_.span, arg_expr))
}
_ => {
type_ident: Ident,
generics: &Generics,
abi: Abi,
- explicit_self: ast::ExplicitSelf,
- arg_types: Vec<(Ident, P<ast::Ty>)> ,
- body: P<Expr>) -> P<ast::ImplItem> {
+ explicit_self: Option<ast::ExplicitSelf>,
+ arg_types: Vec<(Ident, P<ast::Ty>)>,
+ body: P<Expr>)
+ -> ast::ImplItem {
+
// create the generics that aren't for Self
let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics);
- let self_arg = match explicit_self.node {
- ast::SelfStatic => None,
- // creating fresh self id
- _ => 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 self_args = explicit_self.map(|explicit_self| {
+ ast::Arg::from_self(explicit_self,
+ keywords::SelfValue.ident().with_span_pos(trait_.span))
+ });
+ let nonself_args = arg_types.into_iter()
+ .map(|(name, ty)| cx.arg(trait_.span, name, ty));
+ self_args.into_iter().chain(nonself_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 fn_decl = cx.fn_decl(args, ast::FunctionRetTy::Ty(ret_type));
let body_block = cx.block_expr(body);
let unsafety = if self.is_unsafe {
};
// Create the method.
- P(ast::ImplItem {
+ ast::ImplItem {
id: ast::DUMMY_NODE_ID,
attrs: self.attributes.clone(),
+ generics: fn_generics,
span: trait_.span,
- vis: ast::Inherited,
+ vis: respan(trait_.span.shrink_to_lo(), ast::VisibilityKind::Inherited),
+ defaultness: ast::Defaultness::Final,
ident: method_ident,
node: ast::ImplItemKind::Method(ast::MethodSig {
- generics: fn_generics,
- abi: abi,
- explicit_self: explicit_self,
- unsafety: unsafety,
- constness: ast::Constness::NotConst,
- decl: fn_decl
- }, body_block)
- })
+ header: ast::FnHeader {
+ unsafety, abi,
+ ..ast::FnHeader::default()
+ },
+ decl: fn_decl,
+ },
+ body_block),
+ tokens: None,
+ }
}
- /// ```ignore
+ /// ```
/// #[derive(PartialEq)]
+ /// # struct Dummy;
/// struct A { x: i32, y: i32 }
///
/// // equivalent to:
/// impl PartialEq for A {
- /// fn eq(&self, __arg_1: &A) -> bool {
+ /// fn eq(&self, other: &A) -> bool {
/// match *self {
/// A {x: ref __self_0_0, y: ref __self_0_1} => {
- /// match *__arg_1 {
+ /// match *other {
/// A {x: ref __self_1_0, y: ref __self_1_1} => {
/// __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1)
/// }
/// }
/// }
/// }
+ ///
+ /// // or if A is repr(packed) - note fields are matched by-value
+ /// // instead of by-reference.
+ /// impl PartialEq for A {
+ /// fn eq(&self, other: &A) -> bool {
+ /// match *self {
+ /// A {x: __self_0_0, y: __self_0_1} => {
+ /// match other {
+ /// A {x: __self_1_0, y: __self_1_1} => {
+ /// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1)
+ /// }
+ /// }
+ /// }
+ /// }
+ /// }
+ /// }
/// ```
fn expand_struct_method_body<'b>(&self,
- cx: &mut ExtCtxt,
- trait_: &TraitDef<'b>,
- struct_def: &'b VariantData,
- type_ident: Ident,
- self_args: &[P<Expr>],
- nonself_args: &[P<Expr>])
- -> P<Expr> {
+ cx: &mut ExtCtxt,
+ trait_: &TraitDef<'b>,
+ struct_def: &'b VariantData,
+ type_ident: Ident,
+ self_args: &[P<Expr>],
+ nonself_args: &[P<Expr>],
+ use_temporaries: bool)
+ -> P<Expr> {
let mut raw_fields = Vec::new(); // Vec<[fields of self],
// [fields of next Self arg], [etc]>
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);
+ 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::Mutability::Immutable,
+ use_temporaries);
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();
+ 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()
+ FieldInfo {
+ span,
+ name: opt_id,
+ self_: field,
+ other: other_fields.iter_mut()
+ .map(|l| {
+ match l.next().unwrap() {
+ (.., ex, _) => ex,
+ }
+ })
+ .collect(),
+ attrs,
+ }
+ })
+ .collect()
} else {
cx.span_bug(trait_.span,
"no self arguments to non-static method in generic \
};
// body of the inner most destructuring match
- let mut body = self.call_substructure_method(
- cx,
- trait_,
- type_ident,
- self_args,
- nonself_args,
- &Struct(fields));
+ let mut body = self.call_substructure_method(cx,
+ trait_,
+ type_ident,
+ self_args,
+ nonself_args,
+ &Struct(struct_def, fields));
// 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 = cx.expr_match(trait_.span,
+ arg_expr.clone(),
+ vec![cx.arm(trait_.span, vec![pat.clone()], body)])
}
+
body
}
type_ident: Ident,
self_args: &[P<Expr>],
nonself_args: &[P<Expr>])
- -> P<Expr> {
+ -> P<Expr> {
let summary = trait_.summarise_struct(cx, struct_def);
self.call_substructure_method(cx,
trait_,
type_ident,
- self_args, nonself_args,
+ self_args,
+ nonself_args,
&StaticStruct(struct_def, summary))
}
- /// ```ignore
+ /// ```
/// #[derive(PartialEq)]
+ /// # struct Dummy;
/// enum A {
/// A1,
/// A2(i32)
/// // is equivalent to
///
/// impl PartialEq for A {
- /// fn eq(&self, __arg_1: &A) -> ::bool {
- /// match (&*self, &*__arg_1) {
+ /// fn eq(&self, other: &A) -> ::bool {
+ /// match (&*self, &*other) {
/// (&A1, &A1) => true,
- /// (&A2(ref __self_0),
- /// &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)),
+ /// (&A2(ref self_0),
+ /// &A2(ref __arg_1_0)) => (*self_0).eq(&(*__arg_1_0)),
/// _ => {
/// let __self_vi = match *self { A1(..) => 0, A2(..) => 1 };
- /// let __arg_1_vi = match *__arg_1 { A1(..) => 0, A2(..) => 1 };
+ /// let __arg_1_vi = match *other { A1(..) => 0, A2(..) => 1 };
/// false
/// }
/// }
/// as their results are unused. The point of `__self_vi` and
/// `__arg_1_vi` is for `PartialOrd`; see #15503.)
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)
+ 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)
}
/// let __self0_vi = unsafe {
/// std::intrinsics::discriminant_value(&self) } as i32;
/// let __self1_vi = unsafe {
- /// std::intrinsics::discriminant_value(&__arg1) } as i32;
+ /// std::intrinsics::discriminant_value(&arg1) } as i32;
/// let __self2_vi = unsafe {
- /// std::intrinsics::discriminant_value(&__arg2) } as i32;
+ /// std::intrinsics::discriminant_value(&arg2) } as i32;
///
/// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... {
/// match (...) {
/// ... // catch-all remainder can inspect above variant index values.
/// }
/// ```
- 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> {
-
+ 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()
+ let self_arg_names = self_args.iter()
+ .enumerate()
.map(|(arg_count, _self_arg)| {
if arg_count == 0 {
"__self".to_string()
.collect::<Vec<String>>();
let self_arg_idents = self_arg_names.iter()
- .map(|name|cx.ident_of(&name[..]))
+ .map(|name| cx.ident_of(&name[..]))
.collect::<Vec<ast::Ident>>();
// 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[..]);
- cx.ident_of(&vi_suffix[..]) })
+ .map(|name| {
+ let vi_suffix = format!("{}_vi", &name[..]);
+ cx.ident_of(&vi_suffix[..]).gensym()
+ })
.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[..]);
+ let catch_all_substructure =
+ EnumNonMatchingCollapsed(self_arg_idents, &variants[..], &vi_idents[..]);
+
+ let first_fieldless = variants.iter().find(|v| v.node.data.fields().is_empty());
// These arms are of the form:
// (Variant1, Variant1, ...) => Body1
// (Variant2, Variant2, ...) => Body2
// ...
// where each tuple has length = self_args.len()
- let mut match_arms: Vec<ast::Arm> = variants.iter().enumerate()
+ let mut match_arms: Vec<ast::Arm> = variants.iter()
+ .enumerate()
+ .filter(|&(_, v)| !(self.unify_fieldless_variants && v.node.data.fields().is_empty()))
.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 (p, idents) = trait_.create_enum_variant_pattern(cx,
+ type_ident,
+ variant,
+ self_arg_name,
+ ast::Mutability::Immutable);
+ (cx.pat(sp, PatKind::Ref(p, ast::Mutability::Immutable)), idents)
};
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
name: opt_ident,
self_: self_getter_expr,
other: others,
- attrs: attrs,
+ attrs,
}
}).collect::<Vec<FieldInfo>>();
// expressions for referencing every field of every
// Self arg, assuming all are instances of VariantK.
// Build up code associated with such a case.
- let substructure = EnumMatching(index,
- &**variant,
- field_tuples);
- let arm_expr = self.call_substructure_method(
- cx, trait_, type_ident, &self_args[..], nonself_args,
- &substructure);
+ let substructure = EnumMatching(index, variants.len(), 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();
+ })
+ .collect();
+
+ let default = match first_fieldless {
+ Some(v) if self.unify_fieldless_variants => {
+ // We need a default case that handles the fieldless variants.
+ // The index and actual variant aren't meaningful in this case,
+ // so just use whatever
+ let substructure = EnumMatching(0, variants.len(), v, Vec::new());
+ Some(self.call_substructure_method(cx,
+ trait_,
+ type_ident,
+ &self_args[..],
+ nonself_args,
+ &substructure))
+ }
+ _ if variants.len() > 1 && self_args.len() > 1 => {
+ // Since we know that all the arguments will match if we reach
+ // the match expression we add the unreachable intrinsics as the
+ // result of the catch all which should help llvm in optimizing it
+ Some(deriving::call_intrinsic(cx, sp, "unreachable", vec![]))
+ }
+ _ => None,
+ };
+ if let Some(arm) = default {
+ match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], arm));
+ }
+
// We will usually need the catch-all after matching the
// tuples `(VariantK, VariantK, ...)` for each VariantK of the
// enum. But:
// let __self0_vi = unsafe {
// std::intrinsics::discriminant_value(&self) } as i32;
// let __self1_vi = unsafe {
- // std::intrinsics::discriminant_value(&__arg1) } as i32;
+ // std::intrinsics::discriminant_value(&arg1) } as i32;
// let __self2_vi = unsafe {
- // std::intrinsics::discriminant_value(&__arg2) } as i32;
+ // std::intrinsics::discriminant_value(&arg2) } as i32;
// ```
- let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new();
+ let mut index_let_stmts: Vec<ast::Stmt> = Vec::new();
- //We also build an expression which checks whether all discriminants are equal
+ // We also build an expression which checks whether all discriminants are equal
// discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ...
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 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 self_addr = cx.expr_addr_of(sp, self_arg.clone());
+ let variant_value =
+ deriving::call_intrinsic(cx, sp, "discriminant_value", vec![self_addr]);
let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name));
let variant_disr = cx.expr_cast(sp, variant_value, target_ty);
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)
+ let test = cx.expr_binary(sp, BinOpKind::Eq, first_expr, id);
+ discriminant_test =
+ cx.expr_binary(sp, BinOpKind::And, 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);
-
- //Since we know that all the arguments will match if we reach the match expression we
- //add the unreachable intrinsics as the result of the catch all which should help llvm
- //in optimizing it
- 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 arm_expr = self.call_substructure_method(cx,
+ trait_,
+ type_ident,
+ &self_args[..],
+ nonself_args,
+ &catch_all_substructure);
// Final wrinkle: the self_args are expressions that deref
- // down to desired l-values, but we cannot actually deref
+ // down to desired places, but we cannot actually deref
// 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 match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
+ let match_arg = cx.expr(sp, ast::ExprKind::Tup(borrowed_self_args));
- //Lastly we create an expression which branches on all discriminants being equal
+ // Lastly we create an expression which branches on all discriminants being equal
// if discriminant_test {
// match (...) {
// (Variant1, Variant1, ...) => Body1
// }
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)))
+ index_let_stmts.push(cx.stmt_expr(arm_expr));
+ cx.expr_block(cx.block(sp, index_let_stmts))
} else if variants.is_empty() {
// As an additional wrinkle, For a zero-variant enum A,
// currently the compiler
// derive Debug on such a type could here generate code
// that needs the feature gate enabled.)
- cx.expr_unreachable(sp)
- }
- else {
+ deriving::call_intrinsic(cx, sp, "unreachable", vec![])
+ } else {
// Final wrinkle: the self_args are expressions that deref
- // down to desired l-values, but we cannot actually deref
+ // down to desired places, but we cannot actually deref
// 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 match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
+ let match_arg = cx.expr(sp, ast::ExprKind::Tup(borrowed_self_args));
cx.expr_match(sp, match_arg, match_arms)
}
}
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 = trait_.summarise_struct(cx, &v.node.data);
- (ident, v.span, summary)
- }).collect();
- self.call_substructure_method(cx, trait_, type_ident,
- self_args, nonself_args,
+ -> P<Expr> {
+ let summary = enum_def.variants
+ .iter()
+ .map(|v| {
+ let sp = v.span.with_ctxt(trait_.span.ctxt());
+ let summary = trait_.summarise_struct(cx, &v.node.data);
+ (v.node.ident, sp, summary)
+ })
+ .collect();
+ self.call_substructure_method(cx,
+ trait_,
+ type_ident,
+ self_args,
+ nonself_args,
&StaticEnum(enum_def, summary))
}
}
-#[derive(PartialEq)] // dogfooding!
-enum StructType {
- Unknown, Record, Tuple
-}
-
// general helper methods.
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: &VariantData) -> StaticFields {
+ fn summarise_struct(&self, cx: &mut ExtCtxt, struct_def: &VariantData) -> StaticFields {
let mut named_idents = Vec::new();
let mut just_spans = Vec::new();
- for field in struct_def.fields(){
- 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),
+ for field in struct_def.fields() {
+ let sp = field.span.with_ctxt(self.span.ctxt());
+ match field.ident {
+ Some(ident) => named_idents.push((ident, sp)),
+ _ => 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, false) => {
+ cx.span_bug(self.span,
+ "a struct with named and unnamed \
+ fields in generic `derive`")
+ }
// named fields
(_, false) => Named(named_idents),
- // tuple structs (includes empty structs)
- (_, _) => Unnamed(just_spans)
+ // empty structs
+ _ if struct_def.is_struct() => Named(named_idents),
+ _ => Unnamed(just_spans, struct_def.is_tuple()),
}
}
fn create_subpatterns(&self,
cx: &mut ExtCtxt,
- field_paths: Vec<ast::SpannedIdent> ,
- mutbl: ast::Mutability)
+ field_paths: Vec<ast::Ident>,
+ mutbl: ast::Mutability,
+ use_temporaries: bool)
-> Vec<P<ast::Pat>> {
- field_paths.iter().map(|path| {
- cx.pat(path.span,
- ast::PatIdent(ast::BindingMode::ByRef(mutbl), (*path).clone(), None))
- }).collect()
+ field_paths.iter()
+ .map(|path| {
+ let binding_mode = if use_temporaries {
+ ast::BindingMode::ByValue(ast::Mutability::Immutable)
+ } else {
+ ast::BindingMode::ByRef(mutbl)
+ };
+ cx.pat(path.span,
+ PatKind::Ident(binding_mode, (*path).clone(), None))
+ })
+ .collect()
}
- fn create_struct_pattern(&self,
- cx: &mut ExtCtxt,
- struct_path: ast::Path,
- struct_def: &'a VariantData,
- 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![]);
- }
-
+ fn create_struct_pattern
+ (&self,
+ cx: &mut ExtCtxt,
+ struct_path: ast::Path,
+ struct_def: &'a VariantData,
+ prefix: &str,
+ mutbl: ast::Mutability,
+ use_temporaries: bool)
+ -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>)
+ {
let mut paths = Vec::new();
- let mut ident_expr = Vec::new();
- let mut struct_type = Unknown;
-
+ let mut ident_exprs = Vec::new();
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 sp = struct_field.span.with_ctxt(self.span.ctxt());
+ let ident = cx.ident_of(&format!("{}_{}", prefix, i)).gensym();
+ paths.push(ident.with_span_pos(sp));
+ let val = cx.expr_path(cx.path_ident(sp, ident));
+ let val = if use_temporaries {
+ val
+ } else {
+ cx.expr_deref(sp, val)
};
- 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 val = cx.expr(sp, ast::ExprKind::Paren(val));
+
+ ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
}
- let subpats = self.create_subpatterns(cx, paths, mutbl);
-
- // struct_type is definitely not Unknown, since struct_def.fields
- // must be nonempty to reach here
- let pattern = if struct_type == Record {
- let field_pats = subpats.into_iter().zip(&ident_expr)
- .map(|(pat, &(_, id, _, _))| {
- // id is guaranteed to be Some
- 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)
+ let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries);
+ let pattern = match *struct_def {
+ VariantData::Struct(..) => {
+ let field_pats = subpats.into_iter()
+ .zip(&ident_exprs)
+ .map(|(pat, &(sp, ident, ..))| {
+ if ident.is_none() {
+ cx.span_bug(sp, "a braced struct with unnamed fields in `derive`");
+ }
+ codemap::Spanned {
+ span: pat.span.with_ctxt(self.span.ctxt()),
+ node: ast::FieldPat {
+ ident: ident.unwrap(),
+ pat,
+ is_shorthand: false,
+ attrs: ast::ThinVec::new(),
+ },
+ }
+ })
+ .collect();
+ cx.pat_struct(self.span, struct_path, field_pats)
+ }
+ VariantData::Tuple(..) => {
+ cx.pat_tuple_struct(self.span, struct_path, subpats)
+ }
+ VariantData::Unit(..) => {
+ cx.pat_path(self.span, struct_path)
+ }
};
- (pattern, ident_expr)
+ (pattern, ident_exprs)
}
- 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]);
- self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl)
+ 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 sp = variant.span.with_ctxt(self.span.ctxt());
+ let variant_path = cx.path(sp, vec![enum_ident, variant.node.ident]);
+ let use_temporaries = false; // enums can't be repr(packed)
+ self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl,
+ use_temporaries)
+ }
+}
+
+// helpful premade recipes
+
+pub fn cs_fold_fields<'a, F>(use_foldl: bool,
+ mut f: F,
+ base: P<Expr>,
+ cx: &mut ExtCtxt,
+ all_fields: &[FieldInfo<'a>])
+ -> P<Expr>
+ where F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>
+{
+ 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)
+ })
+ }
+}
+
+pub fn cs_fold_enumnonmatch(mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
+ cx: &mut ExtCtxt,
+ trait_span: Span,
+ substructure: &Substructure)
+ -> P<Expr>
+{
+ match *substructure.fields {
+ EnumNonMatchingCollapsed(ref all_args, _, tuple) => {
+ enum_nonmatch_f(cx,
+ trait_span,
+ (&all_args[..], tuple),
+ substructure.nonself_args)
+ }
+ _ => cx.span_bug(trait_span, "cs_fold_enumnonmatch expected an EnumNonMatchingCollapsed")
}
}
-/* helpful premade recipes */
+pub fn cs_fold_static(cx: &mut ExtCtxt,
+ trait_span: Span)
+ -> P<Expr>
+{
+ cx.span_bug(trait_span, "static function in `derive`")
+}
/// Fold the fields. `use_foldl` controls whether this is done
/// left-to-right (`true`) or right-to-left (`false`).
pub fn cs_fold<F>(use_foldl: bool,
- mut f: F,
+ f: F,
base: P<Expr>,
- mut enum_nonmatch_f: EnumNonMatchCollapsedFunc,
+ 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>,
+ -> 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),
+ EnumMatching(.., ref all_fields) |
+ Struct(_, ref all_fields) => {
+ cs_fold_fields(use_foldl, f, base, cx, all_fields)
+ }
+ EnumNonMatchingCollapsed(..) => {
+ cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure)
+ }
StaticEnum(..) | StaticStruct(..) => {
- cx.span_bug(trait_span, "static function in `derive`")
+ cs_fold_static(cx, trait_span)
}
}
}
+/// Function to fold over fields, with three cases, to generate more efficient and concise code.
+/// When the `substructure` has grouped fields, there are two cases:
+/// Zero fields: call the base case function with None (like the usual base case of `cs_fold`).
+/// One or more fields: call the base case function on the first value (which depends on
+/// `use_fold`), and use that as the base case. Then perform `cs_fold` on the remainder of the
+/// fields.
+/// When the `substructure` is a `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f`
+/// is returned. Statics may not be folded over.
+/// See `cs_op` in `partial_ord.rs` for a model example.
+pub fn cs_fold1<F, B>(use_foldl: bool,
+ f: F,
+ mut b: B,
+ 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>,
+ B: FnMut(&mut ExtCtxt, Option<(Span, P<Expr>, &[P<Expr>])>) -> P<Expr>
+{
+ match *substructure.fields {
+ EnumMatching(.., ref all_fields) |
+ Struct(_, ref all_fields) => {
+ let (base, all_fields) = match (all_fields.is_empty(), use_foldl) {
+ (false, true) => {
+ let field = &all_fields[0];
+ let args = (field.span, field.self_.clone(), &field.other[..]);
+ (b(cx, Some(args)), &all_fields[1..])
+ }
+ (false, false) => {
+ let idx = all_fields.len() - 1;
+ let field = &all_fields[idx];
+ let args = (field.span, field.self_.clone(), &field.other[..]);
+ (b(cx, Some(args)), &all_fields[..idx])
+ }
+ (true, _) => (b(cx, None), &all_fields[..])
+ };
+
+ cs_fold_fields(use_foldl, f, base, cx, all_fields)
+ }
+ EnumNonMatchingCollapsed(..) => {
+ cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure)
+ }
+ StaticEnum(..) | StaticStruct(..) => {
+ cs_fold_static(cx, trait_span)
+ }
+ }
+}
/// Call the method that is being derived on all the fields, and then
/// process the collected results. i.e.
///
-/// ```ignore
+/// ```ignore (only-for-syntax-highlight)
/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1),
/// self_2.method(__arg_1_2, __arg_2_2)])
/// ```
cx: &mut ExtCtxt,
trait_span: Span,
substructure: &Substructure)
- -> P<Expr> where
- F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>,
+ -> P<Expr>
+ where F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>
{
match *substructure.fields {
- EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
+ EnumMatching(.., ref all_fields) |
+ Struct(_, ref all_fields) => {
// call self_n.method(other_1_n, other_2_n, ...)
- 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();
+ 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`")
}
+ 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`"),
+ }
+}
+
+/// Return true if the type has no value fields
+/// (for an enum, no variant has any fields)
+pub fn is_type_without_fields(item: &Annotatable) -> bool {
+ if let Annotatable::Item(ref item) = *item {
+ match item.node {
+ ast::ItemKind::Enum(ref enum_def, _) => {
+ enum_def.variants.iter().all(|v| v.node.data.fields().is_empty())
+ }
+ ast::ItemKind::Struct(ref variant_data, _) => variant_data.fields().is_empty(),
+ _ => false,
+ }
+ } else {
+ false
}
}