Skip to content

Commit 3f25e56

Browse files
committed
Auto merge of #104429 - nnethercote:more-deriving-on-packed-structs, r=RalfJung
More deriving on packed structs See [here](#104429 (comment)) for the t-lang nomination summary, and [here](#104429 (comment)) for the approval. r? `@RalfJung`
2 parents f55b002 + 2e93f2c commit 3f25e56

25 files changed

+823
-298
lines changed

compiler/rustc_builtin_macros/src/deriving/bounds.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn expand_deriving_copy(
1717
span,
1818
path: path_std!(marker::Copy),
1919
skip_path_as_bound: false,
20+
needs_copy_as_bound_if_packed: false,
2021
additional_bounds: Vec::new(),
2122
supports_unions: true,
2223
methods: Vec::new(),

compiler/rustc_builtin_macros/src/deriving/clone.rs

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub fn expand_deriving_clone(
7373
span,
7474
path: path_std!(clone::Clone),
7575
skip_path_as_bound: false,
76+
needs_copy_as_bound_if_packed: true,
7677
additional_bounds: bounds,
7778
supports_unions: true,
7879
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub fn expand_deriving_eq(
2727
span,
2828
path: path_std!(cmp::Eq),
2929
skip_path_as_bound: false,
30+
needs_copy_as_bound_if_packed: true,
3031
additional_bounds: Vec::new(),
3132
supports_unions: true,
3233
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub fn expand_deriving_ord(
2020
span,
2121
path: path_std!(cmp::Ord),
2222
skip_path_as_bound: false,
23+
needs_copy_as_bound_if_packed: true,
2324
additional_bounds: Vec::new(),
2425
supports_unions: false,
2526
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ pub fn expand_deriving_partial_eq(
8484
span,
8585
path: path_std!(cmp::PartialEq),
8686
skip_path_as_bound: false,
87+
needs_copy_as_bound_if_packed: true,
8788
additional_bounds: Vec::new(),
8889
supports_unions: false,
8990
methods,

compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub fn expand_deriving_partial_ord(
5959
span,
6060
path: path_std!(cmp::PartialOrd),
6161
skip_path_as_bound: false,
62+
needs_copy_as_bound_if_packed: true,
6263
additional_bounds: vec![],
6364
supports_unions: false,
6465
methods: vec![partial_cmp_def],

compiler/rustc_builtin_macros/src/deriving/debug.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub fn expand_deriving_debug(
2323
span,
2424
path: path_std!(fmt::Debug),
2525
skip_path_as_bound: false,
26+
needs_copy_as_bound_if_packed: true,
2627
additional_bounds: Vec::new(),
2728
supports_unions: false,
2829
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/decodable.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn expand_deriving_rustc_decodable(
2525
span,
2626
path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global),
2727
skip_path_as_bound: false,
28+
needs_copy_as_bound_if_packed: true,
2829
additional_bounds: Vec::new(),
2930
supports_unions: false,
3031
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/default.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn expand_deriving_default(
2525
span,
2626
path: Path::new(vec![kw::Default, sym::Default]),
2727
skip_path_as_bound: has_a_default_variant(item),
28+
needs_copy_as_bound_if_packed: false,
2829
additional_bounds: Vec::new(),
2930
supports_unions: false,
3031
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/encodable.rs

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub fn expand_deriving_rustc_encodable(
109109
span,
110110
path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global),
111111
skip_path_as_bound: false,
112+
needs_copy_as_bound_if_packed: true,
112113
additional_bounds: Vec::new(),
113114
supports_unions: false,
114115
methods: vec![MethodDef {

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+87-48
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,12 @@ pub use SubstructureFields::*;
165165
use crate::deriving;
166166
use rustc_ast::ptr::P;
167167
use rustc_ast::{
168-
self as ast, BindingAnnotation, ByRef, EnumDef, Expr, Generics, Mutability, PatKind,
168+
self as ast, BindingAnnotation, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
169+
Mutability, PatKind, TyKind, VariantData,
169170
};
170-
use rustc_ast::{GenericArg, GenericParamKind, VariantData};
171171
use rustc_attr as attr;
172172
use rustc_expand::base::{Annotatable, ExtCtxt};
173+
use rustc_session::lint::builtin::BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE;
173174
use rustc_span::symbol::{kw, sym, Ident, Symbol};
174175
use rustc_span::{Span, DUMMY_SP};
175176
use std::cell::RefCell;
@@ -191,6 +192,9 @@ pub struct TraitDef<'a> {
191192
/// Whether to skip adding the current trait as a bound to the type parameters of the type.
192193
pub skip_path_as_bound: bool,
193194

195+
/// Whether `Copy` is needed as an additional bound on type parameters in a packed struct.
196+
pub needs_copy_as_bound_if_packed: bool,
197+
194198
/// Additional bounds required of any type parameters of the type,
195199
/// other than the current trait
196200
pub additional_bounds: Vec<Ty>,
@@ -455,18 +459,6 @@ impl<'a> TraitDef<'a> {
455459
}
456460
false
457461
});
458-
let has_no_type_params = match &item.kind {
459-
ast::ItemKind::Struct(_, generics)
460-
| ast::ItemKind::Enum(_, generics)
461-
| ast::ItemKind::Union(_, generics) => !generics
462-
.params
463-
.iter()
464-
.any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })),
465-
_ => unreachable!(),
466-
};
467-
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
468-
let copy_fields =
469-
is_packed && has_no_type_params && cx.resolver.has_derive_copy(container_id);
470462

471463
let newitem = match &item.kind {
472464
ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
@@ -475,7 +467,7 @@ impl<'a> TraitDef<'a> {
475467
item.ident,
476468
generics,
477469
from_scratch,
478-
copy_fields,
470+
is_packed,
479471
),
480472
ast::ItemKind::Enum(enum_def, generics) => {
481473
// We ignore `is_packed` here, because `repr(packed)`
@@ -493,7 +485,7 @@ impl<'a> TraitDef<'a> {
493485
item.ident,
494486
generics,
495487
from_scratch,
496-
copy_fields,
488+
is_packed,
497489
)
498490
} else {
499491
cx.span_err(mitem.span, "this trait cannot be derived for unions");
@@ -565,6 +557,7 @@ impl<'a> TraitDef<'a> {
565557
generics: &Generics,
566558
field_tys: Vec<P<ast::Ty>>,
567559
methods: Vec<P<ast::AssocItem>>,
560+
is_packed: bool,
568561
) -> P<ast::Item> {
569562
let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
570563

@@ -607,20 +600,32 @@ impl<'a> TraitDef<'a> {
607600
.map(|param| match &param.kind {
608601
GenericParamKind::Lifetime { .. } => param.clone(),
609602
GenericParamKind::Type { .. } => {
610-
// I don't think this can be moved out of the loop, since
611-
// a GenericBound requires an ast id
612-
let bounds: Vec<_> =
613-
// extra restrictions on the generics parameters to the
614-
// type being derived upon
615-
self.additional_bounds.iter().map(|p| {
616-
cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))
617-
}).chain(
618-
// require the current trait
619-
self.skip_path_as_bound.not().then(|| cx.trait_bound(trait_path.clone()))
620-
).chain(
621-
// also add in any bounds from the declaration
622-
param.bounds.iter().cloned()
623-
).collect();
603+
// Extra restrictions on the generics parameters to the
604+
// type being derived upon.
605+
let bounds: Vec<_> = self
606+
.additional_bounds
607+
.iter()
608+
.map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)))
609+
.chain(
610+
// Add a bound for the current trait.
611+
self.skip_path_as_bound
612+
.not()
613+
.then(|| cx.trait_bound(trait_path.clone())),
614+
)
615+
.chain({
616+
// Add a `Copy` bound if required.
617+
if is_packed && self.needs_copy_as_bound_if_packed {
618+
let p = deriving::path_std!(marker::Copy);
619+
Some(cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)))
620+
} else {
621+
None
622+
}
623+
})
624+
.chain(
625+
// Also add in any bounds from the declaration.
626+
param.bounds.iter().cloned(),
627+
)
628+
.collect();
624629

625630
cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
626631
}
@@ -692,9 +697,17 @@ impl<'a> TraitDef<'a> {
692697
.map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)))
693698
.collect();
694699

695-
// require the current trait
700+
// Require the current trait.
696701
bounds.push(cx.trait_bound(trait_path.clone()));
697702

703+
// Add a `Copy` bound if required.
704+
if is_packed && self.needs_copy_as_bound_if_packed {
705+
let p = deriving::path_std!(marker::Copy);
706+
bounds.push(
707+
cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)),
708+
);
709+
}
710+
698711
let predicate = ast::WhereBoundPredicate {
699712
span: self.span,
700713
bound_generic_params: field_ty_param.bound_generic_params,
@@ -762,7 +775,7 @@ impl<'a> TraitDef<'a> {
762775
type_ident: Ident,
763776
generics: &Generics,
764777
from_scratch: bool,
765-
copy_fields: bool,
778+
is_packed: bool,
766779
) -> P<ast::Item> {
767780
let field_tys: Vec<P<ast::Ty>> =
768781
struct_def.fields().iter().map(|field| field.ty.clone()).collect();
@@ -790,7 +803,7 @@ impl<'a> TraitDef<'a> {
790803
type_ident,
791804
&selflike_args,
792805
&nonselflike_args,
793-
copy_fields,
806+
is_packed,
794807
)
795808
};
796809

@@ -806,7 +819,7 @@ impl<'a> TraitDef<'a> {
806819
})
807820
.collect();
808821

809-
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
822+
self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed)
810823
}
811824

812825
fn expand_enum_def(
@@ -861,7 +874,8 @@ impl<'a> TraitDef<'a> {
861874
})
862875
.collect();
863876

864-
self.create_derived_impl(cx, type_ident, generics, field_tys, methods)
877+
let is_packed = false; // enums are never packed
878+
self.create_derived_impl(cx, type_ident, generics, field_tys, methods, is_packed)
865879
}
866880
}
867881

@@ -1011,8 +1025,8 @@ impl<'a> MethodDef<'a> {
10111025
/// ```
10121026
/// But if the struct is `repr(packed)`, we can't use something like
10131027
/// `&self.x` because that might cause an unaligned ref. So for any trait
1014-
/// method that takes a reference, if the struct impls `Copy` then we use a
1015-
/// local block to force a copy:
1028+
/// method that takes a reference, we use a local block to force a copy.
1029+
/// This requires that the field impl `Copy`.
10161030
/// ```
10171031
/// # struct A { x: u8, y: u8 }
10181032
/// impl PartialEq for A {
@@ -1027,10 +1041,6 @@ impl<'a> MethodDef<'a> {
10271041
/// ::core::hash::Hash::hash(&{ self.y }, state)
10281042
/// }
10291043
/// }
1030-
/// ```
1031-
/// If the struct doesn't impl `Copy`, we use the normal `&self.x`. This
1032-
/// only works if the fields match the alignment required by the
1033-
/// `packed(N)` attribute. (We'll get errors later on if not.)
10341044
fn expand_struct_method_body<'b>(
10351045
&self,
10361046
cx: &mut ExtCtxt<'_>,
@@ -1039,12 +1049,12 @@ impl<'a> MethodDef<'a> {
10391049
type_ident: Ident,
10401050
selflike_args: &[P<Expr>],
10411051
nonselflike_args: &[P<Expr>],
1042-
copy_fields: bool,
1052+
is_packed: bool,
10431053
) -> BlockOrExpr {
10441054
assert!(selflike_args.len() == 1 || selflike_args.len() == 2);
10451055

10461056
let selflike_fields =
1047-
trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, copy_fields);
1057+
trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, is_packed);
10481058
self.call_substructure_method(
10491059
cx,
10501060
trait_,
@@ -1514,7 +1524,7 @@ impl<'a> TraitDef<'a> {
15141524
cx: &mut ExtCtxt<'_>,
15151525
selflike_args: &[P<Expr>],
15161526
struct_def: &'a VariantData,
1517-
copy_fields: bool,
1527+
is_packed: bool,
15181528
) -> Vec<FieldInfo> {
15191529
self.create_fields(struct_def, |i, struct_field, sp| {
15201530
selflike_args
@@ -1533,10 +1543,39 @@ impl<'a> TraitDef<'a> {
15331543
}),
15341544
),
15351545
);
1536-
if copy_fields {
1537-
field_expr = cx.expr_block(
1538-
cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]),
1539-
);
1546+
// In general, fields in packed structs are copied via a
1547+
// block, e.g. `&{self.0}`. The one exception is `[u8]`
1548+
// fields, which cannot be copied and also never cause
1549+
// unaligned references. This exception is allowed to
1550+
// handle the `FlexZeroSlice` type in the `zerovec` crate
1551+
// within `icu4x-0.9.0`.
1552+
//
1553+
// Once use of `icu4x-0.9.0` has dropped sufficiently, this
1554+
// exception should be removed.
1555+
let is_u8_slice = if let TyKind::Slice(ty) = &struct_field.ty.kind &&
1556+
let TyKind::Path(None, rustc_ast::Path { segments, .. }) = &ty.kind &&
1557+
let [seg] = segments.as_slice() &&
1558+
seg.ident.name == sym::u8 && seg.args.is_none()
1559+
{
1560+
true
1561+
} else {
1562+
false
1563+
};
1564+
if is_packed {
1565+
if is_u8_slice {
1566+
cx.sess.parse_sess.buffer_lint_with_diagnostic(
1567+
BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE,
1568+
sp,
1569+
ast::CRATE_NODE_ID,
1570+
"byte slice in a packed struct that derives a built-in trait",
1571+
rustc_lint_defs::BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive
1572+
);
1573+
} else {
1574+
// Wrap the expression in `{...}`, causing a copy.
1575+
field_expr = cx.expr_block(
1576+
cx.block(struct_field.span, vec![cx.stmt_expr(field_expr)]),
1577+
);
1578+
}
15401579
}
15411580
cx.expr_addr_of(sp, field_expr)
15421581
})

compiler/rustc_builtin_macros/src/deriving/hash.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub fn expand_deriving_hash(
2424
span,
2525
path,
2626
skip_path_as_bound: false,
27+
needs_copy_as_bound_if_packed: true,
2728
additional_bounds: Vec::new(),
2829
supports_unions: false,
2930
methods: vec![MethodDef {

compiler/rustc_lint/src/context.rs

+3
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,9 @@ pub trait LintContext: Sized {
882882
);
883883
}
884884
}
885+
BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => {
886+
db.help("consider implementing the trait by hand, or remove the `packed` attribute");
887+
}
885888
}
886889
// Rewrap `db`, and pass control to the user.
887890
decorate(db)

0 commit comments

Comments
 (0)