Skip to content

Commit 3c3569a

Browse files
Auto merge of #149253 - saethlin:can-compare-bitwise, r=<try>
derive(PartialEq) via bitwise comparison where possible
2 parents ba86c04 + f67692a commit 3c3569a

File tree

20 files changed

+618
-33
lines changed

20 files changed

+618
-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: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability, Safety};
1+
use rustc_ast::{
2+
BinOpKind, BlockCheckMode, BorrowKind, Expr, ExprKind, GenericArg, MetaItem, MutTy, Mutability,
3+
Safety, TyKind, UnsafeSource,
4+
};
25
use rustc_expand::base::{Annotatable, ExtCtxt};
3-
use rustc_span::{Span, sym};
6+
use rustc_span::{Ident, Span, kw, sym};
47
use thin_vec::thin_vec;
58

69
use crate::deriving::generic::ty::*;
@@ -125,7 +128,7 @@ fn get_substructure_equality_expr(
125128
) -> Box<Expr> {
126129
use SubstructureFields::*;
127130

128-
match substructure.fields {
131+
let field_comparison = match substructure.fields {
129132
EnumMatching(.., fields) | Struct(.., fields) => {
130133
let combine = move |acc, field| {
131134
let rhs = get_field_equality_expr(cx, field);
@@ -138,23 +141,35 @@ fn get_substructure_equality_expr(
138141
Some(rhs)
139142
};
140143

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));
144+
// If there are no fields, return true immediately.
145+
// If there is just one, compare it.
146+
// Otherwise, try to do a bitwise comparison.
147+
match &fields[..] {
148+
[] => return cx.expr_bool(span, true),
149+
[field] => return get_field_equality_expr(cx, field),
150+
_ => {
151+
// First compare scalar fields, then compound fields, combining all
152+
// with logical AND.
153+
fields
154+
.iter()
155+
.filter(|field| !field.maybe_scalar)
156+
.fold(
157+
fields.iter().filter(|field| field.maybe_scalar).fold(None, combine),
158+
combine,
159+
)
160+
.unwrap()
161+
}
162+
}
149163
}
150164
EnumDiscr(disc, match_expr) => {
151165
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());
166+
if let Some(match_expr) = match_expr {
167+
// Compare the discriminant first (cheaper), then the rest of the
168+
// fields.
169+
cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone())
170+
} else {
171+
lhs
172+
}
158173
}
159174
StaticEnum(..) => cx.dcx().span_bug(
160175
span,
@@ -168,6 +183,55 @@ fn get_substructure_equality_expr(
168183
span,
169184
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
170185
),
186+
};
187+
188+
if matches!(substructure.fields, Struct(..)) {
189+
// Construct intrinsics::can_compare_bitwise<Self>()
190+
let self_ty = cx.ty_path(cx.path_ident(span, Ident::with_dummy_span(kw::SelfUpper)));
191+
let path = cx.expr_path(cx.path_all(
192+
span,
193+
true,
194+
cx.std_path(&[sym::intrinsics, sym::can_compare_bitwise]),
195+
vec![GenericArg::Type(self_ty.clone())],
196+
));
197+
let cond = cx.expr_call(span, path, thin_vec![]);
198+
199+
let [_self, rhs] = &substructure.selflike_args[..] else {
200+
cx.dcx().span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`");
201+
};
202+
203+
// Construct intrinsics::compare_bitwise(
204+
// self as *const Self as *const u8,
205+
// other as *const Self as *const u8,
206+
//
207+
// );
208+
let self_ptr_ty = cx.ty(span, TyKind::Ptr(MutTy { ty: self_ty, mutbl: Mutability::Not }));
209+
let u8_ty = cx.ty_ident(span, Ident::with_dummy_span(sym::u8));
210+
let u8_ptr_ty = cx.ty(span, TyKind::Ptr(MutTy { ty: u8_ty, mutbl: Mutability::Not }));
211+
212+
let self_ptr = cx.expr_cast(span, cx.expr_self(span), self_ptr_ty.clone());
213+
let self_ptr_u8 = cx.expr_cast(span, self_ptr, u8_ptr_ty.clone());
214+
215+
let other_ptr = cx.expr_cast(span, rhs.clone(), self_ptr_ty);
216+
let other_ptr_u8 = cx.expr_cast(span, other_ptr, u8_ptr_ty);
217+
218+
let size_of_val = cx.std_path(&[sym::intrinsics, sym::size_of_val]);
219+
let size = cx.expr_call_global(span, size_of_val, thin_vec![cx.expr_self(span)]);
220+
221+
let compare_bytes = cx.std_path(&[sym::intrinsics, sym::compare_bytes]);
222+
223+
let call_compare_bytes =
224+
cx.expr_call_global(span, compare_bytes, thin_vec![self_ptr_u8, other_ptr_u8, size]);
225+
226+
let mut call = cx.block_expr(call_compare_bytes);
227+
call.rules = BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated);
228+
let call = cx.expr_block(call);
229+
230+
let is_eq = cx.expr_binary(span, BinOpKind::Eq, call, cx.expr_i32(span, 0));
231+
232+
cx.expr_if(span, cond, is_eq, Some(field_comparison))
233+
} else {
234+
field_comparison
171235
}
172236
}
173237

‎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_expand/src/build.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ impl<'a> ExtCtxt<'a> {
394394
let pathexpr = self.expr_path(self.path_global(sp, fn_path));
395395
self.expr_call(sp, pathexpr, args)
396396
}
397+
398+
pub fn expr_cast(&self, span: Span, src: Box<Expr>, ty: Box<ast::Ty>) -> Box<Expr> {
399+
self.expr(span, ast::ExprKind::Cast(src, ty))
400+
}
401+
397402
pub fn expr_block(&self, b: Box<ast::Block>) -> Box<ast::Expr> {
398403
self.expr(b.span, ast::ExprKind::Block(b, None))
399404
}
@@ -445,6 +450,12 @@ impl<'a> ExtCtxt<'a> {
445450
self.expr(span, ast::ExprKind::Lit(lit))
446451
}
447452

453+
pub fn expr_i32(&self, span: Span, n: u32) -> Box<ast::Expr> {
454+
let suffix = Some(ast::IntTy::I32.name());
455+
let lit = token::Lit::new(token::Integer, sym::integer(n), suffix);
456+
self.expr(span, ast::ExprKind::Lit(lit))
457+
}
458+
448459
pub fn expr_bool(&self, span: Span, value: bool) -> Box<ast::Expr> {
449460
let lit = token::Lit::new(token::Bool, if value { kw::True } else { kw::False }, None);
450461
self.expr(span, ast::ExprKind::Lit(lit))

‎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

0 commit comments

Comments
 (0)