From 2e93f2c92f1f1125d51f104f611a235466154aa1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 21 Nov 2022 14:40:32 +1100 Subject: [PATCH] Allow more deriving on packed structs. Currently, deriving on packed structs has some non-trivial limitations, related to the fact that taking references on unaligned fields is UB. The current approach to field accesses in derived code: - Normal case: `&self.0` - In a packed struct that derives `Copy`: `&{self.0}` - In a packed struct that doesn't derive `Copy`: `&self.0` Plus, we disallow deriving any builtin traits other than `Default` for any packed generic type, because it's possible that there might be misaligned fields. This is a fairly broad restriction. Plus, we disallow deriving any builtin traits other than `Default` for most packed types that don't derive `Copy`. (The exceptions are those where the alignments inherently satisfy the packing, e.g. in a type with `repr(packed(N))` where all the fields have alignments of `N` or less anyway. Such types are pretty strange, because the `packed` attribute is not having any effect.) This commit introduces a new, simpler approach to field accesses: - Normal case: `&self.0` - In a packed struct: `&{self.0}` In the latter case, this requires that all fields impl `Copy`, which is a new restriction. This means that the following example compiles under the old approach and doesn't compile under the new approach. ``` #[derive(Debug)] struct NonCopy(u8); #[derive(Debug) #[repr(packed)] struct MyType(NonCopy); ``` (Note that the old approach's support for cases like this was brittle. Changing the `u8` to a `u16` would be enough to stop it working. So not much capability is lost here.) However, the other constraints from the old rules are removed. We can now derive builtin traits for packed generic structs like this: ``` trait Trait { type A; } #[derive(Hash)] #[repr(packed)] pub struct Foo(T, T::A); ``` To allow this, we add a `T: Copy` bound in the derived impl and a `T::A: Copy` bound in where clauses. So `T` and `T::A` must impl `Copy`. We can now also derive builtin traits for packed structs that don't derive `Copy`, so long as the fields impl `Copy`: ``` #[derive(Hash)] #[repr(packed)] pub struct Foo(u32); ``` This includes types that hand-impl `Copy` rather than deriving it, such as the following, that show up in winapi-0.2: ``` #[derive(Clone)] #[repr(packed)] struct MyType(i32); impl Copy for MyType {} ``` The new approach is simpler to understand and implement, and it avoids the need for the `unsafe_derive_on_repr_packed` check. One exception is required for backwards-compatibility: we allow `[u8]` fields for now. There is a new lint for this, `byte_slice_in_packed_struct_with_derive`. --- .../src/deriving/bounds.rs | 1 + .../src/deriving/clone.rs | 1 + .../src/deriving/cmp/eq.rs | 1 + .../src/deriving/cmp/ord.rs | 1 + .../src/deriving/cmp/partial_eq.rs | 1 + .../src/deriving/cmp/partial_ord.rs | 1 + .../src/deriving/debug.rs | 1 + .../src/deriving/decodable.rs | 1 + .../src/deriving/default.rs | 1 + .../src/deriving/encodable.rs | 1 + .../src/deriving/generic/mod.rs | 135 +++-- .../rustc_builtin_macros/src/deriving/hash.rs | 1 + compiler/rustc_lint/src/context.rs | 3 + compiler/rustc_lint_defs/src/builtin.rs | 33 ++ compiler/rustc_lint_defs/src/lib.rs | 1 + compiler/rustc_middle/src/query/mod.rs | 9 - .../src/check_packed_ref.rs | 45 +- compiler/rustc_mir_transform/src/lib.rs | 1 - .../ui/derives/deriving-with-repr-packed-2.rs | 22 + .../deriving-with-repr-packed-2.stderr | 33 ++ tests/ui/derives/deriving-with-repr-packed.rs | 41 +- .../derives/deriving-with-repr-packed.stderr | 126 +---- tests/ui/deriving/deriving-all-codegen.rs | 85 ++- tests/ui/deriving/deriving-all-codegen.stderr | 63 +++ tests/ui/deriving/deriving-all-codegen.stdout | 513 +++++++++++++++--- 25 files changed, 823 insertions(+), 298 deletions(-) create mode 100644 tests/ui/derives/deriving-with-repr-packed-2.rs create mode 100644 tests/ui/derives/deriving-with-repr-packed-2.stderr create mode 100644 tests/ui/deriving/deriving-all-codegen.stderr diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 240167146e1..0481a118906 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -17,6 +17,7 @@ pub fn expand_deriving_copy( span, path: path_std!(marker::Copy), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: false, additional_bounds: Vec::new(), supports_unions: true, methods: Vec::new(), diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index ef5a75f428d..42f50d8ade2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -73,6 +73,7 @@ pub fn expand_deriving_clone( span, path: path_std!(clone::Clone), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: bounds, supports_unions: true, methods: vec![MethodDef { diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 3e994f037ad..424719f9795 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -27,6 +27,7 @@ pub fn expand_deriving_eq( span, path: path_std!(cmp::Eq), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: true, methods: vec![MethodDef { diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index a926fca4e65..671f32550d2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -20,6 +20,7 @@ pub fn expand_deriving_ord( span, path: path_std!(cmp::Ord), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: false, methods: vec![MethodDef { 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 9051fe0b28a..88d454fbc11 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -84,6 +84,7 @@ fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOr span, path: path_std!(cmp::PartialEq), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: false, methods, 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 2fc30d8e05f..bcc90442eb7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -59,6 +59,7 @@ pub fn expand_deriving_partial_ord( span, path: path_std!(cmp::PartialOrd), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: vec![], supports_unions: false, methods: vec![partial_cmp_def], diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index e0f487e8648..897048f6421 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -23,6 +23,7 @@ pub fn expand_deriving_debug( span, path: path_std!(fmt::Debug), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: false, methods: vec![MethodDef { diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 5f9519dad1b..c783e46eda9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -25,6 +25,7 @@ pub fn expand_deriving_rustc_decodable( span, path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: false, methods: vec![MethodDef { diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 18270747296..a6c8b111527 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -25,6 +25,7 @@ pub fn expand_deriving_default( span, path: Path::new(vec![kw::Default, sym::Default]), skip_path_as_bound: has_a_default_variant(item), + needs_copy_as_bound_if_packed: false, additional_bounds: Vec::new(), supports_unions: false, methods: vec![MethodDef { diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 2afeed927ac..a5e2b599df4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -109,6 +109,7 @@ pub fn expand_deriving_rustc_encodable( span, path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global), skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: false, methods: vec![MethodDef { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 17b7ac0eba1..97de40bac34 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -165,11 +165,12 @@ use crate::deriving; use rustc_ast::ptr::P; use rustc_ast::{ - self as ast, BindingAnnotation, ByRef, EnumDef, Expr, Generics, Mutability, PatKind, + self as ast, BindingAnnotation, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, + Mutability, PatKind, TyKind, VariantData, }; -use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_session::lint::builtin::BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::cell::RefCell; @@ -191,6 +192,9 @@ pub struct TraitDef<'a> { /// Whether to skip adding the current trait as a bound to the type parameters of the type. pub skip_path_as_bound: bool, + /// Whether `Copy` is needed as an additional bound on type parameters in a packed struct. + pub needs_copy_as_bound_if_packed: bool, + /// Additional bounds required of any type parameters of the type, /// other than the current trait pub additional_bounds: Vec, @@ -455,18 +459,6 @@ pub fn expand_ext( } false }); - let has_no_type_params = match &item.kind { - ast::ItemKind::Struct(_, generics) - | ast::ItemKind::Enum(_, generics) - | ast::ItemKind::Union(_, 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 copy_fields = - is_packed && has_no_type_params && cx.resolver.has_derive_copy(container_id); let newitem = match &item.kind { ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def( @@ -475,7 +467,7 @@ pub fn expand_ext( item.ident, generics, from_scratch, - copy_fields, + is_packed, ), ast::ItemKind::Enum(enum_def, generics) => { // We ignore `is_packed` here, because `repr(packed)` @@ -493,7 +485,7 @@ pub fn expand_ext( item.ident, generics, from_scratch, - copy_fields, + is_packed, ) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -565,6 +557,7 @@ fn create_derived_impl( generics: &Generics, field_tys: Vec>, methods: Vec>, + is_packed: bool, ) -> P { let trait_path = self.path.to_path(cx, self.span, type_ident, generics); @@ -607,20 +600,32 @@ fn create_derived_impl( .map(|param| match ¶m.kind { GenericParamKind::Lifetime { .. } => param.clone(), GenericParamKind::Type { .. } => { - // I don't think this can be moved out of the loop, since - // a GenericBound requires an ast id - let bounds: Vec<_> = - // extra restrictions on the generics parameters to the - // type being derived upon - self.additional_bounds.iter().map(|p| { - cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)) - }).chain( - // require the current trait - self.skip_path_as_bound.not().then(|| cx.trait_bound(trait_path.clone())) - ).chain( - // also add in any bounds from the declaration - param.bounds.iter().cloned() - ).collect(); + // Extra restrictions on the generics parameters to the + // type being derived upon. + let bounds: Vec<_> = self + .additional_bounds + .iter() + .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) + .chain( + // Add a bound for the current trait. + self.skip_path_as_bound + .not() + .then(|| cx.trait_bound(trait_path.clone())), + ) + .chain({ + // Add a `Copy` bound if required. + if is_packed && self.needs_copy_as_bound_if_packed { + let p = deriving::path_std!(marker::Copy); + Some(cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) + } else { + None + } + }) + .chain( + // Also add in any bounds from the declaration. + param.bounds.iter().cloned(), + ) + .collect(); cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None) } @@ -692,9 +697,17 @@ fn create_derived_impl( .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) .collect(); - // require the current trait + // Require the current trait. bounds.push(cx.trait_bound(trait_path.clone())); + // Add a `Copy` bound if required. + if is_packed && self.needs_copy_as_bound_if_packed { + let p = deriving::path_std!(marker::Copy); + bounds.push( + cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)), + ); + } + let predicate = ast::WhereBoundPredicate { span: self.span, bound_generic_params: field_ty_param.bound_generic_params, @@ -762,7 +775,7 @@ fn expand_struct_def( type_ident: Ident, generics: &Generics, from_scratch: bool, - copy_fields: bool, + is_packed: bool, ) -> P { let field_tys: Vec> = struct_def.fields().iter().map(|field| field.ty.clone()).collect(); @@ -790,7 +803,7 @@ fn expand_struct_def( type_ident, &selflike_args, &nonselflike_args, - copy_fields, + is_packed, ) }; @@ -806,7 +819,7 @@ fn expand_struct_def( }) .collect(); - self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed) } fn expand_enum_def( @@ -861,7 +874,8 @@ fn expand_enum_def( }) .collect(); - self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + let is_packed = false; // enums are never packed + self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed) } } @@ -1011,8 +1025,8 @@ fn create_method( /// ``` /// But if the struct is `repr(packed)`, we can't use something like /// `&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: + /// method that takes a reference, we use a local block to force a copy. + /// This requires that the field impl `Copy`. /// ``` /// # struct A { x: u8, y: u8 } /// impl PartialEq for A { @@ -1027,10 +1041,6 @@ fn create_method( /// ::core::hash::Hash::hash(&{ self.y }, state) /// } /// } - /// ``` - /// If the struct doesn't impl `Copy`, we use the normal `&self.x`. This - /// 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,12 +1049,12 @@ fn expand_struct_method_body<'b>( type_ident: Ident, selflike_args: &[P], nonselflike_args: &[P], - copy_fields: bool, + is_packed: bool, ) -> BlockOrExpr { assert!(selflike_args.len() == 1 || selflike_args.len() == 2); let selflike_fields = - trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, copy_fields); + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, is_packed); self.call_substructure_method( cx, trait_, @@ -1514,7 +1524,7 @@ fn create_struct_field_access_fields( cx: &mut ExtCtxt<'_>, selflike_args: &[P], struct_def: &'a VariantData, - copy_fields: bool, + is_packed: bool, ) -> Vec { self.create_fields(struct_def, |i, struct_field, sp| { selflike_args @@ -1533,10 +1543,39 @@ fn create_struct_field_access_fields( }), ), ); - if copy_fields { - field_expr = cx.expr_block( - cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]), - ); + // In general, fields in packed structs are copied via a + // block, e.g. `&{self.0}`. The one exception is `[u8]` + // fields, which cannot be copied and also never cause + // unaligned references. This exception is allowed to + // handle the `FlexZeroSlice` type in the `zerovec` crate + // within `icu4x-0.9.0`. + // + // Once use of `icu4x-0.9.0` has dropped sufficiently, this + // exception should be removed. + let is_u8_slice = if let TyKind::Slice(ty) = &struct_field.ty.kind && + let TyKind::Path(None, rustc_ast::Path { segments, .. }) = &ty.kind && + let [seg] = segments.as_slice() && + seg.ident.name == sym::u8 && seg.args.is_none() + { + true + } else { + false + }; + if is_packed { + if is_u8_slice { + cx.sess.parse_sess.buffer_lint_with_diagnostic( + BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, + sp, + ast::CRATE_NODE_ID, + "byte slice in a packed struct that derives a built-in trait", + rustc_lint_defs::BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive + ); + } else { + // Wrap the expression in `{...}`, causing a copy. + field_expr = cx.expr_block( + cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]), + ); + } } cx.expr_addr_of(sp, field_expr) }) diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index f8570d8f86a..5c2e89c5697 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -24,6 +24,7 @@ pub fn expand_deriving_hash( span, path, skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, additional_bounds: Vec::new(), supports_unions: false, methods: vec![MethodDef { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 8046cc21cea..d1d4bb37528 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -882,6 +882,9 @@ fn lookup_with_diagnostics( ); } } + BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => { + db.help("consider implementing the trait by hand, or remove the `packed` attribute"); + } } // Rewrap `db`, and pass control to the user. decorate(db) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b6481d70bc8..4438639d635 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3381,6 +3381,7 @@ REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, NAMED_ARGUMENTS_USED_POSITIONALLY, IMPLIED_BOUNDS_ENTAILMENT, + BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, ] } @@ -4109,3 +4110,35 @@ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow, }; } + +declare_lint! { + /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field + /// (`[u8]`) is used in a `packed` struct that derives one or more built-in traits. + /// + /// ### Example + /// + /// ```rust + /// #[repr(packed)] + /// #[derive(Hash)] + /// struct FlexZeroSlice { + /// width: u8, + /// data: [u8], + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This was previously accepted but is being phased out, because fields in packed structs are + /// now required to implement `Copy` for `derive` to work. Byte slices are a temporary + /// exception because certain crates depended on them. + pub BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE, + Warn, + "`[u8]` slice used in a packed struct with `derive`", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #107457 ", + reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow, + }; + report_in_external_macro +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7054d1e9f10..6efbf5ce9ee 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -521,6 +521,7 @@ pub enum BuiltinLintDiagnostics { /// Indicates if the named argument is used as a width/precision for formatting is_formatting_arg: bool, }, + ByteSliceInPackedStructWithDerive, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 3917a97db4f..fd1e2c072de 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -807,15 +807,6 @@ } } - /// HACK: when evaluated, this reports an "unsafe derive on repr(packed)" error. - /// - /// Unsafety checking is executed for each method separately, but we only want - /// to emit this error once per derive. As there are some impls with multiple - /// methods, we use a query for deduplication. - query unsafe_derive_on_repr_packed(key: LocalDefId) -> () { - desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } - } - /// Returns the types assumed to be well formed while "inside" of the given item. /// /// Note that we've liberated the late bound regions of function signatures, so diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index 51abcf51189..3e7571aa7a2 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,32 +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); - - // FIXME: when we make this a hard error, this should have its - // own error code. - - let extra = if tcx.generics_of(def_id).own_requires_monomorphization() { - "with type or const parameters" - } else { - "that does not derive `Copy`" - }; - let message = format!( - "`{}` can't be derived on this `#[repr(packed)]` struct {}", - tcx.item_name(tcx.trait_id_of_impl(def_id.to_def_id()).expect("derived trait name")), - extra - ); - - tcx.struct_span_lint_hir( - UNALIGNED_REFERENCES, - lint_hir_id, - tcx.def_span(def_id), - message, - |lint| lint, - ); -} - 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. @@ -73,14 +41,13 @@ fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: 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 abf89e550b1..3d9137fa102 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -104,7 +104,6 @@ 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/tests/ui/derives/deriving-with-repr-packed-2.rs b/tests/ui/derives/deriving-with-repr-packed-2.rs new file mode 100644 index 00000000000..cbd7432fce4 --- /dev/null +++ b/tests/ui/derives/deriving-with-repr-packed-2.rs @@ -0,0 +1,22 @@ +#![deny(unaligned_references)] + +// Check that deriving certain builtin traits on certain packed structs cause +// errors. To avoid potentially misaligned references, field copies must be +// used, which involves adding `T: Copy` bounds. + +#[derive(Copy, Clone, Default, PartialEq, Eq)] +#[repr(packed)] +pub struct Foo(T, T, T); + +struct NonCopy; + +fn main() { + // This one is fine because `u32` impls `Copy`. + let x: Foo = Foo(1, 2, 3); + _ = x.clone(); + + // This one is an error because `NonCopy` doesn't impl `Copy`. + let x: Foo = Foo(NonCopy, NonCopy, NonCopy); + _ = x.clone(); + //~^ ERROR the method `clone` exists for struct `Foo`, but its trait bounds were not satisfied +} diff --git a/tests/ui/derives/deriving-with-repr-packed-2.stderr b/tests/ui/derives/deriving-with-repr-packed-2.stderr new file mode 100644 index 00000000000..83540739ee3 --- /dev/null +++ b/tests/ui/derives/deriving-with-repr-packed-2.stderr @@ -0,0 +1,33 @@ +error[E0599]: the method `clone` exists for struct `Foo`, but its trait bounds were not satisfied + --> $DIR/deriving-with-repr-packed-2.rs:20:11 + | +LL | pub struct Foo(T, T, T); + | ----------------- + | | + | method `clone` not found for this struct + | doesn't satisfy `Foo: Clone` +LL | +LL | struct NonCopy; + | -------------- + | | + | doesn't satisfy `NonCopy: Clone` + | doesn't satisfy `NonCopy: Copy` +... +LL | _ = x.clone(); + | ^^^^^ method cannot be called on `Foo` due to unsatisfied trait bounds + | +note: the following trait bounds were not satisfied: + `NonCopy: Clone` + `NonCopy: Copy` + --> $DIR/deriving-with-repr-packed-2.rs:7:16 + | +LL | #[derive(Copy, Clone, Default, PartialEq, Eq)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro +help: consider annotating `NonCopy` with `#[derive(Clone, Copy)]` + | +LL | #[derive(Clone, Copy)] + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/derives/deriving-with-repr-packed.rs b/tests/ui/derives/deriving-with-repr-packed.rs index 3884e397764..eddf41f5553 100644 --- a/tests/ui/derives/deriving-with-repr-packed.rs +++ b/tests/ui/derives/deriving-with-repr-packed.rs @@ -1,45 +1,38 @@ #![deny(unaligned_references)] // Check that deriving certain builtin traits on certain packed structs cause -// errors. This happens when the derived trait would need to use a potentially -// misaligned reference. But there are two cases that are allowed: -// - If all the fields within the struct meet the required alignment: 1 for -// `repr(packed)`, or `N` for `repr(packed(N))`. -// - If `Default` is the only trait derived, because it doesn't involve any -// references. +// errors. To avoid potentially misaligned references, field copies must be +// used, which involves adding `T: Copy` bounds. #[derive(Copy, Clone, Default, PartialEq, Eq)] -//~^ ERROR `Clone` can't be derived on this `#[repr(packed)]` struct with type or const parameters -//~| hard error -//~^^^ ERROR `PartialEq` can't be derived on this `#[repr(packed)]` struct with type or const parameters -//~| hard error #[repr(packed)] pub struct Foo(T, T, T); +// This one is fine because the fields all impl `Copy`. #[derive(Default, Hash)] -//~^ ERROR `Hash` can't be derived on this `#[repr(packed)]` struct that does not derive `Copy` -//~| hard error #[repr(packed)] pub struct Bar(u32, u32, u32); -// This one is fine because the field alignment is 1. -#[derive(Default, Hash)] -#[repr(packed)] -pub struct Bar2(u8, i8, bool); - -// This one is fine because the field alignment is 2, matching `packed(2)`. -#[derive(Default, Hash)] -#[repr(packed(2))] -pub struct Bar3(u16, i16, bool); - // This one is fine because it's not packed. #[derive(Debug, Default)] struct Y(usize); +// This one has an error because `Y` doesn't impl `Copy`. +// Note: there is room for improvement in the error message. #[derive(Debug, Default)] -//~^ ERROR `Debug` can't be derived on this `#[repr(packed)]` struct that does not derive `Copy` -//~| hard error #[repr(packed)] struct X(Y); +//~^ ERROR cannot move out of `self` which is behind a shared reference + +// This is currently allowed, but will be phased out at some point. From +// `zerovec` within icu4x-0.9.0. +#[derive(Debug)] +#[repr(packed)] +struct FlexZeroSlice { + width: u8, + data: [u8], + //~^ WARNING byte slice in a packed struct that derives a built-in trait + //~^^ this was previously accepted +} fn main() {} diff --git a/tests/ui/derives/deriving-with-repr-packed.stderr b/tests/ui/derives/deriving-with-repr-packed.stderr index 0ad800c3981..2cb2a696d97 100644 --- a/tests/ui/derives/deriving-with-repr-packed.stderr +++ b/tests/ui/derives/deriving-with-repr-packed.stderr @@ -1,111 +1,45 @@ -error: `Clone` can't be derived on this `#[repr(packed)]` struct with type or const parameters - --> $DIR/deriving-with-repr-packed.rs:11:16 +warning: byte slice in a packed struct that derives a built-in trait + --> $DIR/deriving-with-repr-packed.rs:33:5 | -LL | #[derive(Copy, Clone, Default, PartialEq, Eq)] - | ^^^^^ +LL | #[derive(Debug)] + | ----- in this derive macro expansion +... +LL | data: [u8], + | ^^^^^^^^^^ | = 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: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: `PartialEq` can't be derived on this `#[repr(packed)]` struct with type or const parameters - --> $DIR/deriving-with-repr-packed.rs:11:32 - | -LL | #[derive(Copy, Clone, Default, 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: `Hash` can't be derived on this `#[repr(packed)]` struct that does not derive `Copy` - --> $DIR/deriving-with-repr-packed.rs:19:19 - | -LL | #[derive(Default, Hash)] - | ^^^^ - | - = 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 `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: for more information, see issue #107457 + = help: consider implementing the trait by hand, or remove the `packed` attribute + = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default + = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `Debug` can't be derived on this `#[repr(packed)]` struct that does not derive `Copy` - --> $DIR/deriving-with-repr-packed.rs:39:10 +error[E0507]: cannot move out of `self` which is behind a shared reference + --> $DIR/deriving-with-repr-packed.rs:24:10 | LL | #[derive(Debug, Default)] - | ^^^^^ + | ----- in this derive macro expansion +LL | #[repr(packed)] +LL | struct X(Y); + | ^ move occurs because `self.0` has type `Y`, which does not implement the `Copy` trait | - = 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 `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to previous error; 1 warning emitted +For more information about this error, try `rustc --explain E0507`. Future incompatibility report: Future breakage diagnostic: -error: `Clone` can't be derived on this `#[repr(packed)]` struct with type or const parameters - --> $DIR/deriving-with-repr-packed.rs:11:16 - | -LL | #[derive(Copy, Clone, Default, 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: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -Future breakage diagnostic: -error: `PartialEq` can't be derived on this `#[repr(packed)]` struct with type or const parameters - --> $DIR/deriving-with-repr-packed.rs:11:32 - | -LL | #[derive(Copy, Clone, Default, 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: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -Future breakage diagnostic: -error: `Hash` can't be derived on this `#[repr(packed)]` struct that does not derive `Copy` - --> $DIR/deriving-with-repr-packed.rs:19:19 - | -LL | #[derive(Default, Hash)] - | ^^^^ +warning: byte slice in a packed struct that derives a built-in trait + --> $DIR/deriving-with-repr-packed.rs:33:5 | - = 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: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) - -Future breakage diagnostic: -error: `Debug` can't be derived on this `#[repr(packed)]` struct that does not derive `Copy` - --> $DIR/deriving-with-repr-packed.rs:39:10 - | -LL | #[derive(Debug, Default)] - | ^^^^^ +LL | #[derive(Debug)] + | ----- in this derive macro expansion +... +LL | data: [u8], + | ^^^^^^^^^^ | = 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: the lint level is defined here - --> $DIR/deriving-with-repr-packed.rs:1:9 - | -LL | #![deny(unaligned_references)] - | ^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: for more information, see issue #107457 + = help: consider implementing the trait by hand, or remove the `packed` attribute + = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default + = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/deriving/deriving-all-codegen.rs b/tests/ui/deriving/deriving-all-codegen.rs index ba7809413bd..51f9708d3cd 100644 --- a/tests/ui/deriving/deriving-all-codegen.rs +++ b/tests/ui/deriving/deriving-all-codegen.rs @@ -21,36 +21,88 @@ #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] struct Empty; -// A basic struct. +// A basic struct. Note: because this derives `Copy`, it gets the simple +// `clone` implemention that just does `*self`. #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] struct Point { x: u32, y: u32, } -// A large struct. -#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] +// A basic packed struct. Note: because this derives `Copy`, it gets the simple +// `clone` implemention that just does `*self`. +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[repr(packed)] +struct PackedPoint { + x: u32, + y: u32, +} + +// A large struct. Note: because this derives `Copy`, it gets the simple +// `clone` implemention that just does `*self`. +#[derive(Clone, Copy, 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, } +// A struct that doesn't impl `Copy`, which means it gets the non-simple +// `clone` implemention that clones the fields individually. +#[derive(Clone)] +struct NonCopy(u32); + +// A packed struct that doesn't impl `Copy`, which means it gets the non-simple +// `clone` implemention that clones the fields individually. +#[derive(Clone)] +#[repr(packed)] +struct PackedNonCopy(u32); + +// A struct that impls `Copy` manually, which means it gets the non-simple +// `clone` implemention that clones the fields individually. +#[derive(Clone)] +struct ManualCopy(u32); +impl Copy for ManualCopy {} + +// A packed struct that impls `Copy` manually, which means it gets the +// non-simple `clone` implemention that clones the fields individually. +#[derive(Clone)] +#[repr(packed)] +struct PackedManualCopy(u32); +impl Copy for PackedManualCopy {} + // 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)] +// A packed struct with an unsized `[u8]` field. This is currently allowed, but +// causes a warning and will be phased out at some point. +#[derive(Debug, Hash)] #[repr(packed)] -struct PackedCopy(u32); +struct PackedUnsizedU8([u8]); +//~^ WARNING byte slice in a packed struct that derives a built-in trait +//~^^ WARNING byte slice in a packed struct that derives a built-in trait +//~^^^ this was previously accepted +//~^^^^ this was previously accepted -// 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)] +trait Trait { + type A; +} + +// A generic struct involving an associated type. +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] +struct Generic { + t: T, + ta: T::A, + u: U, +} + +// A packed, generic tuple struct involving an associated type. Because it is +// packed, a `T: Copy` bound is added to all impls (and where clauses within +// them) except for `Default`. This is because we must access fields using +// copies (e.g. `&{self.0}`), instead of using direct references (e.g. +// `&self.0`) which may be misaligned in a packed struct. +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(packed)] -struct PackedNonCopy(u8); +struct PackedGeneric(T, T::A, U); // An empty enum. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -97,6 +149,13 @@ enum Fielded { Z(Option), } +// A generic enum. Note that `Default` cannot be derived for this enum. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +enum EnumGeneric { + One(T), + Two(U), +} + // A union. Most builtin traits are not derivable for unions. #[derive(Clone, Copy)] pub union Union { diff --git a/tests/ui/deriving/deriving-all-codegen.stderr b/tests/ui/deriving/deriving-all-codegen.stderr new file mode 100644 index 00000000000..503f0cae73b --- /dev/null +++ b/tests/ui/deriving/deriving-all-codegen.stderr @@ -0,0 +1,63 @@ +warning: byte slice in a packed struct that derives a built-in trait + --> $DIR/deriving-all-codegen.rs:80:24 + | +LL | #[derive(Debug, Hash)] + | ----- in this derive macro expansion +LL | #[repr(packed)] +LL | struct PackedUnsizedU8([u8]); + | ^^^^ + | + = 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 #107457 + = help: consider implementing the trait by hand, or remove the `packed` attribute + = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default + = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: byte slice in a packed struct that derives a built-in trait + --> $DIR/deriving-all-codegen.rs:80:24 + | +LL | #[derive(Debug, Hash)] + | ---- in this derive macro expansion +LL | #[repr(packed)] +LL | struct PackedUnsizedU8([u8]); + | ^^^^ + | + = 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 #107457 + = help: consider implementing the trait by hand, or remove the `packed` attribute + = note: this warning originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 2 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +warning: byte slice in a packed struct that derives a built-in trait + --> $DIR/deriving-all-codegen.rs:80:24 + | +LL | #[derive(Debug, Hash)] + | ----- in this derive macro expansion +LL | #[repr(packed)] +LL | struct PackedUnsizedU8([u8]); + | ^^^^ + | + = 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 #107457 + = help: consider implementing the trait by hand, or remove the `packed` attribute + = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default + = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) + +Future breakage diagnostic: +warning: byte slice in a packed struct that derives a built-in trait + --> $DIR/deriving-all-codegen.rs:80:24 + | +LL | #[derive(Debug, Hash)] + | ---- in this derive macro expansion +LL | #[repr(packed)] +LL | struct PackedUnsizedU8([u8]); + | ^^^^ + | + = 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 #107457 + = help: consider implementing the trait by hand, or remove the `packed` attribute + = note: `#[warn(byte_slice_in_packed_struct_with_derive)]` on by default + = note: this warning originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index 2a93a05c627..b4874cef134 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -78,7 +78,8 @@ impl ::core::cmp::Ord for Empty { } } -// A basic struct. +// A basic struct. Note: because this derives `Copy`, it gets the simple +// `clone` implemention that just does `*self`. struct Point { x: u32, y: u32, @@ -161,7 +162,95 @@ impl ::core::cmp::Ord for Point { } } -// A large struct. +// A basic packed struct. Note: because this derives `Copy`, it gets the simple +// `clone` implemention that just does `*self`. +#[repr(packed)] +struct PackedPoint { + x: u32, + y: u32, +} +#[automatically_derived] +impl ::core::clone::Clone for PackedPoint { + #[inline] + fn clone(&self) -> PackedPoint { + let _: ::core::clone::AssertParamIsClone; + *self + } +} +#[automatically_derived] +impl ::core::marker::Copy for PackedPoint { } +#[automatically_derived] +impl ::core::fmt::Debug for PackedPoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_struct_field2_finish(f, "PackedPoint", + "x", &&{ self.x }, "y", &&{ self.y }) + } +} +#[automatically_derived] +impl ::core::default::Default for PackedPoint { + #[inline] + fn default() -> PackedPoint { + PackedPoint { + x: ::core::default::Default::default(), + y: ::core::default::Default::default(), + } + } +} +#[automatically_derived] +impl ::core::hash::Hash for PackedPoint { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + ::core::hash::Hash::hash(&{ self.x }, state); + ::core::hash::Hash::hash(&{ self.y }, state) + } +} +#[automatically_derived] +impl ::core::marker::StructuralPartialEq for PackedPoint { } +#[automatically_derived] +impl ::core::cmp::PartialEq for PackedPoint { + #[inline] + fn eq(&self, other: &PackedPoint) -> bool { + { self.x } == { other.x } && { self.y } == { other.y } + } +} +#[automatically_derived] +impl ::core::marker::StructuralEq for PackedPoint { } +#[automatically_derived] +impl ::core::cmp::Eq for PackedPoint { + #[inline] + #[doc(hidden)] + #[no_coverage] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for PackedPoint { + #[inline] + fn partial_cmp(&self, other: &PackedPoint) + -> ::core::option::Option<::core::cmp::Ordering> { + match ::core::cmp::PartialOrd::partial_cmp(&{ self.x }, &{ other.x }) + { + ::core::option::Option::Some(::core::cmp::Ordering::Equal) => + ::core::cmp::PartialOrd::partial_cmp(&{ self.y }, + &{ other.y }), + cmp => cmp, + } + } +} +#[automatically_derived] +impl ::core::cmp::Ord for PackedPoint { + #[inline] + fn cmp(&self, other: &PackedPoint) -> ::core::cmp::Ordering { + match ::core::cmp::Ord::cmp(&{ self.x }, &{ other.x }) { + ::core::cmp::Ordering::Equal => + ::core::cmp::Ord::cmp(&{ self.y }, &{ other.y }), + cmp => cmp, + } + } +} + +// A large struct. Note: because this derives `Copy`, it gets the simple +// `clone` implemention that just does `*self`. struct Big { b1: u32, b2: u32, @@ -176,19 +265,13 @@ struct Big { impl ::core::clone::Clone for Big { #[inline] fn clone(&self) -> Big { - Big { - b1: ::core::clone::Clone::clone(&self.b1), - b2: ::core::clone::Clone::clone(&self.b2), - b3: ::core::clone::Clone::clone(&self.b3), - b4: ::core::clone::Clone::clone(&self.b4), - b5: ::core::clone::Clone::clone(&self.b5), - b6: ::core::clone::Clone::clone(&self.b6), - b7: ::core::clone::Clone::clone(&self.b7), - b8: ::core::clone::Clone::clone(&self.b8), - } + let _: ::core::clone::AssertParamIsClone; + *self } } #[automatically_derived] +impl ::core::marker::Copy for Big { } +#[automatically_derived] impl ::core::fmt::Debug for Big { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { let names: &'static _ = @@ -336,6 +419,54 @@ impl ::core::cmp::Ord for Big { } } +// A struct that doesn't impl `Copy`, which means it gets the non-simple +// `clone` implemention that clones the fields individually. +struct NonCopy(u32); +#[automatically_derived] +impl ::core::clone::Clone for NonCopy { + #[inline] + fn clone(&self) -> NonCopy { + NonCopy(::core::clone::Clone::clone(&self.0)) + } +} + +// A packed struct that doesn't impl `Copy`, which means it gets the non-simple +// `clone` implemention that clones the fields individually. +#[repr(packed)] +struct PackedNonCopy(u32); +#[automatically_derived] +impl ::core::clone::Clone for PackedNonCopy { + #[inline] + fn clone(&self) -> PackedNonCopy { + PackedNonCopy(::core::clone::Clone::clone(&{ self.0 })) + } +} + +// A struct that impls `Copy` manually, which means it gets the non-simple +// `clone` implemention that clones the fields individually. +struct ManualCopy(u32); +#[automatically_derived] +impl ::core::clone::Clone for ManualCopy { + #[inline] + fn clone(&self) -> ManualCopy { + ManualCopy(::core::clone::Clone::clone(&self.0)) + } +} +impl Copy for ManualCopy {} + +// A packed struct that impls `Copy` manually, which means it gets the +// non-simple `clone` implemention that clones the fields individually. +#[repr(packed)] +struct PackedManualCopy(u32); +#[automatically_derived] +impl ::core::clone::Clone for PackedManualCopy { + #[inline] + fn clone(&self) -> PackedManualCopy { + PackedManualCopy(::core::clone::Clone::clone(&{ self.0 })) + } +} +impl Copy for PackedManualCopy {} + // A struct with an unsized field. Some derives are not usable in this case. struct Unsized([u32]); #[automatically_derived] @@ -385,138 +516,265 @@ impl ::core::cmp::Ord for Unsized { } } -// A packed tuple struct that impls `Copy`. +// A packed struct with an unsized `[u8]` field. This is currently allowed, but +// causes a warning and will be phased out at some point. #[repr(packed)] -struct PackedCopy(u32); +struct PackedUnsizedU8([u8]); +#[automatically_derived] +impl ::core::fmt::Debug for PackedUnsizedU8 { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::debug_tuple_field1_finish(f, + "PackedUnsizedU8", &&self.0) + } +} #[automatically_derived] -impl ::core::clone::Clone for PackedCopy { +impl ::core::hash::Hash for PackedUnsizedU8 { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + ::core::hash::Hash::hash(&self.0, state) + } +} + +trait Trait { + type A; +} + +// A generic struct involving an associated type. +struct Generic { + t: T, + ta: T::A, + u: U, +} +#[automatically_derived] +impl + ::core::clone::Clone for Generic where T::A: ::core::clone::Clone { #[inline] - fn clone(&self) -> PackedCopy { - let _: ::core::clone::AssertParamIsClone; - *self + fn clone(&self) -> Generic { + Generic { + t: ::core::clone::Clone::clone(&self.t), + ta: ::core::clone::Clone::clone(&self.ta), + u: ::core::clone::Clone::clone(&self.u), + } } } #[automatically_derived] -impl ::core::marker::Copy for PackedCopy { } +impl + ::core::marker::Copy for Generic where T::A: ::core::marker::Copy { +} #[automatically_derived] -impl ::core::fmt::Debug for PackedCopy { +impl ::core::fmt::Debug + for Generic where T::A: ::core::fmt::Debug { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PackedCopy", - &&{ self.0 }) + ::core::fmt::Formatter::debug_struct_field3_finish(f, "Generic", "t", + &&self.t, "ta", &&self.ta, "u", &&self.u) } } #[automatically_derived] -impl ::core::default::Default for PackedCopy { +impl + ::core::default::Default for Generic where + T::A: ::core::default::Default { #[inline] - fn default() -> PackedCopy { - PackedCopy(::core::default::Default::default()) + fn default() -> Generic { + Generic { + t: ::core::default::Default::default(), + ta: ::core::default::Default::default(), + u: ::core::default::Default::default(), + } } } #[automatically_derived] -impl ::core::hash::Hash for PackedCopy { +impl ::core::hash::Hash + for Generic where T::A: ::core::hash::Hash { fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - ::core::hash::Hash::hash(&{ self.0 }, state) + ::core::hash::Hash::hash(&self.t, state); + ::core::hash::Hash::hash(&self.ta, state); + ::core::hash::Hash::hash(&self.u, state) } } #[automatically_derived] -impl ::core::marker::StructuralPartialEq for PackedCopy { } +impl ::core::marker::StructuralPartialEq for Generic { } #[automatically_derived] -impl ::core::cmp::PartialEq for PackedCopy { +impl + ::core::cmp::PartialEq for Generic where + T::A: ::core::cmp::PartialEq { #[inline] - fn eq(&self, other: &PackedCopy) -> bool { { self.0 } == { other.0 } } + fn eq(&self, other: &Generic) -> bool { + self.t == other.t && self.ta == other.ta && self.u == other.u + } } #[automatically_derived] -impl ::core::marker::StructuralEq for PackedCopy { } +impl ::core::marker::StructuralEq for Generic { } #[automatically_derived] -impl ::core::cmp::Eq for PackedCopy { +impl ::core::cmp::Eq for + Generic where T::A: ::core::cmp::Eq { #[inline] #[doc(hidden)] #[no_coverage] fn assert_receiver_is_total_eq(&self) -> () { - let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; } } #[automatically_derived] -impl ::core::cmp::PartialOrd for PackedCopy { +impl + ::core::cmp::PartialOrd for Generic where + T::A: ::core::cmp::PartialOrd { #[inline] - fn partial_cmp(&self, other: &PackedCopy) + fn partial_cmp(&self, other: &Generic) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) + match ::core::cmp::PartialOrd::partial_cmp(&self.t, &other.t) { + ::core::option::Option::Some(::core::cmp::Ordering::Equal) => + match ::core::cmp::PartialOrd::partial_cmp(&self.ta, + &other.ta) { + ::core::option::Option::Some(::core::cmp::Ordering::Equal) + => ::core::cmp::PartialOrd::partial_cmp(&self.u, &other.u), + cmp => cmp, + }, + cmp => cmp, + } } } #[automatically_derived] -impl ::core::cmp::Ord for PackedCopy { +impl ::core::cmp::Ord for + Generic where T::A: ::core::cmp::Ord { #[inline] - fn cmp(&self, other: &PackedCopy) -> ::core::cmp::Ordering { - ::core::cmp::Ord::cmp(&{ self.0 }, &{ other.0 }) + fn cmp(&self, other: &Generic) -> ::core::cmp::Ordering { + match ::core::cmp::Ord::cmp(&self.t, &other.t) { + ::core::cmp::Ordering::Equal => + match ::core::cmp::Ord::cmp(&self.ta, &other.ta) { + ::core::cmp::Ordering::Equal => + ::core::cmp::Ord::cmp(&self.u, &other.u), + cmp => cmp, + }, + cmp => cmp, + } } } -// 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. +// A packed, generic tuple struct involving an associated type. Because it is +// packed, a `T: Copy` bound is added to all impls (and where clauses within +// them) except for `Default`. This is because we must access fields using +// copies (e.g. `&{self.0}`), instead of using direct references (e.g. +// `&self.0`) which may be misaligned in a packed struct. #[repr(packed)] -struct PackedNonCopy(u8); +struct PackedGeneric(T, T::A, U); #[automatically_derived] -impl ::core::clone::Clone for PackedNonCopy { +impl ::core::clone::Clone for + PackedGeneric where T::A: ::core::clone::Clone + + ::core::marker::Copy { #[inline] - fn clone(&self) -> PackedNonCopy { - PackedNonCopy(::core::clone::Clone::clone(&self.0)) + fn clone(&self) -> PackedGeneric { + PackedGeneric(::core::clone::Clone::clone(&{ self.0 }), + ::core::clone::Clone::clone(&{ self.1 }), + ::core::clone::Clone::clone(&{ self.2 })) } } #[automatically_derived] -impl ::core::fmt::Debug for PackedNonCopy { +impl + ::core::marker::Copy for PackedGeneric where + T::A: ::core::marker::Copy { +} +#[automatically_derived] +impl ::core::fmt::Debug for + PackedGeneric where T::A: ::core::fmt::Debug + ::core::marker::Copy + { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::debug_tuple_field1_finish(f, "PackedNonCopy", - &&self.0) + ::core::fmt::Formatter::debug_tuple_field3_finish(f, "PackedGeneric", + &&{ self.0 }, &&{ self.1 }, &&{ self.2 }) } } #[automatically_derived] -impl ::core::default::Default for PackedNonCopy { +impl + ::core::default::Default for PackedGeneric where + T::A: ::core::default::Default { #[inline] - fn default() -> PackedNonCopy { - PackedNonCopy(::core::default::Default::default()) + fn default() -> PackedGeneric { + PackedGeneric(::core::default::Default::default(), + ::core::default::Default::default(), + ::core::default::Default::default()) } } #[automatically_derived] -impl ::core::hash::Hash for PackedNonCopy { +impl ::core::hash::Hash for + PackedGeneric where T::A: ::core::hash::Hash + ::core::marker::Copy + { fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { - ::core::hash::Hash::hash(&self.0, state) + ::core::hash::Hash::hash(&{ self.0 }, state); + ::core::hash::Hash::hash(&{ self.1 }, state); + ::core::hash::Hash::hash(&{ self.2 }, state) } } #[automatically_derived] -impl ::core::marker::StructuralPartialEq for PackedNonCopy { } +impl ::core::marker::StructuralPartialEq for PackedGeneric + { +} #[automatically_derived] -impl ::core::cmp::PartialEq for PackedNonCopy { +impl ::core::cmp::PartialEq + for PackedGeneric where T::A: ::core::cmp::PartialEq + + ::core::marker::Copy { #[inline] - fn eq(&self, other: &PackedNonCopy) -> bool { self.0 == other.0 } + fn eq(&self, other: &PackedGeneric) -> bool { + { self.0 } == { other.0 } && { self.1 } == { other.1 } && + { self.2 } == { other.2 } + } } #[automatically_derived] -impl ::core::marker::StructuralEq for PackedNonCopy { } +impl ::core::marker::StructuralEq for PackedGeneric { } #[automatically_derived] -impl ::core::cmp::Eq for PackedNonCopy { +impl ::core::cmp::Eq for PackedGeneric where + T::A: ::core::cmp::Eq + ::core::marker::Copy { #[inline] #[doc(hidden)] #[no_coverage] fn assert_receiver_is_total_eq(&self) -> () { - let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; } } #[automatically_derived] -impl ::core::cmp::PartialOrd for PackedNonCopy { +impl ::core::cmp::PartialOrd + for PackedGeneric where T::A: ::core::cmp::PartialOrd + + ::core::marker::Copy { #[inline] - fn partial_cmp(&self, other: &PackedNonCopy) + fn partial_cmp(&self, other: &PackedGeneric) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + match ::core::cmp::PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) + { + ::core::option::Option::Some(::core::cmp::Ordering::Equal) => + match ::core::cmp::PartialOrd::partial_cmp(&{ self.1 }, + &{ other.1 }) { + ::core::option::Option::Some(::core::cmp::Ordering::Equal) + => + ::core::cmp::PartialOrd::partial_cmp(&{ self.2 }, + &{ other.2 }), + cmp => cmp, + }, + cmp => cmp, + } } } #[automatically_derived] -impl ::core::cmp::Ord for PackedNonCopy { +impl ::core::cmp::Ord for PackedGeneric where + T::A: ::core::cmp::Ord + ::core::marker::Copy { #[inline] - fn cmp(&self, other: &PackedNonCopy) -> ::core::cmp::Ordering { - ::core::cmp::Ord::cmp(&self.0, &other.0) + fn cmp(&self, other: &PackedGeneric) -> ::core::cmp::Ordering { + match ::core::cmp::Ord::cmp(&{ self.0 }, &{ other.0 }) { + ::core::cmp::Ordering::Equal => + match ::core::cmp::Ord::cmp(&{ self.1 }, &{ other.1 }) { + ::core::cmp::Ordering::Equal => + ::core::cmp::Ord::cmp(&{ self.2 }, &{ other.2 }), + cmp => cmp, + }, + cmp => cmp, + } } } @@ -1051,6 +1309,125 @@ impl ::core::cmp::Ord for Fielded { } } +// A generic enum. Note that `Default` cannot be derived for this enum. +enum EnumGeneric { One(T), Two(U), } +#[automatically_derived] +impl ::core::clone::Clone + for EnumGeneric { + #[inline] + fn clone(&self) -> EnumGeneric { + match self { + EnumGeneric::One(__self_0) => + EnumGeneric::One(::core::clone::Clone::clone(__self_0)), + EnumGeneric::Two(__self_0) => + EnumGeneric::Two(::core::clone::Clone::clone(__self_0)), + } + } +} +#[automatically_derived] +impl ::core::marker::Copy + for EnumGeneric { +} +#[automatically_derived] +impl ::core::fmt::Debug for + EnumGeneric { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + match self { + EnumGeneric::One(__self_0) => + ::core::fmt::Formatter::debug_tuple_field1_finish(f, "One", + &__self_0), + EnumGeneric::Two(__self_0) => + ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Two", + &__self_0), + } + } +} +#[automatically_derived] +impl ::core::hash::Hash for + EnumGeneric { + fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { + let __self_tag = ::core::intrinsics::discriminant_value(self); + ::core::hash::Hash::hash(&__self_tag, state); + match self { + EnumGeneric::One(__self_0) => + ::core::hash::Hash::hash(__self_0, state), + EnumGeneric::Two(__self_0) => + ::core::hash::Hash::hash(__self_0, state), + } + } +} +#[automatically_derived] +impl ::core::marker::StructuralPartialEq for EnumGeneric { } +#[automatically_derived] +impl + ::core::cmp::PartialEq for EnumGeneric { + #[inline] + fn eq(&self, other: &EnumGeneric) -> bool { + let __self_tag = ::core::intrinsics::discriminant_value(self); + let __arg1_tag = ::core::intrinsics::discriminant_value(other); + __self_tag == __arg1_tag && + match (self, other) { + (EnumGeneric::One(__self_0), EnumGeneric::One(__arg1_0)) => + *__self_0 == *__arg1_0, + (EnumGeneric::Two(__self_0), EnumGeneric::Two(__arg1_0)) => + *__self_0 == *__arg1_0, + _ => unsafe { ::core::intrinsics::unreachable() } + } + } +} +#[automatically_derived] +impl ::core::marker::StructuralEq for EnumGeneric { } +#[automatically_derived] +impl ::core::cmp::Eq for + EnumGeneric { + #[inline] + #[doc(hidden)] + #[no_coverage] + fn assert_receiver_is_total_eq(&self) -> () { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } +} +#[automatically_derived] +impl + ::core::cmp::PartialOrd for EnumGeneric { + #[inline] + fn partial_cmp(&self, other: &EnumGeneric) + -> ::core::option::Option<::core::cmp::Ordering> { + let __self_tag = ::core::intrinsics::discriminant_value(self); + let __arg1_tag = ::core::intrinsics::discriminant_value(other); + match (self, other) { + (EnumGeneric::One(__self_0), EnumGeneric::One(__arg1_0)) => + ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), + (EnumGeneric::Two(__self_0), EnumGeneric::Two(__arg1_0)) => + ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), + _ => + ::core::cmp::PartialOrd::partial_cmp(&__self_tag, + &__arg1_tag), + } + } +} +#[automatically_derived] +impl ::core::cmp::Ord for + EnumGeneric { + #[inline] + fn cmp(&self, other: &EnumGeneric) -> ::core::cmp::Ordering { + 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) { + (EnumGeneric::One(__self_0), EnumGeneric::One(__arg1_0)) => + ::core::cmp::Ord::cmp(__self_0, __arg1_0), + (EnumGeneric::Two(__self_0), EnumGeneric::Two(__arg1_0)) => + ::core::cmp::Ord::cmp(__self_0, __arg1_0), + _ => unsafe { ::core::intrinsics::unreachable() } + }, + cmp => cmp, + } + } +} + // A union. Most builtin traits are not derivable for unions. pub union Union { pub b: bool, -- 2.44.0