about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs16
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs13
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/encodable.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs488
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/ty.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs38
-rw-r--r--src/test/ui/deriving/deriving-all-codegen.rs26
-rw-r--r--src/test/ui/deriving/deriving-all-codegen.stdout661
11 files changed, 717 insertions, 575 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 9cd72ed0c67..7755ff779c4 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -148,7 +148,7 @@ fn cs_clone_simple(
             ),
         }
     }
-    BlockOrExpr::new_mixed(stmts, cx.expr_deref(trait_span, cx.expr_self(trait_span)))
+    BlockOrExpr::new_mixed(stmts, Some(cx.expr_deref(trait_span, cx.expr_self(trait_span))))
 }
 
 fn cs_clone(
@@ -161,7 +161,7 @@ fn cs_clone(
     let all_fields;
     let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]);
     let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| {
-        let args = vec![cx.expr_addr_of(field.span, field.self_expr.clone())];
+        let args = vec![field.self_expr.clone()];
         cx.expr_call_global(field.span, fn_path.clone(), args)
     };
 
@@ -177,9 +177,7 @@ fn cs_clone(
             all_fields = af;
             vdata = &variant.data;
         }
-        EnumNonMatchingCollapsed(..) => {
-            cx.span_bug(trait_span, &format!("non-matching enum variants in `derive({})`", name,))
-        }
+        EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)),
         StaticEnum(..) | StaticStruct(..) => {
             cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
         }
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index 859e995356e..1612be86237 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -63,10 +63,7 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl
                 let [other_expr] = &field.other_selflike_exprs[..] else {
                         cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
                     };
-                let args = vec![
-                    cx.expr_addr_of(field.span, field.self_expr.clone()),
-                    cx.expr_addr_of(field.span, other_expr.clone()),
-                ];
+                let args = vec![field.self_expr.clone(), other_expr.clone()];
                 cx.expr_call_global(field.span, cmp_path.clone(), args)
             }
             CsFold::Combine(span, expr1, expr2) => {
@@ -76,16 +73,6 @@ pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> Bl
                 cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
             }
             CsFold::Fieldless => cx.expr_path(equal_path.clone()),
-            CsFold::EnumNonMatching(span, tag_tuple) => {
-                if tag_tuple.len() != 2 {
-                    cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`")
-                } else {
-                    let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
-                    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
-                    let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
-                    cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt])
-                }
-            }
         },
     );
     BlockOrExpr::new_expr(expr)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index 724c639984c..0141b337726 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -2,7 +2,8 @@ use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::{path_local, path_std};
 
-use rustc_ast::{BinOpKind, MetaItem};
+use rustc_ast::ptr::P;
+use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
@@ -32,11 +33,24 @@ pub fn expand_deriving_partial_eq(
                     let [other_expr] = &field.other_selflike_exprs[..] else {
                         cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
                     };
-                    cx.expr_binary(field.span, op, field.self_expr.clone(), other_expr.clone())
+
+                    // We received `&T` arguments. Convert them to `T` by
+                    // stripping `&` or adding `*`. This isn't necessary for
+                    // type checking, but it results in much better error
+                    // messages if something goes wrong.
+                    let convert = |expr: &P<Expr>| {
+                        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
+                            &expr.kind
+                        {
+                            inner.clone()
+                        } else {
+                            cx.expr_deref(field.span, expr.clone())
+                        }
+                    };
+                    cx.expr_binary(field.span, op, convert(&field.self_expr), convert(other_expr))
                 }
                 CsFold::Combine(span, expr1, expr2) => cx.expr_binary(span, combiner, expr1, expr2),
                 CsFold::Fieldless => cx.expr_bool(span, base),
-                CsFold::EnumNonMatching(span, _tag_tuple) => cx.expr_bool(span, !base),
             },
         );
         BlockOrExpr::new_expr(expr)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 3f9843922da..2ebb01cc8a0 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -71,10 +71,7 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
                 let [other_expr] = &field.other_selflike_exprs[..] else {
                         cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
                     };
-                let args = vec![
-                    cx.expr_addr_of(field.span, field.self_expr.clone()),
-                    cx.expr_addr_of(field.span, other_expr.clone()),
-                ];
+                let args = vec![field.self_expr.clone(), other_expr.clone()];
                 cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
             }
             CsFold::Combine(span, expr1, expr2) => {
@@ -85,17 +82,6 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_
                 cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
             }
             CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())),
-            CsFold::EnumNonMatching(span, tag_tuple) => {
-                if tag_tuple.len() != 2 {
-                    cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
-                } else {
-                    let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0]));
-                    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1]));
-                    let fn_partial_cmp_path =
-                        cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
-                    cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt])
-                }
-            }
         },
     );
     BlockOrExpr::new_expr(expr)
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index b99198054de..ceef893e862 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -45,7 +45,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
     let (ident, vdata, fields) = match substr.fields {
         Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
         EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
-        EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
+        EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
             cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
         }
     };
@@ -95,9 +95,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
                 );
                 args.push(name);
             }
-            // Use double indirection to make sure this works for unsized types
+            // Use an extra indirection to make sure this works for unsized types.
             let field = cx.expr_addr_of(field.span, field.self_expr.clone());
-            let field = cx.expr_addr_of(field.span, field);
             args.push(field);
         }
         let expr = cx.expr_call_global(span, fn_path_debug, args);
@@ -115,9 +114,9 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
                 ));
             }
 
-            // Use double indirection to make sure this works for unsized types
-            let value_ref = cx.expr_addr_of(field.span, field.self_expr.clone());
-            value_exprs.push(cx.expr_addr_of(field.span, value_ref));
+            // Use an extra indirection to make sure this works for unsized types.
+            let field = cx.expr_addr_of(field.span, field.self_expr.clone());
+            value_exprs.push(field);
         }
 
         // `let names: &'static _ = &["field1", "field2"];`
@@ -177,6 +176,6 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
             stmts.push(names_let.unwrap());
         }
         stmts.push(values_let);
-        BlockOrExpr::new_mixed(stmts, expr)
+        BlockOrExpr::new_mixed(stmts, Some(expr))
     }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
index 49dbe51f762..70167cac68a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
@@ -287,7 +287,7 @@ fn encodable_substructure(
                 fn_emit_enum_path,
                 vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk],
             );
-            BlockOrExpr::new_mixed(vec![me], expr)
+            BlockOrExpr::new_mixed(vec![me], Some(expr))
         }
 
         _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"),
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 74e18bffc2e..076b627ca79 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -21,21 +21,14 @@
 //!   `struct T(i32, char)`).
 //! - `EnumMatching`, when `Self` is an enum and all the arguments are the
 //!   same variant of the enum (e.g., `Some(1)`, `Some(3)` and `Some(4)`)
-//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments
-//!   are not the same variant (e.g., `None`, `Some(1)` and `None`).
+//! - `EnumTag` when `Self` is an enum, for comparing the enum tags.
 //! - `StaticEnum` and `StaticStruct` for static methods, where the type
 //!   being derived upon is either an enum or struct respectively. (Any
 //!   argument with type Self is just grouped among the non-self
 //!   arguments.)
 //!
 //! In the first two cases, the values from the corresponding fields in
-//! all the arguments are grouped together. For `EnumNonMatchingCollapsed`
-//! this isn't possible (different variants have different fields), so the
-//! fields are inaccessible. (Previous versions of the deriving infrastructure
-//! had a way to expand into code that could access them, at the cost of
-//! generating exponential amounts of code; see issue #15375). There are no
-//! fields with values in the static cases, so these are treated entirely
-//! differently.
+//! all the arguments are grouped together.
 //!
 //! The non-static cases have `Option<ident>` in several places associated
 //! with field `expr`s. This represents the name of the field it is
@@ -142,21 +135,15 @@
 //!                }])
 //! ```
 //!
-//! For `C0(a)` and `C1 {x}` ,
+//! For the tags,
 //!
 //! ```{.text}
-//! EnumNonMatchingCollapsed(
-//!     &[<ident for self index value>, <ident of __arg_1 index value>])
+//! EnumTag(
+//!     &[<ident of self tag>, <ident of other tag>], <expr to combine with>)
 //! ```
-//!
-//! It is the same for when the arguments are flipped to `C1 {x}` and
-//! `C0(a)`; the only difference is what the values of the identifiers
-//! <ident for self index value> and <ident of __arg_1 index value> will
-//! be in the generated code.
-//!
-//! `EnumNonMatchingCollapsed` deliberately provides far less information
-//! than is generally available for a given pair of variants; see #15375
-//! for discussion.
+//! Note that this setup doesn't allow for the brute-force "match every variant
+//! against every other variant" approach, which is bad because it produces a
+//! quadratic amount of code (see #15375).
 //!
 //! ## Static
 //!
@@ -180,10 +167,9 @@ use std::iter;
 use std::vec;
 
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, BinOpKind, EnumDef, Expr, Generics, PatKind};
+use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind};
 use rustc_ast::{GenericArg, GenericParamKind, VariantData};
 use rustc_attr as attr;
-use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
@@ -236,6 +222,8 @@ pub struct MethodDef<'a> {
     pub attributes: Vec<ast::Attribute>,
 
     /// Can we combine fieldless variants for enums into a single match arm?
+    /// If true, indicates that the trait operation uses the enum tag in some
+    /// way.
     pub unify_fieldless_variants: bool,
 
     pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
@@ -275,19 +263,22 @@ pub enum StaticFields {
 
 /// A summary of the possible sets of fields.
 pub enum SubstructureFields<'a> {
+    /// A non-static method with `Self` is a struct.
     Struct(&'a ast::VariantData, Vec<FieldInfo>),
+
     /// 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, usize, &'a ast::Variant, Vec<FieldInfo>),
 
-    /// Non-matching variants of the enum, but with all state hidden from the
-    /// consequent code. The field is a list of `Ident`s bound to the variant
-    /// index values for each of the actual input `Self` arguments.
-    EnumNonMatchingCollapsed(&'a [Ident]),
+    /// The tag of an enum. The first field is a `FieldInfo` for the tags, as
+    /// if they were fields. The second field is the expression to combine the
+    /// tag expression with; it will be `None` if no match is necessary.
+    EnumTag(FieldInfo, Option<P<Expr>>),
 
     /// A static method where `Self` is a struct.
     StaticStruct(&'a ast::VariantData, StaticFields),
+
     /// A static method where `Self` is an enum.
     StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>),
 }
@@ -325,8 +316,8 @@ impl BlockOrExpr {
         BlockOrExpr(vec![], Some(expr))
     }
 
-    pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: P<Expr>) -> BlockOrExpr {
-        BlockOrExpr(stmts, Some(expr))
+    pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: Option<P<Expr>>) -> BlockOrExpr {
+        BlockOrExpr(stmts, expr)
     }
 
     // Converts it into a block.
@@ -344,7 +335,14 @@ impl BlockOrExpr {
                 None => cx.expr_block(cx.block(span, vec![])),
                 Some(expr) => expr,
             }
+        } else if self.0.len() == 1
+            && let ast::StmtKind::Expr(expr) = &self.0[0].kind
+            && self.1.is_none()
+        {
+            // There's only a single statement expression. Pull it out.
+            expr.clone()
         } else {
+            // Multiple statements and/or expressions.
             cx.expr_block(self.into_block(cx, span))
         }
     }
@@ -455,7 +453,6 @@ impl<'a> TraitDef<'a> {
                 };
                 let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
                 let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id);
-                let use_temporaries = is_packed && always_copy;
 
                 let newitem = match item.kind {
                     ast::ItemKind::Struct(ref struct_def, ref generics) => self.expand_struct_def(
@@ -464,11 +461,11 @@ impl<'a> TraitDef<'a> {
                         item.ident,
                         generics,
                         from_scratch,
-                        use_temporaries,
                         is_packed,
+                        always_copy,
                     ),
                     ast::ItemKind::Enum(ref enum_def, ref generics) => {
-                        // We ignore `use_temporaries` here, because
+                        // We ignore `is_packed`/`always_copy` here, because
                         // `repr(packed)` enums cause an error later on.
                         //
                         // This can only cause further compilation errors
@@ -484,8 +481,8 @@ impl<'a> TraitDef<'a> {
                                 item.ident,
                                 generics,
                                 from_scratch,
-                                use_temporaries,
                                 is_packed,
+                                always_copy,
                             )
                         } else {
                             cx.span_err(mitem.span, "this trait cannot be derived for unions");
@@ -766,8 +763,8 @@ impl<'a> TraitDef<'a> {
         type_ident: Ident,
         generics: &Generics,
         from_scratch: bool,
-        use_temporaries: bool,
         is_packed: bool,
+        always_copy: bool,
     ) -> P<ast::Item> {
         let field_tys: Vec<P<ast::Ty>> =
             struct_def.fields().iter().map(|field| field.ty.clone()).collect();
@@ -795,8 +792,8 @@ impl<'a> TraitDef<'a> {
                         type_ident,
                         &selflike_args,
                         &nonselflike_args,
-                        use_temporaries,
                         is_packed,
+                        always_copy,
                     )
                 };
 
@@ -937,9 +934,7 @@ impl<'a> MethodDef<'a> {
 
             match ty {
                 // Selflike (`&Self`) arguments only occur in non-static methods.
-                Ref(box Self_, _) if !self.is_static() => {
-                    selflike_args.push(cx.expr_deref(span, arg_expr))
-                }
+                Ref(box Self_, _) if !self.is_static() => selflike_args.push(arg_expr),
                 Self_ => cx.span_bug(span, "`Self` in non-return position"),
                 _ => nonselflike_args.push(arg_expr),
             }
@@ -1008,7 +1003,7 @@ impl<'a> MethodDef<'a> {
     /// ```
     /// #[derive(PartialEq)]
     /// # struct Dummy;
-    /// struct A { x: i32, y: i32 }
+    /// struct A { x: u8, y: u8 }
     ///
     /// // equivalent to:
     /// impl PartialEq for A {
@@ -1018,11 +1013,27 @@ impl<'a> MethodDef<'a> {
     /// }
     /// ```
     /// But if the struct is `repr(packed)`, we can't use something like
-    /// `&self.x` on a packed type (as required for e.g. `Debug` and `Hash`)
-    /// because that might cause an unaligned ref. So we use let-destructuring
-    /// instead.
+    /// `&self.x` because that might cause an unaligned ref. So for any trait
+    /// method that takes a reference, if the struct impls `Copy` then we use a
+    /// local block to force a copy:
     /// ```
-    /// # struct A { x: i32, y: i32 }
+    /// # struct A { x: u8, y: u8 }
+    /// impl PartialEq for A {
+    ///     fn eq(&self, other: &A) -> bool {
+    ///         // Desugars to `{ self.x }.eq(&{ other.y }) && ...`
+    ///         { self.x } == { other.y } && { self.y } == { other.y }
+    ///     }
+    /// }
+    /// impl Hash for A {
+    ///     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
+    ///         ::core::hash::Hash::hash(&{ self.x }, state);
+    ///         ::core::hash::Hash::hash(&{ self.y }, state)
+    ///     }
+    /// }
+    /// ```
+    /// If the struct doesn't impl `Copy`, we use let-destructuring with `ref`:
+    /// ```
+    /// # struct A { x: u8, y: u8 }
     /// impl PartialEq for A {
     ///     fn eq(&self, other: &A) -> bool {
     ///         let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self;
@@ -1031,6 +1042,8 @@ impl<'a> MethodDef<'a> {
     ///     }
     /// }
     /// ```
+    /// This latter case only works if the fields match the alignment required
+    /// by the `packed(N)` attribute. (We'll get errors later on if not.)
     fn expand_struct_method_body<'b>(
         &self,
         cx: &mut ExtCtxt<'_>,
@@ -1039,8 +1052,8 @@ impl<'a> MethodDef<'a> {
         type_ident: Ident,
         selflike_args: &[P<Expr>],
         nonselflike_args: &[P<Expr>],
-        use_temporaries: bool,
         is_packed: bool,
+        always_copy: bool,
     ) -> BlockOrExpr {
         let span = trait_.span;
         assert!(selflike_args.len() == 1 || selflike_args.len() == 2);
@@ -1057,29 +1070,31 @@ impl<'a> MethodDef<'a> {
 
         if !is_packed {
             let selflike_fields =
-                trait_.create_struct_field_access_fields(cx, selflike_args, struct_def);
+                trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, false);
+            mk_body(cx, selflike_fields)
+        } else if always_copy {
+            let selflike_fields =
+                trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true);
             mk_body(cx, selflike_fields)
         } else {
+            // Neither packed nor copy. Need to use ref patterns.
             let prefixes: Vec<_> =
                 (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect();
+            let addr_of = always_copy;
             let selflike_fields =
-                trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, use_temporaries);
+                trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of);
             let mut body = mk_body(cx, selflike_fields);
 
             let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
-            let patterns = trait_.create_struct_patterns(
-                cx,
-                struct_path,
-                struct_def,
-                &prefixes,
-                ast::Mutability::Not,
-                use_temporaries,
-            );
+            let use_ref_pat = is_packed && !always_copy;
+            let patterns =
+                trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, use_ref_pat);
 
             // Do the let-destructuring.
             let mut stmts: Vec<_> = iter::zip(selflike_args, patterns)
                 .map(|(selflike_arg_expr, pat)| {
-                    cx.stmt_let_pat(span, pat, selflike_arg_expr.clone())
+                    let selflike_arg_expr = cx.expr_deref(span, selflike_arg_expr.clone());
+                    cx.stmt_let_pat(span, pat, selflike_arg_expr)
                 })
                 .collect();
             stmts.extend(std::mem::take(&mut body.0));
@@ -1119,82 +1134,128 @@ impl<'a> MethodDef<'a> {
     /// impl ::core::cmp::PartialEq for A {
     ///     #[inline]
     ///     fn eq(&self, other: &A) -> bool {
-    ///         {
-    ///             let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-    ///             let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-    ///             if true && __self_vi == __arg_1_vi {
-    ///                 match (&*self, &*other) {
-    ///                     (&A::A2(ref __self_0), &A::A2(ref __arg_1_0)) =>
-    ///                         (*__self_0) == (*__arg_1_0),
-    ///                     _ => true,
-    ///                 }
-    ///             } else {
-    ///                 false // catch-all handler
+    ///         let __self_tag = ::core::intrinsics::discriminant_value(self);
+    ///         let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+    ///         __self_tag == __arg1_tag &&
+    ///             match (self, other) {
+    ///                 (A::A2(__self_0), A::A2(__arg1_0)) =>
+    ///                     *__self_0 == *__arg1_0,
+    ///                 _ => true,
     ///             }
-    ///         }
     ///     }
     /// }
     /// ```
-    /// Creates a match for a tuple of all `selflike_args`, where either all
-    /// variants match, or it falls into a catch-all for when one variant
-    /// does not match.
-    ///
-    /// There are N + 1 cases because is a case for each of the N
-    /// variants where all of the variants match, and one catch-all for
-    /// when one does not match.
-    ///
-    /// As an optimization we generate code which checks whether all variants
-    /// match first which makes llvm see that C-like enums can be compiled into
-    /// a simple equality check (for PartialEq).
-    ///
-    /// The catch-all handler is provided access the variant index values
-    /// for each of the selflike_args, carried in precomputed variables.
+    /// Creates a tag check combined with a match for a tuple of all
+    /// `selflike_args`, with an arm for each variant with fields, possibly an
+    /// arm for each fieldless variant (if `!unify_fieldless_variants` is not
+    /// true), and possibly a default arm.
     fn expand_enum_method_body<'b>(
         &self,
         cx: &mut ExtCtxt<'_>,
         trait_: &TraitDef<'b>,
         enum_def: &'b EnumDef,
         type_ident: Ident,
-        mut selflike_args: Vec<P<Expr>>,
+        selflike_args: Vec<P<Expr>>,
         nonselflike_args: &[P<Expr>],
     ) -> BlockOrExpr {
         let span = trait_.span;
         let variants = &enum_def.variants;
 
+        // Traits that unify fieldless variants always use the tag(s).
+        let uses_tags = self.unify_fieldless_variants;
+
+        // There is no sensible code to be generated for *any* deriving on a
+        // zero-variant enum. So we just generate a failing expression.
+        if variants.is_empty() {
+            return BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)));
+        }
+
         let prefixes = iter::once("__self".to_string())
             .chain(
                 selflike_args
                     .iter()
                     .enumerate()
                     .skip(1)
-                    .map(|(arg_count, _selflike_arg)| format!("__arg_{}", arg_count)),
+                    .map(|(arg_count, _selflike_arg)| format!("__arg{}", arg_count)),
             )
             .collect::<Vec<String>>();
 
-        // The `vi_idents` will be bound, solely in the catch-all, to
-        // a series of let statements mapping each selflike_arg to an int
-        // value corresponding to its discriminant.
-        let vi_idents = prefixes
-            .iter()
-            .map(|name| {
-                let vi_suffix = format!("{}_vi", name);
-                Ident::from_str_and_span(&vi_suffix, span)
-            })
-            .collect::<Vec<Ident>>();
+        // Build a series of let statements mapping each selflike_arg
+        // to its discriminant value.
+        //
+        // e.g. for `PartialEq::eq` builds two statements:
+        // ```
+        // let __self_tag = ::core::intrinsics::discriminant_value(self);
+        // let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        // ```
+        let get_tag_pieces = |cx: &ExtCtxt<'_>| {
+            let tag_idents: Vec<_> = prefixes
+                .iter()
+                .map(|name| Ident::from_str_and_span(&format!("{}_tag", name), span))
+                .collect();
 
-        // 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(&vi_idents);
+            let mut tag_exprs: Vec<_> = tag_idents
+                .iter()
+                .map(|&ident| cx.expr_addr_of(span, cx.expr_ident(span, ident)))
+                .collect();
 
-        let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
+            let self_expr = tag_exprs.remove(0);
+            let other_selflike_exprs = tag_exprs;
+            let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
+
+            let tag_let_stmts: Vec<_> = iter::zip(&tag_idents, &selflike_args)
+                .map(|(&ident, selflike_arg)| {
+                    let variant_value = deriving::call_intrinsic(
+                        cx,
+                        span,
+                        sym::discriminant_value,
+                        vec![selflike_arg.clone()],
+                    );
+                    cx.stmt_let(span, false, ident, variant_value)
+                })
+                .collect();
+
+            (tag_field, tag_let_stmts)
+        };
+
+        // There are some special cases involving fieldless enums where no
+        // match is necessary.
+        let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty());
+        if all_fieldless {
+            if uses_tags && variants.len() > 1 {
+                // If the type is fieldless and the trait uses the tag and
+                // there are multiple variants, we need just an operation on
+                // the tag(s).
+                let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
+                let mut tag_check = self.call_substructure_method(
+                    cx,
+                    trait_,
+                    type_ident,
+                    nonselflike_args,
+                    &EnumTag(tag_field, None),
+                );
+                tag_let_stmts.append(&mut tag_check.0);
+                return BlockOrExpr(tag_let_stmts, tag_check.1);
+            }
+
+            if variants.len() == 1 {
+                // If there is a single variant, we don't need an operation on
+                // the tag(s). Just use the most degenerate result.
+                return self.call_substructure_method(
+                    cx,
+                    trait_,
+                    type_ident,
+                    nonselflike_args,
+                    &EnumMatching(0, 1, &variants[0], Vec::new()),
+                );
+            };
+        }
 
         // These arms are of the form:
         // (Variant1, Variant1, ...) => Body1
         // (Variant2, Variant2, ...) => Body2
         // ...
         // where each tuple has length = selflike_args.len()
-
         let mut match_arms: Vec<ast::Arm> = variants
             .iter()
             .enumerate()
@@ -1203,30 +1264,22 @@ impl<'a> MethodDef<'a> {
                 // A single arm has form (&VariantK, &VariantK, ...) => BodyK
                 // (see "Final wrinkle" note below for why.)
 
-                let use_temporaries = false; // enums can't be repr(packed)
-                let fields = trait_.create_struct_pattern_fields(
+                let addr_of = false; // because enums can't be repr(packed)
+                let fields =
+                    trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of);
+
+                let sp = variant.span.with_ctxt(trait_.span.ctxt());
+                let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
+                let use_ref_pat = false; // because enums can't be repr(packed)
+                let mut subpats: Vec<_> = trait_.create_struct_patterns(
                     cx,
+                    variant_path,
                     &variant.data,
                     &prefixes,
-                    use_temporaries,
+                    use_ref_pat,
                 );
 
-                let sp = variant.span.with_ctxt(trait_.span.ctxt());
-                let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
-                let mut subpats: Vec<_> = trait_
-                    .create_struct_patterns(
-                        cx,
-                        variant_path,
-                        &variant.data,
-                        &prefixes,
-                        ast::Mutability::Not,
-                        use_temporaries,
-                    )
-                    .into_iter()
-                    .map(|p| cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)))
-                    .collect();
-
-                // Here is the pat = `(&VariantK, &VariantK, ...)`
+                // `(VariantK, VariantK, ...)` or just `VariantK`.
                 let single_pat = if subpats.len() == 1 {
                     subpats.pop().unwrap()
                 } else {
@@ -1256,27 +1309,28 @@ impl<'a> MethodDef<'a> {
             })
             .collect();
 
+        // Add a default arm to the match, if necessary.
+        let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
         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());
+                // We need a default case that handles all the fieldless
+                // variants. The index and actual variant aren't meaningful in
+                // this case, so just use dummy values.
                 Some(
                     self.call_substructure_method(
                         cx,
                         trait_,
                         type_ident,
                         nonselflike_args,
-                        &substructure,
+                        &EnumMatching(0, variants.len(), v, Vec::new()),
                     )
                     .into_expr(cx, span),
                 )
             }
             _ if variants.len() > 1 && selflike_args.len() > 1 => {
-                // Since we know that all the arguments will match if we reach
+                // Because 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
+                // result of the default which should help llvm in optimizing it.
                 Some(deriving::call_unreachable(cx, span))
             }
             _ => None,
@@ -1285,111 +1339,41 @@ impl<'a> MethodDef<'a> {
             match_arms.push(cx.arm(span, cx.pat_wild(span), arm));
         }
 
-        // We will usually need the catch-all after matching the
-        // tuples `(VariantK, VariantK, ...)` for each VariantK of the
-        // enum.  But:
-        //
-        // * when there is only one Self arg, the arms above suffice
-        // (and the deriving we call back into may not be prepared to
-        // handle EnumNonMatchCollapsed), and,
-        //
-        // * when the enum has only one variant, the single arm that
-        // is already present always suffices.
-        //
-        // * In either of the two cases above, if we *did* add a
-        //   catch-all `_` match, it would trigger the
-        //   unreachable-pattern error.
-        //
-        if variants.len() > 1 && selflike_args.len() > 1 {
-            // Build a series of let statements mapping each selflike_arg
-            // to its discriminant value.
-            //
-            // i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
-            // with three Self args, builds three statements:
-            // ```
-            // let __self_vi = std::intrinsics::discriminant_value(&self);
-            // let __arg_1_vi = std::intrinsics::discriminant_value(&arg1);
-            // let __arg_2_vi = std::intrinsics::discriminant_value(&arg2);
-            // ```
-            let mut index_let_stmts: Vec<ast::Stmt> = Vec::with_capacity(vi_idents.len() + 1);
-
-            // We also build an expression which checks whether all discriminants are equal:
-            // `__self_vi == __arg_1_vi && __self_vi == __arg_2_vi && ...`
-            let mut discriminant_test = cx.expr_bool(span, true);
-            for (i, (&ident, selflike_arg)) in iter::zip(&vi_idents, &selflike_args).enumerate() {
-                let selflike_addr = cx.expr_addr_of(span, selflike_arg.clone());
-                let variant_value = deriving::call_intrinsic(
-                    cx,
-                    span,
-                    sym::discriminant_value,
-                    vec![selflike_addr],
-                );
-                let let_stmt = cx.stmt_let(span, false, ident, variant_value);
-                index_let_stmts.push(let_stmt);
-
-                if i > 0 {
-                    let id0 = cx.expr_ident(span, vi_idents[0]);
-                    let id = cx.expr_ident(span, ident);
-                    let test = cx.expr_binary(span, BinOpKind::Eq, id0, id);
-                    discriminant_test = if i == 1 {
-                        test
-                    } else {
-                        cx.expr_binary(span, BinOpKind::And, discriminant_test, test)
-                    };
-                }
-            }
-
-            let arm_expr = self
-                .call_substructure_method(
-                    cx,
-                    trait_,
-                    type_ident,
-                    nonselflike_args,
-                    &catch_all_substructure,
-                )
-                .into_expr(cx, span);
-
-            // Final wrinkle: the selflike_args are expressions that 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, ...)`.
-            selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg));
-            let match_arg = cx.expr(span, ast::ExprKind::Tup(selflike_args));
-
-            // Lastly we create an expression which branches on all discriminants being equal
-            //  if discriminant_test {
-            //      match (...) {
-            //          (Variant1, Variant1, ...) => Body1
-            //          (Variant2, Variant2, ...) => Body2,
-            //          ...
-            //          _ => ::core::intrinsics::unreachable()
-            //      }
-            //  }
-            //  else {
-            //      <delegated expression referring to __self_vi, et al.>
-            //  }
-            let all_match = cx.expr_match(span, match_arg, match_arms);
-            let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr));
-            BlockOrExpr(index_let_stmts, Some(arm_expr))
-        } else if variants.is_empty() {
-            // There is no sensible code to be generated for *any* deriving on
-            // a zero-variant enum. So we just generate a failing expression
-            // for the zero variant case.
-            BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span)))
-        } else {
-            // Final wrinkle: the selflike_args are expressions that 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, ...)`.
-            selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg));
+        // Create a match expression with one arm per discriminant plus
+        // possibly a default arm, e.g.:
+        //      match (self, other) {
+        //          (Variant1, Variant1, ...) => Body1
+        //          (Variant2, Variant2, ...) => Body2,
+        //          ...
+        //          _ => ::core::intrinsics::unreachable()
+        //      }
+        let get_match_expr = |mut selflike_args: Vec<P<Expr>>| {
             let match_arg = if selflike_args.len() == 1 {
                 selflike_args.pop().unwrap()
             } else {
                 cx.expr(span, ast::ExprKind::Tup(selflike_args))
             };
-            BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms)))
+            cx.expr_match(span, match_arg, match_arms)
+        };
+
+        // If the trait uses the tag and there are multiple variants, we need
+        // to add a tag check operation before the match. Otherwise, the match
+        // is enough.
+        if uses_tags && variants.len() > 1 {
+            let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
+
+            // Combine a tag check with the match.
+            let mut tag_check_plus_match = self.call_substructure_method(
+                cx,
+                trait_,
+                type_ident,
+                nonselflike_args,
+                &EnumTag(tag_field, Some(get_match_expr(selflike_args))),
+            );
+            tag_let_stmts.append(&mut tag_check_plus_match.0);
+            BlockOrExpr(tag_let_stmts, tag_check_plus_match.1)
+        } else {
+            BlockOrExpr(vec![], Some(get_match_expr(selflike_args)))
         }
     }
 
@@ -1453,8 +1437,7 @@ impl<'a> TraitDef<'a> {
         struct_path: ast::Path,
         struct_def: &'a VariantData,
         prefixes: &[String],
-        mutbl: ast::Mutability,
-        use_temporaries: bool,
+        use_ref_pat: bool,
     ) -> Vec<P<ast::Pat>> {
         prefixes
             .iter()
@@ -1462,10 +1445,10 @@ impl<'a> TraitDef<'a> {
                 let pieces_iter =
                     struct_def.fields().iter().enumerate().map(|(i, struct_field)| {
                         let sp = struct_field.span.with_ctxt(self.span.ctxt());
-                        let binding_mode = if use_temporaries {
-                            ast::BindingMode::ByValue(ast::Mutability::Not)
+                        let binding_mode = if use_ref_pat {
+                            ast::BindingMode::ByRef(ast::Mutability::Not)
                         } else {
-                            ast::BindingMode::ByRef(mutbl)
+                            ast::BindingMode::ByValue(ast::Mutability::Not)
                         };
                         let ident = self.mk_pattern_ident(prefix, i);
                         let path = ident.with_span_pos(sp);
@@ -1544,7 +1527,7 @@ impl<'a> TraitDef<'a> {
         cx: &mut ExtCtxt<'_>,
         struct_def: &'a VariantData,
         prefixes: &[String],
-        use_temporaries: bool,
+        addr_of: bool,
     ) -> Vec<FieldInfo> {
         self.create_fields(struct_def, |i, _struct_field, sp| {
             prefixes
@@ -1552,7 +1535,7 @@ impl<'a> TraitDef<'a> {
                 .map(|prefix| {
                     let ident = self.mk_pattern_ident(prefix, i);
                     let expr = cx.expr_path(cx.path_ident(sp, ident));
-                    if use_temporaries { expr } else { cx.expr_deref(sp, expr) }
+                    if addr_of { cx.expr_addr_of(sp, expr) } else { expr }
                 })
                 .collect()
         })
@@ -1563,20 +1546,17 @@ impl<'a> TraitDef<'a> {
         cx: &mut ExtCtxt<'_>,
         selflike_args: &[P<Expr>],
         struct_def: &'a VariantData,
+        copy: bool,
     ) -> Vec<FieldInfo> {
         self.create_fields(struct_def, |i, struct_field, sp| {
             selflike_args
                 .iter()
-                .map(|mut selflike_arg| {
-                    // We don't the need the deref, if there is one.
-                    if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &selflike_arg.kind {
-                        selflike_arg = inner;
-                    }
-                    // Note: we must use `struct_field.span` rather than `span` in the
+                .map(|selflike_arg| {
+                    // Note: we must use `struct_field.span` rather than `sp` in the
                     // `unwrap_or_else` case otherwise the hygiene is wrong and we get
                     // "field `0` of struct `Point` is private" errors on tuple
                     // structs.
-                    cx.expr(
+                    let mut field_expr = cx.expr(
                         sp,
                         ast::ExprKind::Field(
                             selflike_arg.clone(),
@@ -1584,7 +1564,13 @@ impl<'a> TraitDef<'a> {
                                 Ident::from_str_and_span(&i.to_string(), struct_field.span)
                             }),
                         ),
-                    )
+                    );
+                    if copy {
+                        field_expr = cx.expr_block(
+                            cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]),
+                        );
+                    }
+                    cx.expr_addr_of(sp, field_expr)
                 })
                 .collect()
         })
@@ -1605,11 +1591,6 @@ pub enum CsFold<'a> {
 
     // The fallback case for a struct or enum variant with no fields.
     Fieldless,
-
-    /// The fallback case for non-matching enum variants. The slice is the
-    /// identifiers holding the variant index value for each of the `Self`
-    /// arguments.
-    EnumNonMatching(Span, &'a [Ident]),
 }
 
 /// Folds over fields, combining the expressions for each field in a sequence.
@@ -1624,8 +1605,8 @@ pub fn cs_fold<F>(
 where
     F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>,
 {
-    match *substructure.fields {
-        EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => {
+    match substructure.fields {
+        EnumMatching(.., all_fields) | Struct(_, all_fields) => {
             if all_fields.is_empty() {
                 return f(cx, CsFold::Fieldless);
             }
@@ -1649,7 +1630,18 @@ where
                 rest.iter().rfold(base_expr, op)
             }
         }
-        EnumNonMatchingCollapsed(tuple) => f(cx, CsFold::EnumNonMatching(trait_span, tuple)),
+        EnumTag(tag_field, match_expr) => {
+            let tag_check_expr = f(cx, CsFold::Single(tag_field));
+            if let Some(match_expr) = match_expr {
+                if use_foldl {
+                    f(cx, CsFold::Combine(trait_span, tag_check_expr, match_expr.clone()))
+                } else {
+                    f(cx, CsFold::Combine(trait_span, match_expr.clone(), tag_check_expr))
+                }
+            } else {
+                tag_check_expr
+            }
+        }
         StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
     }
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
index 4b20d87629d..4d46f7cd48a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
@@ -196,9 +196,8 @@ impl Bounds {
 }
 
 pub fn get_explicit_self(cx: &ExtCtxt<'_>, span: Span) -> (P<Expr>, ast::ExplicitSelf) {
-    // this constructs a fresh `self` path
+    // This constructs a fresh `self` path.
     let self_path = cx.expr_self(span);
     let self_ty = respan(span, SelfKind::Region(None, ast::Mutability::Not));
-    let self_expr = cx.expr_deref(span, self_path);
-    (self_expr, self_ty)
+    (self_path, self_ty)
 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 2213cd6d37d..32ae3d34478 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -1,6 +1,6 @@
 use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
-use crate::deriving::{self, path_std, pathvec_std};
+use crate::deriving::{path_std, pathvec_std};
 
 use rustc_ast::{MetaItem, Mutability};
 use rustc_expand::base::{Annotatable, ExtCtxt};
@@ -52,39 +52,29 @@ fn hash_substructure(
     let [state_expr] = substr.nonselflike_args else {
         cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`");
     };
-    let call_hash = |span, thing_expr| {
+    let call_hash = |span, expr| {
         let hash_path = {
             let strs = cx.std_path(&[sym::hash, sym::Hash, sym::hash]);
 
             cx.expr_path(cx.path_global(span, strs))
         };
-        let ref_thing = cx.expr_addr_of(span, thing_expr);
-        let expr = cx.expr_call(span, hash_path, vec![ref_thing, state_expr.clone()]);
+        let expr = cx.expr_call(span, hash_path, vec![expr, state_expr.clone()]);
         cx.stmt_expr(expr)
     };
-    let mut stmts = Vec::new();
 
-    let fields = match substr.fields {
-        Struct(_, fs) | EnumMatching(_, 1, .., fs) => fs,
-        EnumMatching(.., fs) => {
-            let variant_value = deriving::call_intrinsic(
-                cx,
-                trait_span,
-                sym::discriminant_value,
-                vec![cx.expr_self(trait_span)],
-            );
-
-            stmts.push(call_hash(trait_span, variant_value));
-
-            fs
+    let (stmts, match_expr) = match substr.fields {
+        Struct(_, fields) | EnumMatching(.., fields) => {
+            let stmts =
+                fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect();
+            (stmts, None)
+        }
+        EnumTag(tag_field, match_expr) => {
+            assert!(tag_field.other_selflike_exprs.is_empty());
+            let stmts = vec![call_hash(tag_field.span, tag_field.self_expr.clone())];
+            (stmts, match_expr.clone())
         }
         _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`"),
     };
 
-    stmts.extend(
-        fields
-            .iter()
-            .map(|FieldInfo { ref self_expr, span, .. }| call_hash(*span, self_expr.clone())),
-    );
-    BlockOrExpr::new_stmts(stmts)
+    BlockOrExpr::new_mixed(stmts, match_expr)
 }
diff --git a/src/test/ui/deriving/deriving-all-codegen.rs b/src/test/ui/deriving/deriving-all-codegen.rs
index 1a651b2074c..aef79ae8a5b 100644
--- a/src/test/ui/deriving/deriving-all-codegen.rs
+++ b/src/test/ui/deriving/deriving-all-codegen.rs
@@ -31,13 +31,26 @@ struct Point {
 // A large struct.
 #[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
 struct Big {
-    b1: u32, b2: u32, b3: u32, b4: u32, b5: u32, b6: u32, b7: u32, b8:u32,
+    b1: u32, b2: u32, b3: u32, b4: u32, b5: u32, b6: u32, b7: u32, b8: u32,
 }
 
-// A packed tuple struct.
+// A struct with an unsized field. Some derives are not usable in this case.
+#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+struct Unsized([u32]);
+
+// A packed tuple struct that impls `Copy`.
 #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
 #[repr(packed)]
-struct Packed(u32);
+struct PackedCopy(u32);
+
+// A packed tuple struct that does not impl `Copy`. Note that the alignment of
+// the field must be 1 for this code to be valid. Otherwise it triggers an
+// error "`#[derive]` can't be used on a `#[repr(packed)]` struct that does not
+// derive Copy (error E0133)" at MIR building time. This is a weird case and
+// it's possible that this struct is not supposed to work, but for now it does.
+#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(packed)]
+struct PackedNonCopy(u8);
 
 // An empty enum.
 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
@@ -49,6 +62,13 @@ enum Enum1 {
     Single { x: u32 }
 }
 
+// A C-like, fieldless enum with a single variant.
+#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
+enum Fieldless1 {
+    #[default]
+    A,
+}
+
 // A C-like, fieldless enum.
 #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
 enum Fieldless {
diff --git a/src/test/ui/deriving/deriving-all-codegen.stdout b/src/test/ui/deriving/deriving-all-codegen.stdout
index 9b10192d75a..542911537be 100644
--- a/src/test/ui/deriving/deriving-all-codegen.stdout
+++ b/src/test/ui/deriving/deriving-all-codegen.stdout
@@ -367,91 +367,225 @@ impl ::core::cmp::Ord for Big {
     }
 }
 
-// A packed tuple struct.
+// A struct with an unsized field. Some derives are not usable in this case.
+struct Unsized([u32]);
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::fmt::Debug for Unsized {
+    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Unsized",
+            &&self.0)
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::hash::Hash for Unsized {
+    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
+        ::core::hash::Hash::hash(&self.0, state)
+    }
+}
+impl ::core::marker::StructuralPartialEq for Unsized {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::PartialEq for Unsized {
+    #[inline]
+    fn eq(&self, other: &Unsized) -> bool { self.0 == other.0 }
+    #[inline]
+    fn ne(&self, other: &Unsized) -> bool { self.0 != other.0 }
+}
+impl ::core::marker::StructuralEq for Unsized {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Eq for Unsized {
+    #[inline]
+    #[doc(hidden)]
+    #[no_coverage]
+    fn assert_receiver_is_total_eq(&self) -> () {
+        let _: ::core::cmp::AssertParamIsEq<[u32]>;
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::PartialOrd for Unsized {
+    #[inline]
+    fn partial_cmp(&self, other: &Unsized)
+        -> ::core::option::Option<::core::cmp::Ordering> {
+        ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Ord for Unsized {
+    #[inline]
+    fn cmp(&self, other: &Unsized) -> ::core::cmp::Ordering {
+        ::core::cmp::Ord::cmp(&self.0, &other.0)
+    }
+}
+
+// A packed tuple struct that impls `Copy`.
 #[repr(packed)]
-struct Packed(u32);
+struct PackedCopy(u32);
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::clone::Clone for Packed {
+impl ::core::clone::Clone for PackedCopy {
     #[inline]
-    fn clone(&self) -> Packed {
+    fn clone(&self) -> PackedCopy {
         let _: ::core::clone::AssertParamIsClone<u32>;
         *self
     }
 }
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::marker::Copy for Packed { }
+impl ::core::marker::Copy for PackedCopy { }
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::fmt::Debug for Packed {
+impl ::core::fmt::Debug for PackedCopy {
     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        let Self(__self_0_0) = *self;
-        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Packed",
-            &&__self_0_0)
+        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PackedCopy",
+            &&{ self.0 })
     }
 }
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::default::Default for Packed {
+impl ::core::default::Default for PackedCopy {
     #[inline]
-    fn default() -> Packed { Packed(::core::default::Default::default()) }
+    fn default() -> PackedCopy {
+        PackedCopy(::core::default::Default::default())
+    }
 }
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::hash::Hash for Packed {
+impl ::core::hash::Hash for PackedCopy {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        let Self(__self_0_0) = *self;
-        ::core::hash::Hash::hash(&__self_0_0, state)
+        ::core::hash::Hash::hash(&{ self.0 }, state)
+    }
+}
+impl ::core::marker::StructuralPartialEq for PackedCopy {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::PartialEq for PackedCopy {
+    #[inline]
+    fn eq(&self, other: &PackedCopy) -> bool { { self.0 } == { other.0 } }
+    #[inline]
+    fn ne(&self, other: &PackedCopy) -> bool { { self.0 } != { other.0 } }
+}
+impl ::core::marker::StructuralEq for PackedCopy {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Eq for PackedCopy {
+    #[inline]
+    #[doc(hidden)]
+    #[no_coverage]
+    fn assert_receiver_is_total_eq(&self) -> () {
+        let _: ::core::cmp::AssertParamIsEq<u32>;
     }
 }
-impl ::core::marker::StructuralPartialEq for Packed {}
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::cmp::PartialEq for Packed {
+impl ::core::cmp::PartialOrd for PackedCopy {
     #[inline]
-    fn eq(&self, other: &Packed) -> bool {
-        let Self(__self_0_0) = *self;
-        let Self(__self_1_0) = *other;
-        __self_0_0 == __self_1_0
+    fn partial_cmp(&self, other: &PackedCopy)
+        -> ::core::option::Option<::core::cmp::Ordering> {
+        ::core::cmp::PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
     }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Ord for PackedCopy {
     #[inline]
-    fn ne(&self, other: &Packed) -> bool {
-        let Self(__self_0_0) = *self;
-        let Self(__self_1_0) = *other;
-        __self_0_0 != __self_1_0
+    fn cmp(&self, other: &PackedCopy) -> ::core::cmp::Ordering {
+        ::core::cmp::Ord::cmp(&{ self.0 }, &{ other.0 })
     }
 }
-impl ::core::marker::StructuralEq for Packed {}
+
+// A packed tuple struct that does not impl `Copy`. Note that the alignment of
+// the field must be 1 for this code to be valid. Otherwise it triggers an
+// error "`#[derive]` can't be used on a `#[repr(packed)]` struct that does not
+// derive Copy (error E0133)" at MIR building time. This is a weird case and
+// it's possible that this struct is not supposed to work, but for now it does.
+#[repr(packed)]
+struct PackedNonCopy(u8);
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::cmp::Eq for Packed {
+impl ::core::clone::Clone for PackedNonCopy {
+    #[inline]
+    fn clone(&self) -> PackedNonCopy {
+        let Self(ref __self_0_0) = *self;
+        PackedNonCopy(::core::clone::Clone::clone(__self_0_0))
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::fmt::Debug for PackedNonCopy {
+    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+        let Self(ref __self_0_0) = *self;
+        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PackedNonCopy",
+            &__self_0_0)
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::default::Default for PackedNonCopy {
+    #[inline]
+    fn default() -> PackedNonCopy {
+        PackedNonCopy(::core::default::Default::default())
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::hash::Hash for PackedNonCopy {
+    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
+        let Self(ref __self_0_0) = *self;
+        ::core::hash::Hash::hash(__self_0_0, state)
+    }
+}
+impl ::core::marker::StructuralPartialEq for PackedNonCopy {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::PartialEq for PackedNonCopy {
+    #[inline]
+    fn eq(&self, other: &PackedNonCopy) -> bool {
+        let Self(ref __self_0_0) = *self;
+        let Self(ref __self_1_0) = *other;
+        *__self_0_0 == *__self_1_0
+    }
+    #[inline]
+    fn ne(&self, other: &PackedNonCopy) -> bool {
+        let Self(ref __self_0_0) = *self;
+        let Self(ref __self_1_0) = *other;
+        *__self_0_0 != *__self_1_0
+    }
+}
+impl ::core::marker::StructuralEq for PackedNonCopy {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Eq for PackedNonCopy {
     #[inline]
     #[doc(hidden)]
     #[no_coverage]
     fn assert_receiver_is_total_eq(&self) -> () {
-        let _: ::core::cmp::AssertParamIsEq<u32>;
+        let _: ::core::cmp::AssertParamIsEq<u8>;
     }
 }
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::cmp::PartialOrd for Packed {
+impl ::core::cmp::PartialOrd for PackedNonCopy {
     #[inline]
-    fn partial_cmp(&self, other: &Packed)
+    fn partial_cmp(&self, other: &PackedNonCopy)
         -> ::core::option::Option<::core::cmp::Ordering> {
-        let Self(__self_0_0) = *self;
-        let Self(__self_1_0) = *other;
-        ::core::cmp::PartialOrd::partial_cmp(&__self_0_0, &__self_1_0)
+        let Self(ref __self_0_0) = *self;
+        let Self(ref __self_1_0) = *other;
+        ::core::cmp::PartialOrd::partial_cmp(__self_0_0, __self_1_0)
     }
 }
 #[automatically_derived]
 #[allow(unused_qualifications)]
-impl ::core::cmp::Ord for Packed {
+impl ::core::cmp::Ord for PackedNonCopy {
     #[inline]
-    fn cmp(&self, other: &Packed) -> ::core::cmp::Ordering {
-        let Self(__self_0_0) = *self;
-        let Self(__self_1_0) = *other;
-        ::core::cmp::Ord::cmp(&__self_0_0, &__self_1_0)
+    fn cmp(&self, other: &PackedNonCopy) -> ::core::cmp::Ordering {
+        let Self(ref __self_0_0) = *self;
+        let Self(ref __self_1_0) = *other;
+        ::core::cmp::Ord::cmp(__self_0_0, __self_1_0)
     }
 }
 
@@ -527,9 +661,9 @@ enum Enum1 {
 impl ::core::clone::Clone for Enum1 {
     #[inline]
     fn clone(&self) -> Enum1 {
-        match &*self {
-            &Enum1::Single { x: ref __self_0 } =>
-                Enum1::Single { x: ::core::clone::Clone::clone(&*__self_0) },
+        match self {
+            Enum1::Single { x: __self_0 } =>
+                Enum1::Single { x: ::core::clone::Clone::clone(__self_0) },
         }
     }
 }
@@ -537,10 +671,10 @@ impl ::core::clone::Clone for Enum1 {
 #[allow(unused_qualifications)]
 impl ::core::fmt::Debug for Enum1 {
     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        match &*self {
-            &Enum1::Single { x: ref __self_0 } =>
+        match self {
+            Enum1::Single { x: __self_0 } =>
                 ::core::fmt::Formatter::debug_struct_field1_finish(f,
-                    "Single", "x", &&*__self_0),
+                    "Single", "x", &__self_0),
         }
     }
 }
@@ -548,10 +682,9 @@ impl ::core::fmt::Debug for Enum1 {
 #[allow(unused_qualifications)]
 impl ::core::hash::Hash for Enum1 {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        match &*self {
-            &Enum1::Single { x: ref __self_0 } => {
-                ::core::hash::Hash::hash(&*__self_0, state)
-            }
+        match self {
+            Enum1::Single { x: __self_0 } =>
+                ::core::hash::Hash::hash(__self_0, state),
         }
     }
 }
@@ -561,16 +694,16 @@ impl ::core::marker::StructuralPartialEq for Enum1 {}
 impl ::core::cmp::PartialEq for Enum1 {
     #[inline]
     fn eq(&self, other: &Enum1) -> bool {
-        match (&*self, &*other) {
-            (&Enum1::Single { x: ref __self_0 }, &Enum1::Single {
-                x: ref __arg_1_0 }) => *__self_0 == *__arg_1_0,
+        match (self, other) {
+            (Enum1::Single { x: __self_0 }, Enum1::Single { x: __arg1_0 }) =>
+                *__self_0 == *__arg1_0,
         }
     }
     #[inline]
     fn ne(&self, other: &Enum1) -> bool {
-        match (&*self, &*other) {
-            (&Enum1::Single { x: ref __self_0 }, &Enum1::Single {
-                x: ref __arg_1_0 }) => *__self_0 != *__arg_1_0,
+        match (self, other) {
+            (Enum1::Single { x: __self_0 }, Enum1::Single { x: __arg1_0 }) =>
+                *__self_0 != *__arg1_0,
         }
     }
 }
@@ -591,10 +724,9 @@ impl ::core::cmp::PartialOrd for Enum1 {
     #[inline]
     fn partial_cmp(&self, other: &Enum1)
         -> ::core::option::Option<::core::cmp::Ordering> {
-        match (&*self, &*other) {
-            (&Enum1::Single { x: ref __self_0 }, &Enum1::Single {
-                x: ref __arg_1_0 }) =>
-                ::core::cmp::PartialOrd::partial_cmp(&*__self_0, &*__arg_1_0),
+        match (self, other) {
+            (Enum1::Single { x: __self_0 }, Enum1::Single { x: __arg1_0 }) =>
+                ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
         }
     }
 }
@@ -603,14 +735,77 @@ impl ::core::cmp::PartialOrd for Enum1 {
 impl ::core::cmp::Ord for Enum1 {
     #[inline]
     fn cmp(&self, other: &Enum1) -> ::core::cmp::Ordering {
-        match (&*self, &*other) {
-            (&Enum1::Single { x: ref __self_0 }, &Enum1::Single {
-                x: ref __arg_1_0 }) =>
-                ::core::cmp::Ord::cmp(&*__self_0, &*__arg_1_0),
+        match (self, other) {
+            (Enum1::Single { x: __self_0 }, Enum1::Single { x: __arg1_0 }) =>
+                ::core::cmp::Ord::cmp(__self_0, __arg1_0),
         }
     }
 }
 
+// A C-like, fieldless enum with a single variant.
+enum Fieldless1 {
+
+    #[default]
+    A,
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::clone::Clone for Fieldless1 {
+    #[inline]
+    fn clone(&self) -> Fieldless1 { Fieldless1::A }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::fmt::Debug for Fieldless1 {
+    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+        ::core::fmt::Formatter::write_str(f, "A")
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::default::Default for Fieldless1 {
+    #[inline]
+    fn default() -> Fieldless1 { Self::A }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::hash::Hash for Fieldless1 {
+    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
+}
+impl ::core::marker::StructuralPartialEq for Fieldless1 {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::PartialEq for Fieldless1 {
+    #[inline]
+    fn eq(&self, other: &Fieldless1) -> bool { true }
+}
+impl ::core::marker::StructuralEq for Fieldless1 {}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Eq for Fieldless1 {
+    #[inline]
+    #[doc(hidden)]
+    #[no_coverage]
+    fn assert_receiver_is_total_eq(&self) -> () {}
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::PartialOrd for Fieldless1 {
+    #[inline]
+    fn partial_cmp(&self, other: &Fieldless1)
+        -> ::core::option::Option<::core::cmp::Ordering> {
+        ::core::option::Option::Some(::core::cmp::Ordering::Equal)
+    }
+}
+#[automatically_derived]
+#[allow(unused_qualifications)]
+impl ::core::cmp::Ord for Fieldless1 {
+    #[inline]
+    fn cmp(&self, other: &Fieldless1) -> ::core::cmp::Ordering {
+        ::core::cmp::Ordering::Equal
+    }
+}
+
 // A C-like, fieldless enum.
 enum Fieldless {
 
@@ -632,10 +827,10 @@ impl ::core::marker::Copy for Fieldless { }
 #[allow(unused_qualifications)]
 impl ::core::fmt::Debug for Fieldless {
     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        match &*self {
-            &Fieldless::A => ::core::fmt::Formatter::write_str(f, "A"),
-            &Fieldless::B => ::core::fmt::Formatter::write_str(f, "B"),
-            &Fieldless::C => ::core::fmt::Formatter::write_str(f, "C"),
+        match self {
+            Fieldless::A => ::core::fmt::Formatter::write_str(f, "A"),
+            Fieldless::B => ::core::fmt::Formatter::write_str(f, "B"),
+            Fieldless::C => ::core::fmt::Formatter::write_str(f, "C"),
         }
     }
 }
@@ -649,12 +844,8 @@ impl ::core::default::Default for Fieldless {
 #[allow(unused_qualifications)]
 impl ::core::hash::Hash for Fieldless {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        match &*self {
-            _ => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state)
-            }
-        }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        ::core::hash::Hash::hash(&__self_tag, state)
     }
 }
 impl ::core::marker::StructuralPartialEq for Fieldless {}
@@ -663,11 +854,9 @@ impl ::core::marker::StructuralPartialEq for Fieldless {}
 impl ::core::cmp::PartialEq for Fieldless {
     #[inline]
     fn eq(&self, other: &Fieldless) -> bool {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) { _ => true, }
-            } else { false }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        __self_tag == __arg1_tag
     }
 }
 impl ::core::marker::StructuralEq for Fieldless {}
@@ -685,16 +874,9 @@ impl ::core::cmp::PartialOrd for Fieldless {
     #[inline]
     fn partial_cmp(&self, other: &Fieldless)
         -> ::core::option::Option<::core::cmp::Ordering> {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    _ =>
-                        ::core::option::Option::Some(::core::cmp::Ordering::Equal),
-                }
-            } else {
-               ::core::cmp::PartialOrd::partial_cmp(&__self_vi, &__arg_1_vi)
-           }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag)
     }
 }
 #[automatically_derived]
@@ -702,11 +884,9 @@ impl ::core::cmp::PartialOrd for Fieldless {
 impl ::core::cmp::Ord for Fieldless {
     #[inline]
     fn cmp(&self, other: &Fieldless) -> ::core::cmp::Ordering {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) { _ => ::core::cmp::Ordering::Equal, }
-            } else { ::core::cmp::Ord::cmp(&__self_vi, &__arg_1_vi) }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        ::core::cmp::Ord::cmp(&__self_tag, &__arg1_tag)
     }
 }
 
@@ -738,15 +918,15 @@ impl ::core::marker::Copy for Mixed { }
 #[allow(unused_qualifications)]
 impl ::core::fmt::Debug for Mixed {
     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        match &*self {
-            &Mixed::P => ::core::fmt::Formatter::write_str(f, "P"),
-            &Mixed::Q => ::core::fmt::Formatter::write_str(f, "Q"),
-            &Mixed::R(ref __self_0) =>
+        match self {
+            Mixed::P => ::core::fmt::Formatter::write_str(f, "P"),
+            Mixed::Q => ::core::fmt::Formatter::write_str(f, "Q"),
+            Mixed::R(__self_0) =>
                 ::core::fmt::Formatter::debug_tuple_field1_finish(f, "R",
-                    &&*__self_0),
-            &Mixed::S { d1: ref __self_0, d2: ref __self_1 } =>
+                    &__self_0),
+            Mixed::S { d1: __self_0, d2: __self_1 } =>
                 ::core::fmt::Formatter::debug_struct_field2_finish(f, "S",
-                    "d1", &&*__self_0, "d2", &&*__self_1),
+                    "d1", &__self_0, "d2", &__self_1),
         }
     }
 }
@@ -760,22 +940,15 @@ impl ::core::default::Default for Mixed {
 #[allow(unused_qualifications)]
 impl ::core::hash::Hash for Mixed {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        match &*self {
-            &Mixed::R(ref __self_0) => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state);
-                ::core::hash::Hash::hash(&*__self_0, state)
-            }
-            &Mixed::S { d1: ref __self_0, d2: ref __self_1 } => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state);
-                ::core::hash::Hash::hash(&*__self_0, state);
-                ::core::hash::Hash::hash(&*__self_1, state)
-            }
-            _ => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state)
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        ::core::hash::Hash::hash(&__self_tag, state);
+        match self {
+            Mixed::R(__self_0) => ::core::hash::Hash::hash(__self_0, state),
+            Mixed::S { d1: __self_0, d2: __self_1 } => {
+                ::core::hash::Hash::hash(__self_0, state);
+                ::core::hash::Hash::hash(__self_1, state)
             }
+            _ => {}
         }
     }
 }
@@ -785,33 +958,31 @@ impl ::core::marker::StructuralPartialEq for Mixed {}
 impl ::core::cmp::PartialEq for Mixed {
     #[inline]
     fn eq(&self, other: &Mixed) -> bool {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Mixed::R(ref __self_0), &Mixed::R(ref __arg_1_0)) =>
-                        *__self_0 == *__arg_1_0,
-                    (&Mixed::S { d1: ref __self_0, d2: ref __self_1 },
-                        &Mixed::S { d1: ref __arg_1_0, d2: ref __arg_1_1 }) =>
-                        *__self_0 == *__arg_1_0 && *__self_1 == *__arg_1_1,
-                    _ => true,
-                }
-            } else { false }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        __self_tag == __arg1_tag &&
+            match (self, other) {
+                (Mixed::R(__self_0), Mixed::R(__arg1_0)) =>
+                    *__self_0 == *__arg1_0,
+                (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S {
+                    d1: __arg1_0, d2: __arg1_1 }) =>
+                    *__self_0 == *__arg1_0 && *__self_1 == *__arg1_1,
+                _ => true,
+            }
     }
     #[inline]
     fn ne(&self, other: &Mixed) -> bool {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Mixed::R(ref __self_0), &Mixed::R(ref __arg_1_0)) =>
-                        *__self_0 != *__arg_1_0,
-                    (&Mixed::S { d1: ref __self_0, d2: ref __self_1 },
-                        &Mixed::S { d1: ref __arg_1_0, d2: ref __arg_1_1 }) =>
-                        *__self_0 != *__arg_1_0 || *__self_1 != *__arg_1_1,
-                    _ => false,
-                }
-            } else { true }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        __self_tag != __arg1_tag ||
+            match (self, other) {
+                (Mixed::R(__self_0), Mixed::R(__arg1_0)) =>
+                    *__self_0 != *__arg1_0,
+                (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S {
+                    d1: __arg1_0, d2: __arg1_1 }) =>
+                    *__self_0 != *__arg1_0 || *__self_1 != *__arg1_1,
+                _ => false,
+            }
     }
 }
 impl ::core::marker::StructuralEq for Mixed {}
@@ -831,29 +1002,26 @@ impl ::core::cmp::PartialOrd for Mixed {
     #[inline]
     fn partial_cmp(&self, other: &Mixed)
         -> ::core::option::Option<::core::cmp::Ordering> {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Mixed::R(ref __self_0), &Mixed::R(ref __arg_1_0)) =>
-                        ::core::cmp::PartialOrd::partial_cmp(&*__self_0,
-                            &*__arg_1_0),
-                    (&Mixed::S { d1: ref __self_0, d2: ref __self_1 },
-                        &Mixed::S { d1: ref __arg_1_0, d2: ref __arg_1_1 }) =>
-                        match ::core::cmp::PartialOrd::partial_cmp(&*__self_0,
-                                &*__arg_1_0) {
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        match ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag) {
+            ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
+                match (self, other) {
+                    (Mixed::R(__self_0), Mixed::R(__arg1_0)) =>
+                        ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+                    (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S {
+                        d1: __arg1_0, d2: __arg1_1 }) =>
+                        match ::core::cmp::PartialOrd::partial_cmp(__self_0,
+                                __arg1_0) {
                             ::core::option::Option::Some(::core::cmp::Ordering::Equal)
-                                =>
-                                ::core::cmp::PartialOrd::partial_cmp(&*__self_1,
-                                    &*__arg_1_1),
+                                => ::core::cmp::PartialOrd::partial_cmp(__self_1, __arg1_1),
                             cmp => cmp,
                         },
                     _ =>
                         ::core::option::Option::Some(::core::cmp::Ordering::Equal),
-                }
-            } else {
-               ::core::cmp::PartialOrd::partial_cmp(&__self_vi, &__arg_1_vi)
-           }
+                },
+            cmp => cmp,
+        }
     }
 }
 #[automatically_derived]
@@ -861,22 +1029,24 @@ impl ::core::cmp::PartialOrd for Mixed {
 impl ::core::cmp::Ord for Mixed {
     #[inline]
     fn cmp(&self, other: &Mixed) -> ::core::cmp::Ordering {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Mixed::R(ref __self_0), &Mixed::R(ref __arg_1_0)) =>
-                        ::core::cmp::Ord::cmp(&*__self_0, &*__arg_1_0),
-                    (&Mixed::S { d1: ref __self_0, d2: ref __self_1 },
-                        &Mixed::S { d1: ref __arg_1_0, d2: ref __arg_1_1 }) =>
-                        match ::core::cmp::Ord::cmp(&*__self_0, &*__arg_1_0) {
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        match ::core::cmp::Ord::cmp(&__self_tag, &__arg1_tag) {
+            ::core::cmp::Ordering::Equal =>
+                match (self, other) {
+                    (Mixed::R(__self_0), Mixed::R(__arg1_0)) =>
+                        ::core::cmp::Ord::cmp(__self_0, __arg1_0),
+                    (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S {
+                        d1: __arg1_0, d2: __arg1_1 }) =>
+                        match ::core::cmp::Ord::cmp(__self_0, __arg1_0) {
                             ::core::cmp::Ordering::Equal =>
-                                ::core::cmp::Ord::cmp(&*__self_1, &*__arg_1_1),
+                                ::core::cmp::Ord::cmp(__self_1, __arg1_1),
                             cmp => cmp,
                         },
                     _ => ::core::cmp::Ordering::Equal,
-                }
-            } else { ::core::cmp::Ord::cmp(&__self_vi, &__arg_1_vi) }
+                },
+            cmp => cmp,
+        }
     }
 }
 
@@ -888,13 +1058,13 @@ enum Fielded { X(u32), Y(bool), Z(Option<i32>), }
 impl ::core::clone::Clone for Fielded {
     #[inline]
     fn clone(&self) -> Fielded {
-        match &*self {
-            &Fielded::X(ref __self_0) =>
-                Fielded::X(::core::clone::Clone::clone(&*__self_0)),
-            &Fielded::Y(ref __self_0) =>
-                Fielded::Y(::core::clone::Clone::clone(&*__self_0)),
-            &Fielded::Z(ref __self_0) =>
-                Fielded::Z(::core::clone::Clone::clone(&*__self_0)),
+        match self {
+            Fielded::X(__self_0) =>
+                Fielded::X(::core::clone::Clone::clone(__self_0)),
+            Fielded::Y(__self_0) =>
+                Fielded::Y(::core::clone::Clone::clone(__self_0)),
+            Fielded::Z(__self_0) =>
+                Fielded::Z(::core::clone::Clone::clone(__self_0)),
         }
     }
 }
@@ -902,16 +1072,16 @@ impl ::core::clone::Clone for Fielded {
 #[allow(unused_qualifications)]
 impl ::core::fmt::Debug for Fielded {
     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-        match &*self {
-            &Fielded::X(ref __self_0) =>
+        match self {
+            Fielded::X(__self_0) =>
                 ::core::fmt::Formatter::debug_tuple_field1_finish(f, "X",
-                    &&*__self_0),
-            &Fielded::Y(ref __self_0) =>
+                    &__self_0),
+            Fielded::Y(__self_0) =>
                 ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Y",
-                    &&*__self_0),
-            &Fielded::Z(ref __self_0) =>
+                    &__self_0),
+            Fielded::Z(__self_0) =>
                 ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Z",
-                    &&*__self_0),
+                    &__self_0),
         }
     }
 }
@@ -919,22 +1089,12 @@ impl ::core::fmt::Debug for Fielded {
 #[allow(unused_qualifications)]
 impl ::core::hash::Hash for Fielded {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
-        match &*self {
-            &Fielded::X(ref __self_0) => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state);
-                ::core::hash::Hash::hash(&*__self_0, state)
-            }
-            &Fielded::Y(ref __self_0) => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state);
-                ::core::hash::Hash::hash(&*__self_0, state)
-            }
-            &Fielded::Z(ref __self_0) => {
-                ::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
-                    state);
-                ::core::hash::Hash::hash(&*__self_0, state)
-            }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        ::core::hash::Hash::hash(&__self_tag, state);
+        match self {
+            Fielded::X(__self_0) => ::core::hash::Hash::hash(__self_0, state),
+            Fielded::Y(__self_0) => ::core::hash::Hash::hash(__self_0, state),
+            Fielded::Z(__self_0) => ::core::hash::Hash::hash(__self_0, state),
         }
     }
 }
@@ -944,35 +1104,33 @@ impl ::core::marker::StructuralPartialEq for Fielded {}
 impl ::core::cmp::PartialEq for Fielded {
     #[inline]
     fn eq(&self, other: &Fielded) -> bool {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Fielded::X(ref __self_0), &Fielded::X(ref __arg_1_0)) =>
-                        *__self_0 == *__arg_1_0,
-                    (&Fielded::Y(ref __self_0), &Fielded::Y(ref __arg_1_0)) =>
-                        *__self_0 == *__arg_1_0,
-                    (&Fielded::Z(ref __self_0), &Fielded::Z(ref __arg_1_0)) =>
-                        *__self_0 == *__arg_1_0,
-                    _ => unsafe { ::core::intrinsics::unreachable() }
-                }
-            } else { false }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        __self_tag == __arg1_tag &&
+            match (self, other) {
+                (Fielded::X(__self_0), Fielded::X(__arg1_0)) =>
+                    *__self_0 == *__arg1_0,
+                (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) =>
+                    *__self_0 == *__arg1_0,
+                (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) =>
+                    *__self_0 == *__arg1_0,
+                _ => unsafe { ::core::intrinsics::unreachable() }
+            }
     }
     #[inline]
     fn ne(&self, other: &Fielded) -> bool {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Fielded::X(ref __self_0), &Fielded::X(ref __arg_1_0)) =>
-                        *__self_0 != *__arg_1_0,
-                    (&Fielded::Y(ref __self_0), &Fielded::Y(ref __arg_1_0)) =>
-                        *__self_0 != *__arg_1_0,
-                    (&Fielded::Z(ref __self_0), &Fielded::Z(ref __arg_1_0)) =>
-                        *__self_0 != *__arg_1_0,
-                    _ => unsafe { ::core::intrinsics::unreachable() }
-                }
-            } else { true }
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        __self_tag != __arg1_tag ||
+            match (self, other) {
+                (Fielded::X(__self_0), Fielded::X(__arg1_0)) =>
+                    *__self_0 != *__arg1_0,
+                (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) =>
+                    *__self_0 != *__arg1_0,
+                (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) =>
+                    *__self_0 != *__arg1_0,
+                _ => unsafe { ::core::intrinsics::unreachable() }
+            }
     }
 }
 impl ::core::marker::StructuralEq for Fielded {}
@@ -994,24 +1152,21 @@ impl ::core::cmp::PartialOrd for Fielded {
     #[inline]
     fn partial_cmp(&self, other: &Fielded)
         -> ::core::option::Option<::core::cmp::Ordering> {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Fielded::X(ref __self_0), &Fielded::X(ref __arg_1_0)) =>
-                        ::core::cmp::PartialOrd::partial_cmp(&*__self_0,
-                            &*__arg_1_0),
-                    (&Fielded::Y(ref __self_0), &Fielded::Y(ref __arg_1_0)) =>
-                        ::core::cmp::PartialOrd::partial_cmp(&*__self_0,
-                            &*__arg_1_0),
-                    (&Fielded::Z(ref __self_0), &Fielded::Z(ref __arg_1_0)) =>
-                        ::core::cmp::PartialOrd::partial_cmp(&*__self_0,
-                            &*__arg_1_0),
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        match ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag) {
+            ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
+                match (self, other) {
+                    (Fielded::X(__self_0), Fielded::X(__arg1_0)) =>
+                        ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+                    (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) =>
+                        ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+                    (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) =>
+                        ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
                     _ => unsafe { ::core::intrinsics::unreachable() }
-                }
-            } else {
-               ::core::cmp::PartialOrd::partial_cmp(&__self_vi, &__arg_1_vi)
-           }
+                },
+            cmp => cmp,
+        }
     }
 }
 #[automatically_derived]
@@ -1019,19 +1174,21 @@ impl ::core::cmp::PartialOrd for Fielded {
 impl ::core::cmp::Ord for Fielded {
     #[inline]
     fn cmp(&self, other: &Fielded) -> ::core::cmp::Ordering {
-        let __self_vi = ::core::intrinsics::discriminant_value(&*self);
-        let __arg_1_vi = ::core::intrinsics::discriminant_value(&*other);
-        if __self_vi == __arg_1_vi {
-                match (&*self, &*other) {
-                    (&Fielded::X(ref __self_0), &Fielded::X(ref __arg_1_0)) =>
-                        ::core::cmp::Ord::cmp(&*__self_0, &*__arg_1_0),
-                    (&Fielded::Y(ref __self_0), &Fielded::Y(ref __arg_1_0)) =>
-                        ::core::cmp::Ord::cmp(&*__self_0, &*__arg_1_0),
-                    (&Fielded::Z(ref __self_0), &Fielded::Z(ref __arg_1_0)) =>
-                        ::core::cmp::Ord::cmp(&*__self_0, &*__arg_1_0),
+        let __self_tag = ::core::intrinsics::discriminant_value(self);
+        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
+        match ::core::cmp::Ord::cmp(&__self_tag, &__arg1_tag) {
+            ::core::cmp::Ordering::Equal =>
+                match (self, other) {
+                    (Fielded::X(__self_0), Fielded::X(__arg1_0)) =>
+                        ::core::cmp::Ord::cmp(__self_0, __arg1_0),
+                    (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) =>
+                        ::core::cmp::Ord::cmp(__self_0, __arg1_0),
+                    (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) =>
+                        ::core::cmp::Ord::cmp(__self_0, __arg1_0),
                     _ => unsafe { ::core::intrinsics::unreachable() }
-                }
-            } else { ::core::cmp::Ord::cmp(&__self_vi, &__arg_1_vi) }
+                },
+            cmp => cmp,
+        }
     }
 }