Skip to content

Commit e44f742

Browse files
Auto merge of #149253 - saethlin:can-compare-bitwise, r=<try>
derive(PartialEq) via bitwise comparison where possible
2 parents f40a70d + 6c5a721 commit e44f742

File tree

18 files changed

+473
-33
lines changed

18 files changed

+473
-33
lines changed

compiler/rustc_abi/src/layout/ty.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,29 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
191191
Ty::ty_and_layout_field(self, cx, i)
192192
}
193193

194+
pub fn is_pod_layout<C>(self, cx: &C) -> bool
195+
where
196+
Ty: TyAbiInterface<'a, C> + Copy,
197+
{
198+
let Variants::Single { .. } = self.variants else {
199+
return false;
200+
};
201+
202+
let mut expected_offset = Size::ZERO;
203+
for i in self.fields.index_by_increasing_offset() {
204+
if self.fields.offset(i) != expected_offset {
205+
return false;
206+
}
207+
expected_offset = self.fields.offset(i) + TyAndLayout::field(self, cx, i).size;
208+
}
209+
210+
if expected_offset != self.size {
211+
return false;
212+
}
213+
214+
true
215+
}
216+
194217
pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
195218
where
196219
Ty: TyAbiInterface<'a, C>,

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

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability, Safety};
1+
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, MetaItem, Mutability, Safety};
22
use rustc_expand::base::{Annotatable, ExtCtxt};
3-
use rustc_span::{Span, sym};
3+
use rustc_span::{Ident, Span, kw, sym};
44
use thin_vec::thin_vec;
55

66
use crate::deriving::generic::ty::*;
@@ -125,7 +125,7 @@ fn get_substructure_equality_expr(
125125
) -> Box<Expr> {
126126
use SubstructureFields::*;
127127

128-
match substructure.fields {
128+
let field_comparison = match substructure.fields {
129129
EnumMatching(.., fields) | Struct(.., fields) => {
130130
let combine = move |acc, field| {
131131
let rhs = get_field_equality_expr(cx, field);
@@ -138,23 +138,35 @@ fn get_substructure_equality_expr(
138138
Some(rhs)
139139
};
140140

141-
// First compare scalar fields, then compound fields, combining all
142-
// with logical AND.
143-
return fields
144-
.iter()
145-
.filter(|field| !field.maybe_scalar)
146-
.fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine)
147-
// If there are no fields, treat as always equal.
148-
.unwrap_or_else(|| cx.expr_bool(span, true));
141+
// If there are no fields, return true immediately.
142+
// If there is just one, compare it.
143+
// Otherwise, try to do a bitwise comparison.
144+
match &fields[..] {
145+
[] => return cx.expr_bool(span, true),
146+
[field] => return get_field_equality_expr(cx, field),
147+
_ => {
148+
// First compare scalar fields, then compound fields, combining all
149+
// with logical AND.
150+
fields
151+
.iter()
152+
.filter(|field| !field.maybe_scalar)
153+
.fold(
154+
fields.iter().filter(|field| field.maybe_scalar).fold(None, combine),
155+
combine,
156+
)
157+
.unwrap()
158+
}
159+
}
149160
}
150161
EnumDiscr(disc, match_expr) => {
151162
let lhs = get_field_equality_expr(cx, disc);
152-
let Some(match_expr) = match_expr else {
153-
return lhs;
154-
};
155-
// Compare the discriminant first (cheaper), then the rest of the
156-
// fields.
157-
return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone());
163+
if let Some(match_expr) = match_expr {
164+
// Compare the discriminant first (cheaper), then the rest of the
165+
// fields.
166+
cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone())
167+
} else {
168+
lhs
169+
}
158170
}
159171
StaticEnum(..) => cx.dcx().span_bug(
160172
span,
@@ -168,6 +180,31 @@ fn get_substructure_equality_expr(
168180
span,
169181
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
170182
),
183+
};
184+
185+
if matches!(substructure.fields, Struct(..)) {
186+
// Construct intrinsics::can_compare_bitwise<Self>()
187+
let self_ty = cx.ty_path(cx.path_ident(span, Ident::with_dummy_span(kw::SelfUpper)));
188+
let path = cx.expr_path(cx.path_all(
189+
span,
190+
true,
191+
cx.std_path(&[sym::intrinsics, sym::can_compare_bitwise]),
192+
vec![GenericArg::Type(self_ty)],
193+
));
194+
let cond = cx.expr_call(span, path, thin_vec![]);
195+
196+
let [_self, rhs] = &substructure.selflike_args[..] else {
197+
cx.dcx().span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`");
198+
};
199+
200+
// Construct intrinsics::compare_bitwise(self, other)
201+
let compare_bitwise = cx.std_path(&[sym::intrinsics, sym::compare_bitwise]);
202+
let call_compare_bitwise =
203+
cx.expr_call_global(span, compare_bitwise, thin_vec![cx.expr_self(span), rhs.clone()]);
204+
205+
cx.expr_if(span, cond, call_compare_bitwise, Some(field_comparison))
206+
} else {
207+
field_comparison
171208
}
172209
}
173210

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ pub(crate) struct Substructure<'a> {
278278
/// Verbatim access to any non-selflike arguments, i.e. arguments that
279279
/// don't have type `&Self`.
280280
pub nonselflike_args: &'a [Box<Expr>],
281+
pub selflike_args: &'a [Box<Expr>],
281282
pub fields: &'a SubstructureFields<'a>,
282283
}
283284

@@ -879,6 +880,7 @@ impl<'a> TraitDef<'a> {
879880
self,
880881
struct_def,
881882
type_ident,
883+
&selflike_args,
882884
&nonselflike_args,
883885
)
884886
} else {
@@ -935,6 +937,7 @@ impl<'a> TraitDef<'a> {
935937
self,
936938
enum_def,
937939
type_ident,
940+
&selflike_args,
938941
&nonselflike_args,
939942
)
940943
} else {
@@ -971,11 +974,12 @@ impl<'a> MethodDef<'a> {
971974
cx: &ExtCtxt<'_>,
972975
trait_: &TraitDef<'_>,
973976
type_ident: Ident,
977+
selflike_args: &[Box<Expr>],
974978
nonselflike_args: &[Box<Expr>],
975979
fields: &SubstructureFields<'_>,
976980
) -> BlockOrExpr {
977981
let span = trait_.span;
978-
let substructure = Substructure { type_ident, nonselflike_args, fields };
982+
let substructure = Substructure { type_ident, selflike_args, nonselflike_args, fields };
979983
let mut f = self.combine_substructure.borrow_mut();
980984
let f: &mut CombineSubstructureFunc<'_> = &mut *f;
981985
f(cx, span, &substructure)
@@ -1150,6 +1154,7 @@ impl<'a> MethodDef<'a> {
11501154
cx,
11511155
trait_,
11521156
type_ident,
1157+
selflike_args,
11531158
nonselflike_args,
11541159
&Struct(struct_def, selflike_fields),
11551160
)
@@ -1161,6 +1166,7 @@ impl<'a> MethodDef<'a> {
11611166
trait_: &TraitDef<'_>,
11621167
struct_def: &VariantData,
11631168
type_ident: Ident,
1169+
selflike_args: &[Box<Expr>],
11641170
nonselflike_args: &[Box<Expr>],
11651171
) -> BlockOrExpr {
11661172
let summary = trait_.summarise_struct(cx, struct_def);
@@ -1169,6 +1175,7 @@ impl<'a> MethodDef<'a> {
11691175
cx,
11701176
trait_,
11711177
type_ident,
1178+
selflike_args,
11721179
nonselflike_args,
11731180
&StaticStruct(struct_def, summary),
11741181
)
@@ -1305,6 +1312,7 @@ impl<'a> MethodDef<'a> {
13051312
cx,
13061313
trait_,
13071314
type_ident,
1315+
&selflike_args,
13081316
nonselflike_args,
13091317
&EnumDiscr(discr_field, None),
13101318
);
@@ -1316,6 +1324,7 @@ impl<'a> MethodDef<'a> {
13161324
cx,
13171325
trait_,
13181326
type_ident,
1327+
&selflike_args,
13191328
nonselflike_args,
13201329
&AllFieldlessEnum(enum_def),
13211330
);
@@ -1329,6 +1338,7 @@ impl<'a> MethodDef<'a> {
13291338
cx,
13301339
trait_,
13311340
type_ident,
1341+
&selflike_args,
13321342
nonselflike_args,
13331343
&EnumMatching(variant, Vec::new()),
13341344
);
@@ -1381,6 +1391,7 @@ impl<'a> MethodDef<'a> {
13811391
cx,
13821392
trait_,
13831393
type_ident,
1394+
&selflike_args,
13841395
nonselflike_args,
13851396
&substructure,
13861397
)
@@ -1402,6 +1413,7 @@ impl<'a> MethodDef<'a> {
14021413
cx,
14031414
trait_,
14041415
type_ident,
1416+
&selflike_args,
14051417
nonselflike_args,
14061418
&EnumMatching(v, Vec::new()),
14071419
)
@@ -1448,6 +1460,7 @@ impl<'a> MethodDef<'a> {
14481460
cx,
14491461
trait_,
14501462
type_ident,
1463+
&selflike_args.clone(),
14511464
nonselflike_args,
14521465
&EnumDiscr(discr_field, Some(get_match_expr(selflike_args))),
14531466
);
@@ -1464,12 +1477,14 @@ impl<'a> MethodDef<'a> {
14641477
trait_: &TraitDef<'_>,
14651478
enum_def: &EnumDef,
14661479
type_ident: Ident,
1480+
selflike_args: &[Box<Expr>],
14671481
nonselflike_args: &[Box<Expr>],
14681482
) -> BlockOrExpr {
14691483
self.call_substructure_method(
14701484
cx,
14711485
trait_,
14721486
type_ident,
1487+
selflike_args,
14731488
nonselflike_args,
14741489
&StaticEnum(enum_def),
14751490
)

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,14 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
8080
| sym::breakpoint
8181
| sym::bswap
8282
| sym::caller_location
83+
| sym::can_compare_bitwise
8384
| sym::carrying_mul_add
8485
| sym::ceilf16
8586
| sym::ceilf32
8687
| sym::ceilf64
8788
| sym::ceilf128
8889
| sym::cold_path
90+
| sym::compare_bitwise
8991
| sym::const_eval_select
9092
| sym::contract_check_ensures
9193
| sym::contract_check_requires
@@ -342,6 +344,18 @@ pub(crate) fn check_intrinsic_type(
342344
vec![Ty::new_mut_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize],
343345
tcx.types.unit,
344346
),
347+
sym::compare_bitwise => {
348+
let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon };
349+
let first_arg =
350+
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
351+
let br =
352+
ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon };
353+
let second_arg =
354+
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
355+
356+
(1, 0, vec![first_arg, second_arg], tcx.types.bool)
357+
}
358+
sym::can_compare_bitwise => (1, 0, vec![], tcx.types.bool),
345359
sym::compare_bytes => {
346360
let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8);
347361
(0, 0, vec![byte_ptr, byte_ptr, tcx.types.usize], tcx.types.i32)

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,6 +1987,14 @@ impl<'tcx> Ty<'tcx> {
19871987
}
19881988
}
19891989

1990+
pub fn is_pod(self, tcx: TyCtxt<'tcx>) -> bool {
1991+
match self.kind() {
1992+
ty::Int(..) | ty::Uint(..) | ty::Bool | ty::Char => true,
1993+
ty::Array(element_ty, _len) => element_ty.is_pod(tcx),
1994+
_ => false,
1995+
}
1996+
}
1997+
19901998
pub fn is_trivially_wf(self, tcx: TyCtxt<'tcx>) -> bool {
19911999
match *self.kind() {
19922000
ty::Bool

compiler/rustc_mir_transform/src/known_panics_lint.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
255255
return None;
256256
}
257257

258+
// Don't try to evaluate the Operand::Const of calls to a concrete fn
259+
if matches!(c.ty().kind(), ty::FnDef(..)) {
260+
return None;
261+
}
262+
258263
// Normalization needed b/c known panics lint runs in
259264
// `mir_drops_elaborated_and_const_checked`, which happens before
260265
// optimized MIR. Only after optimizing the MIR can we guarantee

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub(super) struct LowerIntrinsics;
1212
impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
1313
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
1414
let local_decls = &body.local_decls;
15+
let typing_env = body.typing_env(tcx);
1516
for block in body.basic_blocks.as_mut() {
1617
let terminator = block.terminator.as_mut().unwrap();
1718
if let TerminatorKind::Call { func, args, destination, target, .. } =
@@ -20,6 +21,44 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
2021
&& let Some(intrinsic) = tcx.intrinsic(def_id)
2122
{
2223
match intrinsic.name {
24+
sym::can_compare_bitwise => {
25+
let mut can_compare = false;
26+
27+
// The type that we want to check is the generic arg of can_compare_bitwise
28+
let arg = generic_args[0].as_type().unwrap();
29+
30+
// can_compare_bitwise is only used when T is a struct, and our task is to
31+
// check if all the fields of the struct are POD and if there is no padding
32+
// between them.
33+
let ty::Adt(adt_def, args) = arg.kind() else { unreachable!() };
34+
assert!(adt_def.is_struct());
35+
36+
// Check if all the struct fields are POD
37+
let is_pod =
38+
adt_def.all_fields().all(|field| field.ty(tcx, args).is_pod(tcx));
39+
40+
// Only query the layout if all the struct fields are POD. Then use the
41+
// layout to check if there is any padding between them.
42+
if is_pod && let Ok(layout) = tcx.layout_of(typing_env.as_query_input(arg))
43+
{
44+
can_compare = layout.is_pod_layout(
45+
&rustc_middle::ty::layout::LayoutCx::new(tcx, typing_env),
46+
)
47+
}
48+
49+
block.statements.push(Statement::new(
50+
terminator.source_info,
51+
StatementKind::Assign(Box::new((
52+
*destination,
53+
Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
54+
span: terminator.source_info.span,
55+
user_ty: None,
56+
const_: Const::from_bool(tcx, can_compare),
57+
}))),
58+
))),
59+
));
60+
terminator.kind = TerminatorKind::Goto { target: target.unwrap() };
61+
}
2362
sym::unreachable => {
2463
terminator.kind = TerminatorKind::Unreachable;
2564
}

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ symbols! {
622622
call_ref_future,
623623
caller,
624624
caller_location,
625+
can_compare_bitwise,
625626
capture_disjoint_fields,
626627
carrying_mul_add,
627628
catch_unwind,
@@ -699,6 +700,7 @@ symbols! {
699700
collapse_debuginfo,
700701
column,
701702
common,
703+
compare_bitwise,
702704
compare_bytes,
703705
compare_exchange,
704706
compare_exchange_weak,

0 commit comments

Comments
 (0)