Skip to content

Commit d46c728

Browse files
committed
Auto merge of #98446 - nnethercote:derive-no-match-destructuring, r=scottmcm
Don't use match-destructuring for derived ops on structs. r? `@scottmcm`
2 parents 2557603 + ecc6e95 commit d46c728

File tree

6 files changed

+394
-439
lines changed

6 files changed

+394
-439
lines changed

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

+81-46
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
//!
6767
//! # "`cs`" functions
6868
//!
69-
//! The `cs_...` functions ("combine substructure) are designed to
69+
//! The `cs_...` functions ("combine substructure") are designed to
7070
//! make life easier by providing some pre-made recipes for common
7171
//! threads; mostly calling the function being derived on all the
7272
//! arguments and then combining them back together in some way (or
@@ -429,6 +429,7 @@ impl<'a> TraitDef<'a> {
429429
generics,
430430
from_scratch,
431431
use_temporaries,
432+
is_packed,
432433
),
433434
ast::ItemKind::Enum(ref enum_def, ref generics) => {
434435
// We ignore `use_temporaries` here, because
@@ -448,6 +449,7 @@ impl<'a> TraitDef<'a> {
448449
generics,
449450
from_scratch,
450451
use_temporaries,
452+
is_packed,
451453
)
452454
} else {
453455
cx.span_err(mitem.span, "this trait cannot be derived for unions");
@@ -729,6 +731,7 @@ impl<'a> TraitDef<'a> {
729731
generics: &Generics,
730732
from_scratch: bool,
731733
use_temporaries: bool,
734+
is_packed: bool,
732735
) -> P<ast::Item> {
733736
let field_tys: Vec<P<ast::Ty>> =
734737
struct_def.fields().iter().map(|field| field.ty.clone()).collect();
@@ -757,6 +760,7 @@ impl<'a> TraitDef<'a> {
757760
&self_args,
758761
&nonself_args,
759762
use_temporaries,
763+
is_packed,
760764
)
761765
};
762766

@@ -945,6 +949,7 @@ impl<'a> MethodDef<'a> {
945949
})
946950
}
947951

952+
/// The normal case uses field access.
948953
/// ```
949954
/// #[derive(PartialEq)]
950955
/// # struct Dummy;
@@ -953,33 +958,21 @@ impl<'a> MethodDef<'a> {
953958
/// // equivalent to:
954959
/// impl PartialEq for A {
955960
/// fn eq(&self, other: &A) -> bool {
956-
/// match *self {
957-
/// A {x: ref __self_0_0, y: ref __self_0_1} => {
958-
/// match *other {
959-
/// A {x: ref __self_1_0, y: ref __self_1_1} => {
960-
/// __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1)
961-
/// }
962-
/// }
963-
/// }
964-
/// }
961+
/// self.x == other.x && self.y == other.y
965962
/// }
966963
/// }
967964
/// ```
968-
/// or if A is repr(packed) - note fields are matched by-value
969-
/// instead of by-reference.
965+
/// But if the struct is `repr(packed)`, we can't use something like
966+
/// `&self.x` on a packed type (as required for e.g. `Debug` and `Hash`)
967+
/// because that might cause an unaligned ref. So we use let-destructuring
968+
/// instead.
970969
/// ```
971970
/// # struct A { x: i32, y: i32 }
972971
/// impl PartialEq for A {
973972
/// fn eq(&self, other: &A) -> bool {
974-
/// match *self {
975-
/// A {x: __self_0_0, y: __self_0_1} => {
976-
/// match other {
977-
/// A {x: __self_1_0, y: __self_1_1} => {
978-
/// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1)
979-
/// }
980-
/// }
981-
/// }
982-
/// }
973+
/// let Self { x: ref __self_0_0, y: ref __self_0_1 } = *self;
974+
/// let Self { x: ref __self_1_0, y: ref __self_1_1 } = *other;
975+
/// *__self_0_0 == *__self_1_0 && *__self_0_1 == *__self_1_1
983976
/// }
984977
/// }
985978
/// ```
@@ -992,24 +985,33 @@ impl<'a> MethodDef<'a> {
992985
self_args: &[P<Expr>],
993986
nonself_args: &[P<Expr>],
994987
use_temporaries: bool,
988+
is_packed: bool,
995989
) -> P<Expr> {
996990
let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]>
997991
let span = trait_.span;
998992
let mut patterns = Vec::new();
999-
for i in 0..self_args.len() {
1000-
// We could use `type_ident` instead of `Self`, but in the case of a type parameter
1001-
// shadowing the struct name, that causes a second, unnecessary E0578 error. #97343
1002-
let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
1003-
let (pat, ident_expr) = trait_.create_struct_pattern(
1004-
cx,
1005-
struct_path,
1006-
struct_def,
1007-
&format!("__self_{}", i),
1008-
ast::Mutability::Not,
1009-
use_temporaries,
1010-
);
1011-
patterns.push(pat);
1012-
raw_fields.push(ident_expr);
993+
994+
for (i, self_arg) in self_args.iter().enumerate() {
995+
let ident_exprs = if !is_packed {
996+
trait_.create_struct_field_accesses(cx, self_arg, struct_def)
997+
} else {
998+
// Get the pattern for the let-destructuring.
999+
//
1000+
// We could use `type_ident` instead of `Self`, but in the case of a type parameter
1001+
// shadowing the struct name, that causes a second, unnecessary E0578 error. #97343
1002+
let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
1003+
let (pat, ident_exprs) = trait_.create_struct_pattern(
1004+
cx,
1005+
struct_path,
1006+
struct_def,
1007+
&format!("__self_{}", i),
1008+
ast::Mutability::Not,
1009+
use_temporaries,
1010+
);
1011+
patterns.push(pat);
1012+
ident_exprs
1013+
};
1014+
raw_fields.push(ident_exprs);
10131015
}
10141016

10151017
// transpose raw_fields
@@ -1036,7 +1038,6 @@ impl<'a> MethodDef<'a> {
10361038
cx.span_bug(span, "no `self` parameter for method in generic `derive`")
10371039
};
10381040

1039-
// body of the inner most destructuring match
10401041
let mut body = self.call_substructure_method(
10411042
cx,
10421043
trait_,
@@ -1045,14 +1046,18 @@ impl<'a> MethodDef<'a> {
10451046
&Struct(struct_def, fields),
10461047
);
10471048

1048-
// make a series of nested matches, to destructure the
1049-
// structs. This is actually right-to-left, but it shouldn't
1050-
// matter.
1051-
for (arg_expr, pat) in iter::zip(self_args, patterns) {
1052-
body = cx.expr_match(span, arg_expr.clone(), vec![cx.arm(span, pat.clone(), body)])
1053-
}
1049+
if !is_packed {
1050+
body.span = span;
1051+
body
1052+
} else {
1053+
// Do the let-destructuring.
1054+
let mut stmts: Vec<_> = iter::zip(self_args, patterns)
1055+
.map(|(arg_expr, pat)| cx.stmt_let_pat(span, pat, arg_expr.clone()))
1056+
.collect();
1057+
stmts.push(cx.stmt_expr(body));
10541058

1055-
body
1059+
cx.expr_block(cx.block(span, stmts))
1060+
}
10561061
}
10571062

10581063
fn expand_static_struct_method_body(
@@ -1522,8 +1527,6 @@ impl<'a> TraitDef<'a> {
15221527
paths.push(ident.with_span_pos(sp));
15231528
let val = cx.expr_path(cx.path_ident(sp, ident));
15241529
let val = if use_temporaries { val } else { cx.expr_deref(sp, val) };
1525-
let val = cx.expr(sp, ast::ExprKind::Paren(val));
1526-
15271530
ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
15281531
}
15291532

@@ -1555,6 +1558,39 @@ impl<'a> TraitDef<'a> {
15551558
(pattern, ident_exprs)
15561559
}
15571560

1561+
fn create_struct_field_accesses(
1562+
&self,
1563+
cx: &mut ExtCtxt<'_>,
1564+
mut self_arg: &P<Expr>,
1565+
struct_def: &'a VariantData,
1566+
) -> Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])> {
1567+
let mut ident_exprs = Vec::new();
1568+
for (i, struct_field) in struct_def.fields().iter().enumerate() {
1569+
let sp = struct_field.span.with_ctxt(self.span.ctxt());
1570+
1571+
// We don't the need the deref, if there is one.
1572+
if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &self_arg.kind {
1573+
self_arg = inner;
1574+
}
1575+
1576+
// Note: we must use `struct_field.span` rather than `span` in the
1577+
// `unwrap_or_else` case otherwise the hygiene is wrong and we get
1578+
// "field `0` of struct `Point` is private" errors on tuple
1579+
// structs.
1580+
let val = cx.expr(
1581+
sp,
1582+
ast::ExprKind::Field(
1583+
self_arg.clone(),
1584+
struct_field.ident.unwrap_or_else(|| {
1585+
Ident::from_str_and_span(&i.to_string(), struct_field.span)
1586+
}),
1587+
),
1588+
);
1589+
ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
1590+
}
1591+
ident_exprs
1592+
}
1593+
15581594
fn create_enum_variant_pattern(
15591595
&self,
15601596
cx: &mut ExtCtxt<'_>,
@@ -1643,7 +1679,6 @@ where
16431679
/// fields.
16441680
/// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f`
16451681
/// is returned. Statics may not be folded over.
1646-
/// See `cs_op` in `partial_ord.rs` for a model example.
16471682
pub fn cs_fold1<F, B>(
16481683
use_foldl: bool,
16491684
f: F,

compiler/rustc_expand/src/build.rs

+13
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,19 @@ impl<'a> ExtCtxt<'a> {
152152
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
153153
}
154154

155+
pub fn stmt_let_pat(&self, sp: Span, pat: P<ast::Pat>, ex: P<ast::Expr>) -> ast::Stmt {
156+
let local = P(ast::Local {
157+
pat,
158+
ty: None,
159+
id: ast::DUMMY_NODE_ID,
160+
kind: LocalKind::Init(ex),
161+
span: sp,
162+
attrs: AttrVec::new(),
163+
tokens: None,
164+
});
165+
self.stmt_local(local, sp)
166+
}
167+
155168
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
156169
self.stmt_let_ty(sp, mutbl, ident, None, ex)
157170
}

0 commit comments

Comments
 (0)