diff options
| author | bors <bors@rust-lang.org> | 2019-10-27 09:35:12 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-10-27 09:35:12 +0000 |
| commit | b7176b44a203322c834302f3b515f8c10a54f2c1 (patch) | |
| tree | 5f80b511b61d444c5e1fb772cc92f51ef3b1cea1 /src/libsyntax_ext | |
| parent | cf148a717a275741a35b5f51eab182aa42bd06a6 (diff) | |
| parent | f645e90992d4f76a2c5e7f8b4656246769a285ff (diff) | |
| download | rust-b7176b44a203322c834302f3b515f8c10a54f2c1.tar.gz rust-b7176b44a203322c834302f3b515f8c10a54f2c1.zip | |
Auto merge of #65519 - pnkfelix:issue-63438-trait-based-structural-match, r=matthewjasper
trait-based structural match implementation Moves from using a `#[structural_match]` attribute to using a marker trait (or pair of such traits, really) instead. Fix #63438. (This however does not remove the hacks that I believe were put into place to support the previous approach of injecting the attribute based on the presence of both derives... I have left that for follow-on work.)
Diffstat (limited to 'src/libsyntax_ext')
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/eq.rs | 6 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/cmp/partial_eq.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax_ext/deriving/mod.rs | 84 |
3 files changed, 95 insertions, 2 deletions
diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs index 92721dab878..162aaedafea 100644 --- a/src/libsyntax_ext/deriving/cmp/eq.rs +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -42,6 +42,12 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt<'_>, }], associated_types: Vec::new(), }; + + super::inject_impl_of_structural_trait( + cx, span, item, + path_std!(cx, marker::StructuralEq), + push); + trait_def.expand_ext(cx, mitem, item, push, true) } diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs index 1615d991792..c3e2b78bbe5 100644 --- a/src/libsyntax_ext/deriving/cmp/partial_eq.rs +++ b/src/libsyntax_ext/deriving/cmp/partial_eq.rs @@ -6,7 +6,7 @@ use syntax::ast::{BinOpKind, Expr, MetaItem}; use syntax_expand::base::{Annotatable, ExtCtxt, SpecialDerives}; use syntax::ptr::P; use syntax::symbol::sym; -use syntax_pos::Span; +use syntax_pos::{self, Span}; pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt<'_>, span: Span, @@ -81,6 +81,11 @@ pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt<'_>, } } } + super::inject_impl_of_structural_trait( + cx, span, item, + path_std!(cx, marker::StructuralPartialEq), + push); + // avoid defining `ne` if we can // c-like enums, enums without any fields and structs without fields // can safely define only `eq`. diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index f0471a857dc..a98cce1fd61 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -1,6 +1,6 @@ //! The compiler code necessary to implement the `#[derive]` extensions. -use syntax::ast::{self, MetaItem}; +use syntax::ast::{self, ItemKind, MetaItem}; use syntax_expand::base::{Annotatable, ExtCtxt, MultiItemModifier}; use syntax::ptr::P; use syntax::symbol::{Symbol, sym}; @@ -74,3 +74,85 @@ fn call_intrinsic(cx: &ExtCtxt<'_>, span, })) } + + +// Injects `impl<...> Structural for ItemType<...> { }`. In particular, +// does *not* add `where T: Structural` for parameters `T` in `...`. +// (That's the main reason we cannot use TraitDef here.) +fn inject_impl_of_structural_trait(cx: &mut ExtCtxt<'_>, + span: Span, + item: &Annotatable, + structural_path: generic::ty::Path<'_>, + push: &mut dyn FnMut(Annotatable)) { + let item = match *item { + Annotatable::Item(ref item) => item, + _ => { + // Non-Item derive is an error, but it should have been + // set earlier; see + // libsyntax/ext/expand.rs:MacroExpander::expand() + return; + } + }; + + let generics = match item.kind { + ItemKind::Struct(_, ref generics) | + ItemKind::Enum(_, ref generics) => generics, + // Do not inject `impl Structural for Union`. (`PartialEq` does not + // support unions, so we will see error downstream.) + ItemKind::Union(..) => return, + _ => unreachable!(), + }; + + // Create generics param list for where clauses and impl headers + let mut generics = generics.clone(); + + // Create the type of `self`. + // + // in addition, remove defaults from type params (impls cannot have them). + let self_params: Vec<_> = generics.params.iter_mut().map(|param| match &mut param.kind { + ast::GenericParamKind::Lifetime => { + ast::GenericArg::Lifetime(cx.lifetime(span, param.ident)) + } + ast::GenericParamKind::Type { default } => { + *default = None; + ast::GenericArg::Type(cx.ty_ident(span, param.ident)) + } + ast::GenericParamKind::Const { ty: _ } => { + ast::GenericArg::Const(cx.const_ident(span, param.ident)) + } + }).collect(); + + let type_ident = item.ident; + + let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics)); + let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params)); + + // It would be nice to also encode constraint `where Self: Eq` (by adding it + // onto `generics` cloned above). Unfortunately, that strategy runs afoul of + // rust-lang/rust#48214. So we perform that additional check in the compiler + // itself, instead of encoding it here. + + // Keep the lint and stability attributes of the original item, to control + // how the generated implementation is linted. + let mut attrs = Vec::new(); + attrs.extend(item.attrs + .iter() + .filter(|a| { + [sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable] + .contains(&a.name_or_empty()) + }) + .cloned()); + + let newitem = cx.item(span, + ast::Ident::invalid(), + attrs, + ItemKind::Impl(ast::Unsafety::Normal, + ast::ImplPolarity::Positive, + ast::Defaultness::Final, + generics, + Some(trait_ref), + self_type, + Vec::new())); + + push(Annotatable::Item(newitem)); +} |
