Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
refactor ub_checks and contract_checks to share logic
  • Loading branch information
pitaj committed Oct 9, 2025
commit 55187791fec22373b553128ef30a456a3009ec66
3 changes: 1 addition & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,8 +1057,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ConstraintCategory::SizedBound,
);
}
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
&Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => {}

Rvalue::ShallowInitBox(_operand, ty) => {
let trait_ref =
Expand Down
13 changes: 2 additions & 11 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,17 +855,8 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
fields.iter(),
)
.bytes(),
NullOp::UbChecks => {
let val = fx.tcx.sess.ub_checks();
let val = CValue::by_val(
fx.bcx.ins().iconst(types::I8, i64::from(val)),
fx.layout_of(fx.tcx.types.bool),
);
lval.write_cvalue(fx, val);
return;
}
NullOp::ContractChecks => {
let val = fx.tcx.sess.contract_checks();
NullOp::RuntimeChecks(kind) => {
let val = kind.value(fx.tcx.sess);
let val = CValue::by_val(
fx.bcx.ins().iconst(types::I8, i64::from(val)),
fx.layout_of(fx.tcx.types.bool),
Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,12 +631,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
.bytes();
bx.cx().const_usize(val)
}
mir::NullOp::UbChecks => {
let val = bx.tcx().sess.ub_checks();
bx.cx().const_bool(val)
}
mir::NullOp::ContractChecks => {
let val = bx.tcx().sess.contract_checks();
mir::NullOp::RuntimeChecks(kind) => {
let val = kind.value(bx.tcx().sess);
bx.cx().const_bool(val)
}
};
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,11 +646,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}

Rvalue::NullaryOp(
NullOp::SizeOf
| NullOp::AlignOf
| NullOp::OffsetOf(_)
| NullOp::UbChecks
| NullOp::ContractChecks,
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_),
_,
) => {}
Rvalue::ShallowInitBox(_, _) => {}
Expand Down
22 changes: 9 additions & 13 deletions compiler/rustc_const_eval/src/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ pub trait Machine<'tcx>: Sized {
interp_ok(())
}

/// Determines the result of a `NullaryOp::UbChecks` invocation.
fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;

/// Determines the result of a `NullaryOp::ContractChecks` invocation.
fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
/// Determines the result of a `NullaryOp::RuntimeChecks` invocation.
fn runtime_checks(
_ecx: &InterpCx<'tcx, Self>,
r: mir::RuntimeChecks,
) -> InterpResult<'tcx, bool>;

/// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
/// You can use this to detect long or endlessly running programs.
Expand Down Expand Up @@ -681,14 +681,10 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
}

#[inline(always)]
fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
// unsound differences in evaluating the same constant at different instantiation sites.
interp_ok(true)
}

#[inline(always)]
fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
fn runtime_checks(
_ecx: &InterpCx<$tcx, Self>,
_r: mir::RuntimeChecks,
) -> InterpResult<$tcx, bool> {
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
// unsound differences in evaluating the same constant at different instantiation sites.
interp_ok(true)
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_const_eval/src/interpret/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes();
ImmTy::from_uint(val, usize_layout())
}
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx),
RuntimeChecks(r) => ImmTy::from_bool(M::runtime_checks(self, r)?, *self.tcx),
})
}
}
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,9 @@ impl<'tcx> Body<'tcx> {
}

match rvalue {
Rvalue::NullaryOp(NullOp::UbChecks, _) => Some((tcx.sess.ub_checks() as u128, targets)),
Rvalue::NullaryOp(NullOp::RuntimeChecks(kind), _) => {
Some((kind.value(tcx.sess) as u128, targets))
}
Rvalue::Use(Operand::Constant(constant)) => {
let bits = eval_mono_const(constant)?;
Some((bits, targets))
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1096,8 +1096,10 @@ impl<'tcx> Debug for Rvalue<'tcx> {
NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
NullOp::UbChecks => write!(fmt, "UbChecks()"),
NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
NullOp::RuntimeChecks(RuntimeChecks::UbChecks) => write!(fmt, "UbChecks()"),
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
write!(fmt, "ContractChecks()")
}
}
}
ThreadLocalRef(did) => ty::tls::with(|tcx| {
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,8 +786,7 @@ impl<'tcx> Rvalue<'tcx> {
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
tcx.types.usize
}
Rvalue::NullaryOp(NullOp::ContractChecks, _)
| Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => tcx.types.bool,
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
AggregateKind::Tuple => {
Expand Down Expand Up @@ -855,7 +854,7 @@ impl<'tcx> NullOp<'tcx> {
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) => tcx.types.usize,
NullOp::UbChecks | NullOp::ContractChecks => tcx.types.bool,
NullOp::RuntimeChecks(_) => tcx.types.bool,
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,12 @@ pub enum NullOp<'tcx> {
AlignOf,
/// Returns the offset of a field
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
/// Returns whether we should perform some checking at runtime.
RuntimeChecks(RuntimeChecks),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum RuntimeChecks {
/// Returns whether we should perform some UB-checking at runtime.
/// See the `ub_checks` intrinsic docs for details.
UbChecks,
Expand All @@ -1575,6 +1581,15 @@ pub enum NullOp<'tcx> {
ContractChecks,
}

impl RuntimeChecks {
pub fn value(self, sess: &rustc_session::Session) -> bool {
match self {
Self::UbChecks => sess.ub_checks(),
Self::ContractChecks => sess.contract_checks(),
}
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub enum UnOp {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/mir/traversal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ pub fn reverse_postorder<'a, 'tcx>(
/// reachable.
///
/// Such a traversal is mostly useful because it lets us skip lowering the `false` side
/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
/// of `if <T as Trait>::CONST`, as well as [`NullOp::RuntimeChecks`].
///
/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
/// [`NullOp::RuntimeChecks`]: rustc_middle::mir::NullOp::RuntimeChecks
pub fn mono_reachable<'a, 'tcx>(
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
Expand Down
6 changes: 1 addition & 5 deletions compiler/rustc_mir_dataflow/src/move_paths/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
| Rvalue::RawPtr(..)
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(
NullOp::SizeOf
| NullOp::AlignOf
| NullOp::OffsetOf(..)
| NullOp::UbChecks
| NullOp::ContractChecks,
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_),
_,
) => {}
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_transform/src/cost_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {

fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, _location: Location) {
match rvalue {
Rvalue::NullaryOp(NullOp::UbChecks, ..)
// FIXME: Should we do the same for `OverflowChecks`?
Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks), ..)
if !self
.tcx
.sess
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
.tcx
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
.bytes(),
NullOp::UbChecks => return None,
NullOp::ContractChecks => return None,
NullOp::RuntimeChecks(_) => return None,
};
ImmTy::from_uint(val, ty).into()
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_transform/src/instsimplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
}

fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) {
let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue else { return };
// FIXME: Should we do the same for overflow checks?
let Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks), _) = *rvalue else {
return;
};

let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks());
let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None };
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_transform/src/known_panics_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,8 +614,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
.tcx
.offset_of_subfield(self.typing_env, op_layout, fields.iter())
.bytes(),
NullOp::UbChecks => return None,
NullOp::ContractChecks => return None,
NullOp::RuntimeChecks(_) => return None,
};
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
}
Expand Down
20 changes: 7 additions & 13 deletions compiler/rustc_mir_transform/src/lower_intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,18 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
sym::unreachable => {
terminator.kind = TerminatorKind::Unreachable;
}
sym::ub_checks => {
let target = target.unwrap();
block.statements.push(Statement::new(
terminator.source_info,
StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool),
))),
));
terminator.kind = TerminatorKind::Goto { target };
}
sym::contract_checks => {
sym::ub_checks | sym::contract_checks => {
let op = match intrinsic.name {
sym::ub_checks => RuntimeChecks::UbChecks,
sym::contract_checks => RuntimeChecks::ContractChecks,
_ => unreachable!(),
};
let target = target.unwrap();
block.statements.push(Statement::new(
terminator.source_info,
StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool),
Rvalue::NullaryOp(NullOp::RuntimeChecks(op), tcx.types.bool),
))),
));
terminator.kind = TerminatorKind::Goto { target };
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_transform/src/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,7 @@ impl<'tcx> Validator<'_, 'tcx> {
NullOp::SizeOf => {}
NullOp::AlignOf => {}
NullOp::OffsetOf(_) => {}
NullOp::UbChecks => {}
NullOp::ContractChecks => {}
NullOp::RuntimeChecks(_) => {}
},

Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_mir_transform/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1371,10 +1371,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::RawPtr(_, _)
| Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks,
_,
)
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::RuntimeChecks(_), _)
| Rvalue::Discriminant(_) => {}

Rvalue::WrapUnsafeBinder(op, ty) => {
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_public/src/mir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,8 +645,7 @@ impl Rvalue {
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
Ok(Ty::usize_ty())
}
Rvalue::NullaryOp(NullOp::ContractChecks, _)
| Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => Ok(Ty::bool_ty()),
Rvalue::Aggregate(ak, ops) => match *ak {
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
AggregateKind::Tuple => Ok(Ty::new_tuple(
Expand Down Expand Up @@ -1031,6 +1030,12 @@ pub enum NullOp {
AlignOf,
/// Returns the offset of a field.
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
/// Codegen conditions for runtime checks.
RuntimeChecks(RuntimeChecks),
}

#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
pub enum RuntimeChecks {
/// cfg!(ub_checks), but at codegen time
UbChecks,
/// cfg!(contract_checks), but at codegen time
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_public/src/unstable/convert/stable/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,14 +325,17 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
cx: &CompilerCtxt<'cx, BridgeTys>,
) -> Self::T {
use rustc_middle::mir::NullOp::*;
use rustc_middle::mir::RuntimeChecks::*;
match self {
SizeOf => crate::mir::NullOp::SizeOf,
AlignOf => crate::mir::NullOp::AlignOf,
OffsetOf(indices) => crate::mir::NullOp::OffsetOf(
indices.iter().map(|idx| idx.stable(tables, cx)).collect(),
),
UbChecks => crate::mir::NullOp::UbChecks,
ContractChecks => crate::mir::NullOp::ContractChecks,
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
UbChecks => crate::mir::RuntimeChecks::UbChecks,
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
}),
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,7 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks,
_,
)
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _)
| Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, cx.tcx);
Expand Down
9 changes: 2 additions & 7 deletions src/tools/miri/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1299,13 +1299,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}

#[inline(always)]
fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
interp_ok(ecx.tcx.sess.ub_checks())
}

#[inline(always)]
fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
interp_ok(ecx.tcx.sess.contract_checks())
fn runtime_checks(ecx: &InterpCx<'tcx, Self>, r: mir::RuntimeChecks) -> InterpResult<'tcx, bool> {
interp_ok(r.value(&ecx.tcx.sess))
}

#[inline(always)]
Expand Down