diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 9cd72ed0c67b..7755ff779c4d 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 859e995356e8..1612be862377 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 724c639984cc..0141b3377262 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| { + 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 3f9843922dad..2ebb01cc8a03 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 b99198054def..ceef893e862e 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 49dbe51f7626..70167cac68a7 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 74e18bffc2ec..aa4b3aec4a5f 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` 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( -//! &[, ]) +//! EnumTag( +//! &[, ], ) //! ``` -//! -//! 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 -//! and 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, /// 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>, @@ -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), + /// 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), - /// 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>), /// 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, expr: P) -> BlockOrExpr { - BlockOrExpr(stmts, Some(expr)) + pub fn new_mixed(stmts: Vec, expr: Option>) -> 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)) } } @@ -415,6 +413,12 @@ fn find_type_parameters( visitor.type_params } +#[derive(Clone, Copy)] +struct PackedStatus { + has_type_params: bool, + has_derive_copy: bool, +} + impl<'a> TraitDef<'a> { pub fn expand( self, @@ -444,18 +448,22 @@ impl<'a> TraitDef<'a> { } false }); - let has_no_type_params = match item.kind { - ast::ItemKind::Struct(_, ref generics) - | ast::ItemKind::Enum(_, ref generics) - | ast::ItemKind::Union(_, ref generics) => !generics - .params - .iter() - .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), - _ => unreachable!(), + let packed_status = if !is_packed { + None + } else { + let has_type_params = match item.kind { + ast::ItemKind::Struct(_, ref generics) + | ast::ItemKind::Enum(_, ref generics) + | ast::ItemKind::Union(_, ref generics) => generics + .params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), + _ => unreachable!(), + }; + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let has_derive_copy = cx.resolver.has_derive_copy(container_id); + Some(PackedStatus { has_type_params, has_derive_copy }) }; - 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 +472,10 @@ impl<'a> TraitDef<'a> { item.ident, generics, from_scratch, - use_temporaries, - is_packed, + packed_status, ), ast::ItemKind::Enum(ref enum_def, ref generics) => { - // We ignore `use_temporaries` here, because + // We ignore `packed_status` here, because // `repr(packed)` enums cause an error later on. // // This can only cause further compilation errors @@ -484,8 +491,7 @@ impl<'a> TraitDef<'a> { item.ident, generics, from_scratch, - use_temporaries, - is_packed, + packed_status, ) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -766,8 +772,7 @@ impl<'a> TraitDef<'a> { type_ident: Ident, generics: &Generics, from_scratch: bool, - use_temporaries: bool, - is_packed: bool, + packed_status: Option, ) -> P { let field_tys: Vec> = struct_def.fields().iter().map(|field| field.ty.clone()).collect(); @@ -786,6 +791,7 @@ impl<'a> TraitDef<'a> { struct_def, type_ident, &nonselflike_args, + packed_status, ) } else { method_def.expand_struct_method_body( @@ -795,8 +801,7 @@ impl<'a> TraitDef<'a> { type_ident, &selflike_args, &nonselflike_args, - use_temporaries, - is_packed, + packed_status, ) }; @@ -937,9 +942,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 +1011,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,16 +1021,21 @@ 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` on a packed type because that might cause an unaligned ref. + /// So for any trait method that takes a reference we use a local block to + /// force a copy, which works because such structs must be `Copy`: /// ``` - /// # struct A { x: i32, y: i32 } + /// # 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; - /// let Self { x: ref __self_1_0, y: ref __self_1_1 } = *other; - /// *__self_0_0 == *__self_1_0 && *__self_0_1 == *__self_1_1 + /// // 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) /// } /// } /// ``` @@ -1039,52 +1047,24 @@ impl<'a> MethodDef<'a> { type_ident: Ident, selflike_args: &[P], nonselflike_args: &[P], - use_temporaries: bool, - is_packed: bool, + packed_status: Option, ) -> BlockOrExpr { let span = trait_.span; assert!(selflike_args.len() == 1 || selflike_args.len() == 2); - let mk_body = |cx, selflike_fields| { - self.call_substructure_method( - cx, - trait_, - type_ident, - nonselflike_args, - &Struct(struct_def, selflike_fields), - ) - }; - - if !is_packed { - let selflike_fields = - trait_.create_struct_field_access_fields(cx, selflike_args, struct_def); - mk_body(cx, selflike_fields) - } else { - let prefixes: Vec<_> = - (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect(); - let selflike_fields = - trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, use_temporaries); - 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, - ); - - // 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()) - }) - .collect(); - stmts.extend(std::mem::take(&mut body.0)); - BlockOrExpr(stmts, body.1) + if let Err(res) = Self::check_packed_status(cx, span, packed_status) { + return res; } + + let selflike_fields = + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, packed_status); + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &Struct(struct_def, selflike_fields), + ) } fn expand_static_struct_method_body( @@ -1094,9 +1074,15 @@ impl<'a> MethodDef<'a> { struct_def: &VariantData, type_ident: Ident, nonselflike_args: &[P], + packed_status: Option, ) -> BlockOrExpr { - let summary = trait_.summarise_struct(cx, struct_def); + let span = trait_.span; + + if Self::check_packed_status(cx, span, packed_status).is_err() { + return BlockOrExpr(vec![], Some(deriving::call_abort(cx, span))); + } + let summary = trait_.summarise_struct(cx, struct_def); self.call_substructure_method( cx, trait_, @@ -1106,6 +1092,32 @@ impl<'a> MethodDef<'a> { ) } + fn check_packed_status( + cx: &ExtCtxt<'_>, + span: Span, + packed_status: Option, + ) -> Result<(), BlockOrExpr> { + let mut res = Ok(()); + let err = || Err(BlockOrExpr(vec![], Some(deriving::call_abort(cx, span)))); + if let Some(PackedStatus { has_type_params, has_derive_copy }) = packed_status { + // FIXME: when we make this a hard error, this should have its + // own error code. + if has_type_params { + let msg = "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ + type or const parameters (error E0133)"; + cx.sess.struct_span_err(span, msg).emit(); + res = err(); + } + if !has_derive_copy { + let msg = "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ + does not derive Copy (error E0133)"; + cx.sess.struct_span_err(span, msg).emit(); + res = err(); + } + } + res + } + /// ``` /// #[derive(PartialEq)] /// # struct Dummy; @@ -1119,82 +1131,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>, + selflike_args: Vec>, nonselflike_args: &[P], ) -> 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::>(); - // 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::>(); + // 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 = variants .iter() .enumerate() @@ -1203,30 +1261,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 +1306,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 +1336,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 { 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 = 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 { - // - // } - 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>| { 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 +1434,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> { prefixes .iter() @@ -1462,10 +1442,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 +1524,7 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt<'_>, struct_def: &'a VariantData, prefixes: &[String], - use_temporaries: bool, + addr_of: bool, ) -> Vec { self.create_fields(struct_def, |i, _struct_field, sp| { prefixes @@ -1552,7 +1532,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 +1543,17 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt<'_>, selflike_args: &[P], struct_def: &'a VariantData, + packed_status: Option, ) -> Vec { 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 +1561,13 @@ impl<'a> TraitDef<'a> { Ident::from_str_and_span(&i.to_string(), struct_field.span) }), ), - ) + ); + if packed_status.is_some() { + 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 +1588,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 +1602,8 @@ pub fn cs_fold( where F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P, { - 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 +1627,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 4b20d87629d9..4d46f7cd48a5 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, 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 2213cd6d37d2..32ae3d344789 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/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index c1ca089da221..4f30e468fb75 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -106,6 +106,13 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { })) } +/// Constructs an expression that calls the `abort` intrinsic. +fn call_abort(cx: &ExtCtxt<'_>, span: Span) -> P { + let span = cx.with_def_site_ctxt(span); + let path = cx.std_path(&[sym::intrinsics, sym::abort]); + cx.expr_call_global(span, path, vec![]) +} + // 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.) diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index 2eb38941f1a5..db49d7f90705 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -1,17 +1,11 @@ -use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::UNALIGNED_REFERENCES; use crate::util; use crate::MirLint; -pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { unsafe_derive_on_repr_packed, ..*providers }; -} - pub struct CheckPackedRef; impl<'tcx> MirLint<'tcx> for CheckPackedRef { @@ -30,23 +24,6 @@ struct PackedRefChecker<'a, 'tcx> { source_info: SourceInfo, } -fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - - tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| { - // FIXME: when we make this a hard error, this should have its - // own error code. - let message = if tcx.generics_of(def_id).own_requires_monomorphization() { - "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ - type or const parameters (error E0133)" - } else { - "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ - does not derive Copy (error E0133)" - }; - lint.build(message).emit(); - }); -} - impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { // Make sure we know where in the MIR we are. @@ -64,14 +41,13 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { if context.is_borrow() { if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { let def_id = self.body.source.instance.def_id(); - if let Some(impl_def_id) = self - .tcx - .impl_of_method(def_id) - .filter(|&def_id| self.tcx.is_builtin_derive(def_id)) + if let Some(impl_def_id) = self.tcx.impl_of_method(def_id) + && self.tcx.is_builtin_derive(impl_def_id) { - // If a method is defined in the local crate, - // the impl containing that method should also be. - self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local()); + // If we ever reach here it means that the generated derive + // code is somehow doing an unaligned reference, which it + // shouldn't do. + unreachable!(); } else { let source_info = self.source_info; let lint_root = self.body.source_scopes[source_info.scope] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 0887775aae5e..cf3fcda326d6 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -97,7 +97,6 @@ use rustc_mir_dataflow::rustc_peek; pub fn provide(providers: &mut Providers) { check_unsafety::provide(providers); - check_packed_ref::provide(providers); coverage::query::provide(providers); ffi_unwind_calls::provide(providers); shim::provide(providers); diff --git a/src/test/ui/derives/deriving-with-repr-packed.rs b/src/test/ui/derives/deriving-with-repr-packed.rs index b78eeaa90551..312eaea74d42 100644 --- a/src/test/ui/derives/deriving-with-repr-packed.rs +++ b/src/test/ui/derives/deriving-with-repr-packed.rs @@ -1,31 +1,41 @@ #![deny(unaligned_references)] -// check that derive on a packed struct with non-Copy fields +// Check that derive on a packed struct with non-Copy fields // correctly. This can't be made to work perfectly because // we can't just use the field from the struct as it might // not be aligned. +// One error for `Clone`, two (identical) errors for `PartialEq`, one error for `Eq`. #[derive(Copy, Clone, PartialEq, Eq)] -//~^ ERROR `#[derive]` can't be used -//~| hard error -//~^^^ ERROR `#[derive]` can't be used -//~| hard error +//~^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters +//~^^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters +//~^^^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters +//~^^^^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters #[repr(packed)] pub struct Foo(T, T, T); -#[derive(PartialEq, Eq)] -//~^ ERROR `#[derive]` can't be used -//~| hard error +// One error for `Debug`. +#[derive(Debug)] +//~^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy #[repr(packed)] pub struct Bar(u32, u32, u32); -#[derive(PartialEq)] +#[derive(Default)] struct Y(usize); -#[derive(PartialEq)] -//~^ ERROR `#[derive]` can't be used -//~| hard error +// Two different errors for `Default`. This used to be allowed because +// `default` is a static method, but no longer. +#[derive(Default)] +//~^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy +//~^^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters #[repr(packed)] -struct X(Y); +struct X(Y, T); + +// One error for `Hash`. This used to be allowed because the alignment of the +// fields is 1, but no longer. +#[derive(Hash)] +//~^ ERROR `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy +#[repr(packed)] +pub struct Baz(u8, bool); fn main() {} diff --git a/src/test/ui/derives/deriving-with-repr-packed.stderr b/src/test/ui/derives/deriving-with-repr-packed.stderr index 1002b359f60b..a313d61dc82e 100644 --- a/src/test/ui/derives/deriving-with-repr-packed.stderr +++ b/src/test/ui/derives/deriving-with-repr-packed.stderr @@ -1,111 +1,66 @@ error: `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters (error E0133) - --> $DIR/deriving-with-repr-packed.rs:8:16 + --> $DIR/deriving-with-repr-packed.rs:9:16 | LL | #[derive(Copy, Clone, PartialEq, Eq)] | ^^^^^ | -note: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters (error E0133) - --> $DIR/deriving-with-repr-packed.rs:8:23 + --> $DIR/deriving-with-repr-packed.rs:9:23 | LL | #[derive(Copy, Clone, PartialEq, Eq)] | ^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) - --> $DIR/deriving-with-repr-packed.rs:16:10 - | -LL | #[derive(PartialEq, Eq)] - | ^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) - --> $DIR/deriving-with-repr-packed.rs:25:10 +error: `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters (error E0133) + --> $DIR/deriving-with-repr-packed.rs:9:23 | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ +LL | #[derive(Copy, Clone, PartialEq, Eq)] + | ^^^^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors - -Future incompatibility report: Future breakage diagnostic: error: `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters (error E0133) - --> $DIR/deriving-with-repr-packed.rs:8:16 + --> $DIR/deriving-with-repr-packed.rs:9:34 | LL | #[derive(Copy, Clone, PartialEq, Eq)] - | ^^^^^ + | ^^ | -note: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) + --> $DIR/deriving-with-repr-packed.rs:18:10 | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +LL | #[derive(Debug)] + | ^^^^^ + | + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) -Future breakage diagnostic: error: `#[derive]` can't be used on a `#[repr(packed)]` struct with type or const parameters (error E0133) - --> $DIR/deriving-with-repr-packed.rs:8:23 - | -LL | #[derive(Copy, Clone, PartialEq, Eq)] - | ^^^^^^^^^ + --> $DIR/deriving-with-repr-packed.rs:28:10 | -note: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 +LL | #[derive(Default)] + | ^^^^^^^ | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) -Future breakage diagnostic: error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) - --> $DIR/deriving-with-repr-packed.rs:16:10 - | -LL | #[derive(PartialEq, Eq)] - | ^^^^^^^^^ + --> $DIR/deriving-with-repr-packed.rs:28:10 | -note: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 +LL | #[derive(Default)] + | ^^^^^^^ | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) -Future breakage diagnostic: error: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133) - --> $DIR/deriving-with-repr-packed.rs:25:10 + --> $DIR/deriving-with-repr-packed.rs:36:10 | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ +LL | #[derive(Hash)] + | ^^^^ | -note: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #82523 - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 8 previous errors diff --git a/src/test/ui/deriving/deriving-all-codegen.rs b/src/test/ui/deriving/deriving-all-codegen.rs index 1a651b2074c5..4fe81d164522 100644 --- a/src/test/ui/deriving/deriving-all-codegen.rs +++ b/src/test/ui/deriving/deriving-all-codegen.rs @@ -31,13 +31,17 @@ 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); // An empty enum. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -49,6 +53,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 9b10192d75a1..caf1b078416e 100644 --- a/src/test/ui/deriving/deriving-all-codegen.stdout +++ b/src/test/ui/deriving/deriving-all-codegen.stdout @@ -367,65 +367,112 @@ 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; *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 Packed {} +impl ::core::marker::StructuralPartialEq for PackedCopy {} #[automatically_derived] #[allow(unused_qualifications)] -impl ::core::cmp::PartialEq for Packed { +impl ::core::cmp::PartialEq 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 eq(&self, other: &PackedCopy) -> bool { { self.0 } == { other.0 } } #[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 ne(&self, other: &PackedCopy) -> bool { { self.0 } != { other.0 } } } -impl ::core::marker::StructuralEq for Packed {} +impl ::core::marker::StructuralEq for PackedCopy {} #[automatically_derived] #[allow(unused_qualifications)] -impl ::core::cmp::Eq for Packed { +impl ::core::cmp::Eq for PackedCopy { #[inline] #[doc(hidden)] #[no_coverage] @@ -435,23 +482,19 @@ impl ::core::cmp::Eq for Packed { } #[automatically_derived] #[allow(unused_qualifications)] -impl ::core::cmp::PartialOrd for Packed { +impl ::core::cmp::PartialOrd for PackedCopy { #[inline] - fn partial_cmp(&self, other: &Packed) + fn partial_cmp(&self, other: &PackedCopy) -> ::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) + ::core::cmp::PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) } } #[automatically_derived] #[allow(unused_qualifications)] -impl ::core::cmp::Ord for Packed { +impl ::core::cmp::Ord for PackedCopy { #[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: &PackedCopy) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&{ self.0 }, &{ other.0 }) } } @@ -527,9 +570,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 +580,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 +591,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 +603,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 +633,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 +644,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 +736,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 +753,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 +763,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 +783,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 +793,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 +827,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 +849,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 +867,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 +911,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 +938,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 +967,13 @@ enum Fielded { X(u32), Y(bool), Z(Option), } 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 +981,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 +998,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 +1013,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 +1061,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 +1083,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, + } } } diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs index 269cc3cc2825..4389617de66b 100644 --- a/src/test/ui/print_type_sizes/packed.rs +++ b/src/test/ui/print_type_sizes/packed.rs @@ -15,7 +15,7 @@ #![allow(dead_code)] #![feature(start)] -#[derive(Default)] +#[derive(Clone, Copy, Default)] #[repr(packed)] struct Packed1 { a: u8, @@ -26,7 +26,7 @@ struct Packed1 { d: u8, } -#[derive(Default)] +#[derive(Clone, Copy, Default)] #[repr(packed(2))] struct Packed2 { a: u8, @@ -37,7 +37,7 @@ struct Packed2 { d: u8, } -#[derive(Default)] +#[derive(Clone, Copy, Default)] #[repr(packed(2))] #[repr(C)] struct Packed2C {