about summary refs log tree commit diff
path: root/src/libsyntax_ext
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-10-27 09:35:12 +0000
committerbors <bors@rust-lang.org>2019-10-27 09:35:12 +0000
commitb7176b44a203322c834302f3b515f8c10a54f2c1 (patch)
tree5f80b511b61d444c5e1fb772cc92f51ef3b1cea1 /src/libsyntax_ext
parentcf148a717a275741a35b5f51eab182aa42bd06a6 (diff)
parentf645e90992d4f76a2c5e7f8b4656246769a285ff (diff)
downloadrust-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.rs6
-rw-r--r--src/libsyntax_ext/deriving/cmp/partial_eq.rs7
-rw-r--r--src/libsyntax_ext/deriving/mod.rs84
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));
+}