Skip to content

refactor check_{lang,library}_ub: use a single intrinsic #122629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 23, 2024
Merged
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
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
@@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::SizedBound,
);
}
&Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {}
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}

Rvalue::ShallowInitBox(operand, ty) => {
self.check_operand(operand, location);
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
@@ -780,7 +780,7 @@ fn codegen_stmt<'tcx>(
NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(fx, fields.iter()).bytes()
}
NullOp::UbCheck(_) => {
NullOp::UbChecks => {
let val = fx.tcx.sess.opts.debug_assertions;
let val = CValue::by_val(
fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
@@ -680,8 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes();
bx.cx().const_usize(val)
}
mir::NullOp::UbCheck(_) => {
// In codegen, we want to check for language UB and library UB
mir::NullOp::UbChecks => {
let val = bx.tcx().sess.opts.debug_assertions;
bx.cx().const_bool(val)
}
12 changes: 1 addition & 11 deletions compiler/rustc_const_eval/src/interpret/step.rs
Original file line number Diff line number Diff line change
@@ -258,17 +258,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let val = layout.offset_of_subfield(self, fields.iter()).bytes();
Scalar::from_target_usize(val, self)
}
mir::NullOp::UbCheck(kind) => {
// We want to enable checks for library UB, because the interpreter doesn't
// know about those on its own.
// But we want to disable checks for language UB, because the interpreter
// has its own better checks for that.
let should_check = match kind {
mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions,
mir::UbKind::LanguageUb => false,
};
Scalar::from_bool(should_check)
}
mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.opts.debug_assertions),
};
self.write_scalar(val, &dest)?;
}
Original file line number Diff line number Diff line change
@@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}

Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_),
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks,
_,
) => {}
Rvalue::ShallowInitBox(_, _) => {}
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
@@ -1168,7 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::AddressOf(_, _)
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _)
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _)
| Rvalue::Discriminant(_) => {}
}
self.super_rvalue(rvalue, location);
5 changes: 2 additions & 3 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
@@ -127,8 +127,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
| sym::variant_count
| sym::is_val_statically_known
| sym::ptr_mask
| sym::check_language_ub
| sym::check_library_ub
| sym::ub_checks
| sym::fadd_algebraic
| sym::fsub_algebraic
| sym::fmul_algebraic
@@ -571,7 +570,7 @@ pub fn check_intrinsic_type(
(0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
}

sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool),
sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),

sym::simd_eq
| sym::simd_ne
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -796,7 +796,7 @@ impl<'tcx> Body<'tcx> {
}

match rvalue {
Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {
Rvalue::NullaryOp(NullOp::UbChecks, _) => {
Some((tcx.sess.opts.debug_assertions as u128, targets))
}
Rvalue::Use(Operand::Constant(constant)) => {
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
@@ -944,7 +944,7 @@ 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::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"),
NullOp::UbChecks => write!(fmt, "UbChecks()"),
Copy link
Member

@saethlin saethlin Mar 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any corresponding changes to the mir-opt tests, which has me worried. CI is green but the mir-opt suite does not pass locally. Do you know what's happening here? I'm confident that this either will fail in bors or should fail in bors, so at the least the mir-opt tests need to be blessed, and maybe we also have a CI/bootstrap bug.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think this happens because of

//@ ignore-debug assertions change the output MIR

The PR runner has debug assertions turned on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes this old problem. I'll see if I can do a PR to fix it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that even still needed? The stdlib is less dependent on the sysroot cfg(debug_assertions) now with your new intrinsic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is less need for it, but it's not gone entirely. I'll start by just enabling the tests that can be enabled for free: #122921

}
}
ThreadLocalRef(did) => ty::tls::with(|tcx| {
13 changes: 3 additions & 10 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
@@ -1367,16 +1367,9 @@ pub enum NullOp<'tcx> {
AlignOf,
/// Returns the offset of a field
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
/// Returns whether we want to check for library UB or language UB at monomorphization time.
/// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in
/// const-eval/Miri, because the interpreter has its own better checks for language UB.
UbCheck(UbKind),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum UbKind {
LanguageUb,
LibraryUb,
/// Returns whether we want to check for UB.
/// This returns the value of `cfg!(debug_assertions)` at monomorphization time.
UbChecks,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/tcx.rs
Original file line number Diff line number Diff line change
@@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> {
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
tcx.types.usize
}
Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool,
Rvalue::NullaryOp(NullOp::UbChecks, _) => 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 => {
2 changes: 1 addition & 1 deletion compiler/rustc_mir_dataflow/src/move_paths/builder.rs
Original file line number Diff line number Diff line change
@@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
| Rvalue::Discriminant(..)
| Rvalue::Len(..)
| Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_),
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks,
_,
) => {}
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/gvn.rs
Original file line number Diff line number Diff line change
@@ -487,7 +487,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
}
NullOp::UbCheck(_) => return None,
NullOp::UbChecks => return None,
};
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
let imm = ImmTy::try_from_uint(val, usize_layout)?;
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/known_panics_lint.rs
Original file line number Diff line number Diff line change
@@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
NullOp::OffsetOf(fields) => {
op_layout.offset_of_subfield(self, fields.iter()).bytes()
}
NullOp::UbCheck(_) => return None,
NullOp::UbChecks => return None,
};
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
}
21 changes: 2 additions & 19 deletions compiler/rustc_mir_transform/src/lower_intrinsics.rs
Original file line number Diff line number Diff line change
@@ -20,30 +20,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
sym::unreachable => {
terminator.kind = TerminatorKind::Unreachable;
}
sym::check_language_ub => {
sym::ub_checks => {
let target = target.unwrap();
block.statements.push(Statement {
source_info: terminator.source_info,
kind: StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(
NullOp::UbCheck(UbKind::LanguageUb),
tcx.types.bool,
),
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
sym::check_library_ub => {
let target = target.unwrap();
block.statements.push(Statement {
source_info: terminator.source_info,
kind: StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(
NullOp::UbCheck(UbKind::LibraryUb),
tcx.types.bool,
),
Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool),
))),
});
terminator.kind = TerminatorKind::Goto { target };
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/promote_consts.rs
Original file line number Diff line number Diff line change
@@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> {
NullOp::SizeOf => {}
NullOp::AlignOf => {}
NullOp::OffsetOf(_) => {}
NullOp::UbCheck(_) => {}
NullOp::UbChecks => {}
},

Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
8 changes: 1 addition & 7 deletions compiler/rustc_smir/src/rustc_smir/convert/mir.rs
Original file line number Diff line number Diff line change
@@ -251,19 +251,13 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
type T = stable_mir::mir::NullOp;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
use rustc_middle::mir::NullOp::*;
use rustc_middle::mir::UbKind;
match self {
SizeOf => stable_mir::mir::NullOp::SizeOf,
AlignOf => stable_mir::mir::NullOp::AlignOf,
OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf(
indices.iter().map(|idx| idx.stable(tables)).collect(),
),
UbCheck(UbKind::LanguageUb) => {
stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb)
}
UbCheck(UbKind::LibraryUb) => {
stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb)
}
UbChecks => stable_mir::mir::NullOp::UbChecks,
}
}
}
3 changes: 1 addition & 2 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -518,8 +518,6 @@ symbols! {
cfi,
cfi_encoding,
char,
check_language_ub,
check_library_ub,
client,
clippy,
clobber_abi,
@@ -1867,6 +1865,7 @@ symbols! {
u8_legacy_fn_max_value,
u8_legacy_fn_min_value,
u8_legacy_mod,
ub_checks,
unaligned_volatile_load,
unaligned_volatile_store,
unboxed_closures,
10 changes: 2 additions & 8 deletions compiler/stable_mir/src/mir/body.rs
Original file line number Diff line number Diff line change
@@ -621,7 +621,7 @@ impl Rvalue {
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
Ok(Ty::usize_ty())
}
Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()),
Rvalue::NullaryOp(NullOp::UbChecks, _) => 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(
@@ -989,13 +989,7 @@ pub enum NullOp {
/// Returns the offset of a field.
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
/// cfg!(debug_assertions), but at codegen time
UbCheck(UbKind),
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UbKind {
LanguageUb,
LibraryUb,
UbChecks,
Copy link
Member Author

@RalfJung RalfJung Mar 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no choice but to change smir here; the things it exposed were never meant to be very stable to begin with and I don't think we want to keep them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just been breaking smir every time I change MIR. Is there any guidance or enforcement that we don't do that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea. SMIR people get pinged when this happens so I guess they'll complain when it gets too much for them. ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to avoid these breakages. If something is unstable, we recommend to not expose them, or expose as an Opaque type.

See Coverage statement as an example.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this specific case, we could just always use the same UbKind value, and mark UbValue as deprecated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we leave compatibility shims in stable MIR (not that I think we should do that in this PR), is there a plan for how to remove them eventually?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StableMIR is not yet versioned, but once it is, we would likely go over things that has been marked as deprecated and remove them when a new major is about to be released.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RalfJung I think it is fine to remove it now.

I do have a question about how to evaluate this operator though. Is the idea here that we are assessing the value of cfg!(debug_assertions) for the crate being compiled? So if the MIR comes from a dependency that was compiled with debug_assertions off, but the current crate has the debug_assertions on, this rvalue will be evaluated to true. Is that correct?

Is it possible to document the answer here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly. This is what the existing documentation means when it says "the value of debug_assertions at monomorphization time".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have also extended the intrinsic's docs to make this more clear.

}

impl Operand {
2 changes: 1 addition & 1 deletion library/core/src/char/convert.rs
Original file line number Diff line number Diff line change
@@ -4,9 +4,9 @@ use crate::char::TryFromCharError;
use crate::convert::TryFrom;
use crate::error::Error;
use crate::fmt;
use crate::intrinsics::assert_unsafe_precondition;
use crate::mem::transmute;
use crate::str::FromStr;
use crate::ub_checks::assert_unsafe_precondition;

/// Converts a `u32` to a `char`. See [`char::from_u32`].
#[must_use]
5 changes: 3 additions & 2 deletions library/core/src/hint.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
//! Hints may be compile time or runtime.

use crate::intrinsics;
use crate::ub_checks;

/// Informs the compiler that the site which is calling this function is not
/// reachable, possibly enabling further optimizations.
@@ -98,7 +99,7 @@ use crate::intrinsics;
#[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub const unsafe fn unreachable_unchecked() -> ! {
intrinsics::assert_unsafe_precondition!(
ub_checks::assert_unsafe_precondition!(
check_language_ub,
"hint::unreachable_unchecked must never be reached",
() => false
@@ -148,7 +149,7 @@ pub const unsafe fn unreachable_unchecked() -> ! {
pub const unsafe fn assert_unchecked(cond: bool) {
// SAFETY: The caller promised `cond` is true.
unsafe {
intrinsics::assert_unsafe_precondition!(
ub_checks::assert_unsafe_precondition!(
check_language_ub,
"hint::assert_unchecked must never be called when the condition is false",
(cond: bool = cond) => cond,
Loading