Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,29 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
Ty::ty_and_layout_field(self, cx, i)
}

pub fn is_pod_layout<C>(self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
let Variants::Single { .. } = self.variants else {
return false;
};

let mut expected_offset = Size::ZERO;
for i in self.fields.index_by_increasing_offset() {
if self.fields.offset(i) != expected_offset {
return false;
}
expected_offset = self.fields.offset(i) + TyAndLayout::field(self, cx, i).size;
}

if expected_offset != self.size {
return false;
}

true
}

pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo>
where
Ty: TyAbiInterface<'a, C>,
Expand Down
71 changes: 54 additions & 17 deletions compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability, Safety};
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, MetaItem, Mutability, Safety};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::{Span, sym};
use rustc_span::{Ident, Span, kw, sym};
use thin_vec::thin_vec;

use crate::deriving::generic::ty::*;
Expand Down Expand Up @@ -125,7 +125,7 @@ fn get_substructure_equality_expr(
) -> Box<Expr> {
use SubstructureFields::*;

match substructure.fields {
let field_comparison = match substructure.fields {
EnumMatching(.., fields) | Struct(.., fields) => {
let combine = move |acc, field| {
let rhs = get_field_equality_expr(cx, field);
Expand All @@ -138,23 +138,35 @@ fn get_substructure_equality_expr(
Some(rhs)
};

// First compare scalar fields, then compound fields, combining all
// with logical AND.
return fields
.iter()
.filter(|field| !field.maybe_scalar)
.fold(fields.iter().filter(|field| field.maybe_scalar).fold(None, combine), combine)
// If there are no fields, treat as always equal.
.unwrap_or_else(|| cx.expr_bool(span, true));
// If there are no fields, return true immediately.
// If there is just one, compare it.
// Otherwise, try to do a bitwise comparison.
match &fields[..] {
[] => return cx.expr_bool(span, true),
[field] => return get_field_equality_expr(cx, field),
_ => {
// First compare scalar fields, then compound fields, combining all
// with logical AND.
fields
.iter()
.filter(|field| !field.maybe_scalar)
.fold(
fields.iter().filter(|field| field.maybe_scalar).fold(None, combine),
combine,
)
.unwrap()
}
}
}
EnumDiscr(disc, match_expr) => {
let lhs = get_field_equality_expr(cx, disc);
let Some(match_expr) = match_expr else {
return lhs;
};
// Compare the discriminant first (cheaper), then the rest of the
// fields.
return cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone());
if let Some(match_expr) = match_expr {
// Compare the discriminant first (cheaper), then the rest of the
// fields.
cx.expr_binary(disc.span, BinOpKind::And, lhs, match_expr.clone())
} else {
lhs
}
}
StaticEnum(..) => cx.dcx().span_bug(
span,
Expand All @@ -168,6 +180,31 @@ fn get_substructure_equality_expr(
span,
"unexpected all-fieldless enum encountered during `derive(PartialEq)` expansion",
),
};

if matches!(substructure.fields, Struct(..)) {
// Construct intrinsics::can_compare_bitwise<Self>()
let self_ty = cx.ty_path(cx.path_ident(span, Ident::with_dummy_span(kw::SelfUpper)));
let path = cx.expr_path(cx.path_all(
span,
true,
cx.std_path(&[sym::intrinsics, sym::can_compare_bitwise]),
vec![GenericArg::Type(self_ty)],
));
let cond = cx.expr_call(span, path, thin_vec![]);

let [_self, rhs] = &substructure.selflike_args[..] else {
cx.dcx().span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`");
};

// Construct intrinsics::compare_bitwise(self, other)
let compare_bitwise = cx.std_path(&[sym::intrinsics, sym::compare_bitwise]);
let call_compare_bitwise =
cx.expr_call_global(span, compare_bitwise, thin_vec![cx.expr_self(span), rhs.clone()]);

cx.expr_if(span, cond, call_compare_bitwise, Some(field_comparison))
} else {
field_comparison
}
}

Expand Down
17 changes: 16 additions & 1 deletion compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ pub(crate) struct Substructure<'a> {
/// Verbatim access to any non-selflike arguments, i.e. arguments that
/// don't have type `&Self`.
pub nonselflike_args: &'a [Box<Expr>],
pub selflike_args: &'a [Box<Expr>],
pub fields: &'a SubstructureFields<'a>,
}

Expand Down Expand Up @@ -879,6 +880,7 @@ impl<'a> TraitDef<'a> {
self,
struct_def,
type_ident,
&selflike_args,
&nonselflike_args,
)
} else {
Expand Down Expand Up @@ -935,6 +937,7 @@ impl<'a> TraitDef<'a> {
self,
enum_def,
type_ident,
&selflike_args,
&nonselflike_args,
)
} else {
Expand Down Expand Up @@ -971,11 +974,12 @@ impl<'a> MethodDef<'a> {
cx: &ExtCtxt<'_>,
trait_: &TraitDef<'_>,
type_ident: Ident,
selflike_args: &[Box<Expr>],
nonselflike_args: &[Box<Expr>],
fields: &SubstructureFields<'_>,
) -> BlockOrExpr {
let span = trait_.span;
let substructure = Substructure { type_ident, nonselflike_args, fields };
let substructure = Substructure { type_ident, selflike_args, nonselflike_args, fields };
let mut f = self.combine_substructure.borrow_mut();
let f: &mut CombineSubstructureFunc<'_> = &mut *f;
f(cx, span, &substructure)
Expand Down Expand Up @@ -1150,6 +1154,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
selflike_args,
nonselflike_args,
&Struct(struct_def, selflike_fields),
)
Expand All @@ -1161,6 +1166,7 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef<'_>,
struct_def: &VariantData,
type_ident: Ident,
selflike_args: &[Box<Expr>],
nonselflike_args: &[Box<Expr>],
) -> BlockOrExpr {
let summary = trait_.summarise_struct(cx, struct_def);
Expand All @@ -1169,6 +1175,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
selflike_args,
nonselflike_args,
&StaticStruct(struct_def, summary),
)
Expand Down Expand Up @@ -1305,6 +1312,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
&selflike_args,
nonselflike_args,
&EnumDiscr(discr_field, None),
);
Expand All @@ -1316,6 +1324,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
&selflike_args,
nonselflike_args,
&AllFieldlessEnum(enum_def),
);
Expand All @@ -1329,6 +1338,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
&selflike_args,
nonselflike_args,
&EnumMatching(variant, Vec::new()),
);
Expand Down Expand Up @@ -1381,6 +1391,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
&selflike_args,
nonselflike_args,
&substructure,
)
Expand All @@ -1402,6 +1413,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
&selflike_args,
nonselflike_args,
&EnumMatching(v, Vec::new()),
)
Expand Down Expand Up @@ -1448,6 +1460,7 @@ impl<'a> MethodDef<'a> {
cx,
trait_,
type_ident,
&selflike_args.clone(),
nonselflike_args,
&EnumDiscr(discr_field, Some(get_match_expr(selflike_args))),
);
Expand All @@ -1464,12 +1477,14 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef<'_>,
enum_def: &EnumDef,
type_ident: Ident,
selflike_args: &[Box<Expr>],
nonselflike_args: &[Box<Expr>],
) -> BlockOrExpr {
self.call_substructure_method(
cx,
trait_,
type_ident,
selflike_args,
nonselflike_args,
&StaticEnum(enum_def),
)
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::breakpoint
| sym::bswap
| sym::caller_location
| sym::can_compare_bitwise
| sym::carrying_mul_add
| sym::ceilf16
| sym::ceilf32
| sym::ceilf64
| sym::ceilf128
| sym::cold_path
| sym::compare_bitwise
| sym::const_eval_select
| sym::contract_check_ensures
| sym::contract_check_requires
Expand Down Expand Up @@ -340,6 +342,18 @@ pub(crate) fn check_intrinsic_type(
vec![Ty::new_mut_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize],
tcx.types.unit,
),
sym::compare_bitwise => {
let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon };
let first_arg =
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
let br =
ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon };
let second_arg =
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));

(1, 0, vec![first_arg, second_arg], tcx.types.bool)
}
sym::can_compare_bitwise => (1, 0, vec![], tcx.types.bool),
sym::compare_bytes => {
let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8);
(0, 0, vec![byte_ptr, byte_ptr, tcx.types.usize], tcx.types.i32)
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,14 @@ impl<'tcx> Ty<'tcx> {
}
}

pub fn is_pod(self, tcx: TyCtxt<'tcx>) -> bool {
match self.kind() {
ty::Int(..) | ty::Uint(..) | ty::Bool | ty::Char => true,
ty::Array(element_ty, _len) => element_ty.is_pod(tcx),
_ => false,
}
}

pub fn is_trivially_wf(self, tcx: TyCtxt<'tcx>) -> bool {
match *self.kind() {
ty::Bool
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_mir_transform/src/known_panics_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}

// Don't try to evaluate the Operand::Const of calls to a concrete fn
if matches!(c.ty().kind(), ty::FnDef(..)) {
return None;
}

// Normalization needed b/c known panics lint runs in
// `mir_drops_elaborated_and_const_checked`, which happens before
// optimized MIR. Only after optimizing the MIR can we guarantee
Expand Down
39 changes: 39 additions & 0 deletions compiler/rustc_mir_transform/src/lower_intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub(super) struct LowerIntrinsics;
impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let local_decls = &body.local_decls;
let typing_env = body.typing_env(tcx);
for block in body.basic_blocks.as_mut() {
let terminator = block.terminator.as_mut().unwrap();
if let TerminatorKind::Call { func, args, destination, target, .. } =
Expand All @@ -20,6 +21,44 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
&& let Some(intrinsic) = tcx.intrinsic(def_id)
{
match intrinsic.name {
sym::can_compare_bitwise => {
let mut can_compare = false;

// The type that we want to check is the generic arg of can_compare_bitwise
let arg = generic_args[0].as_type().unwrap();

// can_compare_bitwise is only used when T is a struct, and our task is to
// check if all the fields of the struct are POD and if there is no padding
// between them.
let ty::Adt(adt_def, args) = arg.kind() else { unreachable!() };
assert!(adt_def.is_struct());

// Check if all the struct fields are POD
let is_pod =
adt_def.all_fields().all(|field| field.ty(tcx, args).is_pod(tcx));

// Only query the layout if all the struct fields are POD. Then use the
// layout to check if there is any padding between them.
if is_pod && let Ok(layout) = tcx.layout_of(typing_env.as_query_input(arg))
{
can_compare = layout.is_pod_layout(
&rustc_middle::ty::layout::LayoutCx::new(tcx, typing_env),
)
}

block.statements.push(Statement::new(
terminator.source_info,
StatementKind::Assign(Box::new((
*destination,
Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
span: terminator.source_info.span,
user_ty: None,
const_: Const::from_bool(tcx, can_compare),
}))),
))),
));
terminator.kind = TerminatorKind::Goto { target: target.unwrap() };
}
sym::unreachable => {
terminator.kind = TerminatorKind::Unreachable;
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ symbols! {
call_ref_future,
caller,
caller_location,
can_compare_bitwise,
capture_disjoint_fields,
carrying_mul_add,
catch_unwind,
Expand Down Expand Up @@ -699,6 +700,7 @@ symbols! {
collapse_debuginfo,
column,
common,
compare_bitwise,
compare_bytes,
compare_exchange,
compare_exchange_weak,
Expand Down
Loading
Loading