From 2e5d395f2bfdedfda6bddc4797fbd4c04711efde Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 3 May 2025 22:58:27 -0600
Subject: [PATCH 1/2] refactor ub_checks and contract_checks to share logic
---
compiler/rustc_borrowck/src/type_check/mod.rs | 3 +--
compiler/rustc_codegen_cranelift/src/base.rs | 13 ++---------
compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 8 ++-----
.../src/check_consts/check.rs | 2 +-
.../rustc_const_eval/src/interpret/machine.rs | 22 ++++++++-----------
.../src/interpret/operator.rs | 3 +--
compiler/rustc_middle/src/mir/mod.rs | 4 +++-
compiler/rustc_middle/src/mir/pretty.rs | 6 +++--
compiler/rustc_middle/src/mir/statement.rs | 5 ++---
compiler/rustc_middle/src/mir/syntax.rs | 15 +++++++++++++
compiler/rustc_middle/src/mir/traversal.rs | 4 ++--
.../src/move_paths/builder.rs | 2 +-
.../rustc_mir_transform/src/cost_checker.rs | 3 ++-
compiler/rustc_mir_transform/src/gvn.rs | 3 +--
.../rustc_mir_transform/src/instsimplify.rs | 5 ++++-
.../src/known_panics_lint.rs | 3 +--
.../src/lower_intrinsics.rs | 9 ++++++--
.../rustc_mir_transform/src/promote_consts.rs | 3 +--
compiler/rustc_mir_transform/src/validate.rs | 2 +-
compiler/rustc_public/src/mir/body.rs | 9 ++++++--
.../src/unstable/convert/stable/mir.rs | 7 ++++--
.../clippy_utils/src/qualify_min_const_fn.rs | 2 +-
src/tools/miri/src/machine.rs | 9 ++------
23 files changed, 75 insertions(+), 67 deletions(-)
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index d1d0bff8fe06b..46f39ea8f2507 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1046,8 +1046,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
- &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
- &Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
+ &Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => {}
Rvalue::ShallowInitBox(_operand, ty) => {
let trait_ref =
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 7d50548b40262..6a1217a6a49b5 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -841,17 +841,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),
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 640f7211dc994..9f7eb9bc51bb5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -618,12 +618,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)
}
};
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index ca173fe26c2f6..c0a9bd187c147 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -646,7 +646,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}
Rvalue::NullaryOp(
- NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks,
+ NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_),
_,
) => {}
Rvalue::ShallowInitBox(_, _) => {}
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 1725635e0b479..27b1ca01514c4 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -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.
@@ -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)
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 58b90abf01295..feba237692090 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -522,8 +522,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),
})
}
}
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index fab04df500a5c..802adceb063ea 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -649,7 +649,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))
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 17d8eeee68bfd..a7941290de2e0 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1093,8 +1093,10 @@ impl<'tcx> Debug for Rvalue<'tcx> {
let t = with_no_trimmed_paths!(format!("{}", t));
match op {
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| {
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index 3cbb4b70b062a..a1f5ffb0b2774 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -795,8 +795,7 @@ impl<'tcx> Rvalue<'tcx> {
}
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
Rvalue::NullaryOp(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 => {
@@ -864,7 +863,7 @@ impl<'tcx> NullOp<'tcx> {
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
NullOp::OffsetOf(_) => tcx.types.usize,
- NullOp::UbChecks | NullOp::ContractChecks => tcx.types.bool,
+ NullOp::RuntimeChecks(_) => tcx.types.bool,
}
}
}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 9d99239546ae2..865b91817b3c5 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1565,6 +1565,12 @@ pub enum AggregateKind<'tcx> {
pub enum NullOp<'tcx> {
/// 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,
@@ -1573,6 +1579,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 {
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index 9308570d89d18..7f6c7376501fa 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -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 ::CONST`, as well as [`NullOp::UbChecks`].
+/// of `if ::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>,
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index dd65f0699e97e..8801fa8d9fd32 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -452,7 +452,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
| Rvalue::RawPtr(..)
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(
- NullOp::OffsetOf(..) | NullOp::UbChecks | NullOp::ContractChecks,
+ NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_),
_,
) => {}
}
diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs
index 00a8293966b04..1a9af0e22bbe2 100644
--- a/compiler/rustc_mir_transform/src/cost_checker.rs
+++ b/compiler/rustc_mir_transform/src/cost_checker.rs
@@ -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
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 8eae80e235ccd..e19a88d519a45 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -675,8 +675,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()
}
diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs
index 932744e4fa25c..51e18516534ad 100644
--- a/compiler/rustc_mir_transform/src/instsimplify.rs
+++ b/compiler/rustc_mir_transform/src/instsimplify.rs
@@ -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 };
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 131e3943689b8..dab2267eea690 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -612,8 +612,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()
}
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index ad7c52064a8ef..e6fa30a72b9b8 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -23,13 +23,18 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
sym::unreachable => {
terminator.kind = TerminatorKind::Unreachable;
}
- sym::ub_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::UbChecks, tcx.types.bool),
+ Rvalue::NullaryOp(NullOp::RuntimeChecks(op), tcx.types.bool),
))),
));
terminator.kind = TerminatorKind::Goto { target };
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 9204c221515c9..da5814c6b4cce 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -451,8 +451,7 @@ impl<'tcx> Validator<'_, 'tcx> {
Rvalue::NullaryOp(op, _) => match op {
NullOp::OffsetOf(_) => {}
- NullOp::UbChecks => {}
- NullOp::ContractChecks => {}
+ NullOp::RuntimeChecks(_) => {}
},
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index 5a9018a62c574..0e71e396c1441 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -1478,7 +1478,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::RawPtr(_, _)
- | Rvalue::NullaryOp(NullOp::UbChecks | NullOp::ContractChecks, _)
+ | Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _)
| Rvalue::Discriminant(_) => {}
Rvalue::WrapUnsafeBinder(op, ty) => {
diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs
index 1a939cbe8dba2..551f666023b0e 100644
--- a/compiler/rustc_public/src/mir/body.rs
+++ b/compiler/rustc_public/src/mir/body.rs
@@ -642,8 +642,7 @@ impl Rvalue {
.ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}"))
}
Rvalue::NullaryOp(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(
@@ -1024,6 +1023,12 @@ pub enum CastKind {
pub enum NullOp {
/// 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
diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
index f850bc5f89b8a..1de5a2b327799 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
@@ -322,12 +322,15 @@ 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 {
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,
+ }),
}
}
}
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 90ea2616890a4..0cf1ad348953b 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -194,7 +194,7 @@ fn check_rvalue<'tcx>(
))
}
},
- Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _)
+ Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _)
| Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, cx.tcx);
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index fadbdf5cea999..393895fe1a467 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -1329,13 +1329,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)]
From cc8b95cc5490552e7080ec4e98c19249b47dac3a Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 3 Aug 2024 22:57:10 -0600
Subject: [PATCH 2/2] add `overflow_checks` intrinsic
---
.../src/check_consts/check.rs | 5 +---
.../rustc_hir_analysis/src/check/intrinsic.rs | 3 +-
compiler/rustc_middle/src/mir/pretty.rs | 3 ++
compiler/rustc_middle/src/mir/syntax.rs | 4 +++
.../src/move_paths/builder.rs | 5 +---
.../src/lower_intrinsics.rs | 3 +-
compiler/rustc_public/src/mir/body.rs | 2 ++
.../src/unstable/convert/stable/mir.rs | 1 +
library/core/src/intrinsics/mod.rs | 18 ++++++++++++
.../auxiliary/overflow_checks_add.rs | 10 +++++++
tests/codegen-llvm/overflow-checks.rs | 29 +++++++++++++++++++
tests/ui/consts/const-eval/overflow_checks.rs | 8 +++++
12 files changed, 81 insertions(+), 10 deletions(-)
create mode 100644 tests/codegen-llvm/auxiliary/overflow_checks_add.rs
create mode 100644 tests/codegen-llvm/overflow-checks.rs
create mode 100644 tests/ui/consts/const-eval/overflow_checks.rs
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index c0a9bd187c147..f515f5d751bc6 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -645,10 +645,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}
- Rvalue::NullaryOp(
- NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_),
- _,
- ) => {}
+ Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) => {}
Rvalue::ShallowInitBox(_, _) => {}
Rvalue::UnaryOp(op, operand) => {
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index a6659912e3fb9..39c26f4ea40dc 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -163,6 +163,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::minnumf128
| sym::mul_with_overflow
| sym::needs_drop
+ | sym::overflow_checks
| sym::powf16
| sym::powf32
| sym::powf64
@@ -643,7 +644,7 @@ pub(crate) fn check_intrinsic_type(
sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)),
sym::ptr_metadata => (2, 0, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
- sym::ub_checks => (0, 0, Vec::new(), tcx.types.bool),
+ sym::ub_checks | sym::overflow_checks => (0, 0, Vec::new(), tcx.types.bool),
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index a7941290de2e0..f881ff1067d96 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1097,6 +1097,9 @@ impl<'tcx> Debug for Rvalue<'tcx> {
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
write!(fmt, "ContractChecks()")
}
+ NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
+ write!(fmt, "OverflowChecks()")
+ }
}
}
ThreadLocalRef(did) => ty::tls::with(|tcx| {
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 865b91817b3c5..3b48a68df1262 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1577,6 +1577,9 @@ pub enum RuntimeChecks {
/// Returns whether we should perform contract-checking at runtime.
/// See the `contract_checks` intrinsic docs for details.
ContractChecks,
+ /// Returns whether we should perform some overflow-checking at runtime.
+ /// See the `overflow_checks` intrinsic docs for details.
+ OverflowChecks,
}
impl RuntimeChecks {
@@ -1584,6 +1587,7 @@ impl RuntimeChecks {
match self {
Self::UbChecks => sess.ub_checks(),
Self::ContractChecks => sess.contract_checks(),
+ Self::OverflowChecks => sess.overflow_checks(),
}
}
}
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 8801fa8d9fd32..b4ffeb782b596 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -451,10 +451,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
Rvalue::Ref(..)
| Rvalue::RawPtr(..)
| Rvalue::Discriminant(..)
- | Rvalue::NullaryOp(
- NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_),
- _,
- ) => {}
+ | Rvalue::NullaryOp(NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_), _) => {}
}
}
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index e6fa30a72b9b8..1e874300e25ea 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -23,10 +23,11 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
sym::unreachable => {
terminator.kind = TerminatorKind::Unreachable;
}
- sym::ub_checks | sym::contract_checks => {
+ sym::ub_checks | sym::overflow_checks | sym::contract_checks => {
let op = match intrinsic.name {
sym::ub_checks => RuntimeChecks::UbChecks,
sym::contract_checks => RuntimeChecks::ContractChecks,
+ sym::overflow_checks => RuntimeChecks::OverflowChecks,
_ => unreachable!(),
};
let target = target.unwrap();
diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs
index 551f666023b0e..5f41b1063280e 100644
--- a/compiler/rustc_public/src/mir/body.rs
+++ b/compiler/rustc_public/src/mir/body.rs
@@ -1033,6 +1033,8 @@ pub enum RuntimeChecks {
UbChecks,
/// cfg!(contract_checks), but at codegen time
ContractChecks,
+ /// cfg!(overflow_checks), but at codegen time
+ OverflowChecks,
}
impl Operand {
diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
index 1de5a2b327799..d5896474d0093 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
@@ -330,6 +330,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
UbChecks => crate::mir::RuntimeChecks::UbChecks,
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,
+ OverflowChecks => crate::mir::RuntimeChecks::OverflowChecks,
}),
}
}
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index c397e762d5589..41afb3694b912 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -2585,6 +2585,24 @@ pub const fn ub_checks() -> bool {
cfg!(ub_checks)
}
+/// Returns whether we should perform some overflow-checking at runtime. This eventually evaluates to
+/// `cfg!(overflow_checks)`, but behaves different from `cfg!` when mixing crates built with different
+/// flags: if the crate has overflow checks enabled or carries the `#[rustc_inherit_overflow_checks]`
+/// attribute, evaluation is delayed until monomorphization (or until the call gets inlined into
+/// a crate that does not delay evaluation further); otherwise it can happen any time.
+///
+/// The common case here is a user program built with overflow_checks linked against the distributed
+/// sysroot which is built without overflow_checks but with `#[rustc_inherit_overflow_checks]`.
+/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with
+/// `#[inline]`), gating assertions on `overflow_checks()` rather than `cfg!(overflow_checks)` means that
+/// assertions are enabled whenever the *user crate* has overflow checks enabled. However if the
+/// user has overflow checks disabled, the checks will still get optimized out.
+#[inline(always)]
+#[rustc_intrinsic]
+pub const fn overflow_checks() -> bool {
+ cfg!(debug_assertions)
+}
+
/// Allocates a block of memory at compile time.
/// At runtime, just returns a null pointer.
///
diff --git a/tests/codegen-llvm/auxiliary/overflow_checks_add.rs b/tests/codegen-llvm/auxiliary/overflow_checks_add.rs
new file mode 100644
index 0000000000000..ea9db1e9837e1
--- /dev/null
+++ b/tests/codegen-llvm/auxiliary/overflow_checks_add.rs
@@ -0,0 +1,10 @@
+//@ compile-flags: -Cdebug-assertions=yes
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+/// Emulates the default behavior of `+` using `intrinsics::overflow_checks()`.
+#[inline]
+pub fn add(a: u8, b: u8) -> u8 {
+ if core::intrinsics::overflow_checks() { a.strict_add(b) } else { a.wrapping_add(b) }
+}
diff --git a/tests/codegen-llvm/overflow-checks.rs b/tests/codegen-llvm/overflow-checks.rs
new file mode 100644
index 0000000000000..c8b10df507b0f
--- /dev/null
+++ b/tests/codegen-llvm/overflow-checks.rs
@@ -0,0 +1,29 @@
+// With -Coverflow-checks=yes (enabled by default by -Cdebug-assertions=yes) we will produce a
+// runtime check that panics when an operation would result in integer overflow.
+//
+// This test ensures that such a runtime check is *not* emitted when debug-assertions are enabled,
+// but overflow-checks are explicitly disabled. It also ensures that even if a dependency is
+// compiled with overflow checks, `intrinsics::overflow_checks()` will be treated with the
+// overflow-checks setting of the current crate (when `#[rustc_inherit_overflow_checks]`) is used.
+
+//@ aux-build:overflow_checks_add.rs
+//@ revisions: DEBUG NOCHECKS
+//@ compile-flags: -O -Cdebug-assertions=yes
+//@ [NOCHECKS] compile-flags: -Coverflow-checks=no
+
+#![crate_type = "lib"]
+
+extern crate overflow_checks_add;
+
+// CHECK-LABEL: @add(
+#[no_mangle]
+pub unsafe fn add(a: u8, b: u8) -> u8 {
+ // CHECK: i8 noundef %a, i8 noundef %b
+ // CHECK: add i8 %b, %a
+ // DEBUG: icmp ult i8 [[zero:[^,]+]], %a
+ // DEBUG: call core::num::overflow_panic::add
+ // DEBUG: unreachable
+ // NOCHECKS-NOT: unreachable
+ // NOCHECKS: ret i8 %0
+ overflow_checks_add::add(a, b)
+}
diff --git a/tests/ui/consts/const-eval/overflow_checks.rs b/tests/ui/consts/const-eval/overflow_checks.rs
new file mode 100644
index 0000000000000..7f69157779906
--- /dev/null
+++ b/tests/ui/consts/const-eval/overflow_checks.rs
@@ -0,0 +1,8 @@
+//@ build-pass
+//@ compile-flags: -O -C overflow-checks=no
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+// Always returns true during CTFE, even if overflow checks are disabled.
+const _: () = assert!(core::intrinsics::overflow_checks());