Auto merge of #121662 - saethlin:precondition-unification, r=RalfJung · rust-lang/rust@768408a (original) (raw)
47 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | ||
2000 | 2000 | ConstraintCategory::SizedBound, |
2001 | 2001 | ); |
2002 | 2002 | } |
2003 | -&Rvalue::NullaryOp(NullOp::DebugAssertions, _) => {} | |
2003 | +&Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {} | |
2004 | 2004 | |
2005 | 2005 | Rvalue::ShallowInitBox(operand, ty) => { |
2006 | 2006 | self.check_operand(operand, location); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -779,7 +779,7 @@ fn codegen_stmt<'tcx>( | ||
779 | 779 | NullOp::OffsetOf(fields) => { |
780 | 780 | layout.offset_of_subfield(fx, fields.iter()).bytes() |
781 | 781 | } |
782 | -NullOp::DebugAssertions => { | |
782 | +NullOp::UbCheck(_) => { | |
783 | 783 | let val = fx.tcx.sess.opts.debug_assertions; |
784 | 784 | let val = CValue::by_val( |
785 | 785 | fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -685,7 +685,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | ||
685 | 685 | let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); |
686 | 686 | bx.cx().const_usize(val) |
687 | 687 | } |
688 | - mir::NullOp::DebugAssertions => { | |
688 | + mir::NullOp::UbCheck(_) => { | |
689 | +// In codegen, we want to check for language UB and library UB | |
689 | 690 | let val = bx.tcx().sess.opts.debug_assertions; |
690 | 691 | bx.cx().const_bool(val) |
691 | 692 | } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -258,10 +258,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ||
258 | 258 | let val = layout.offset_of_subfield(self, fields.iter()).bytes(); |
259 | 259 | Scalar::from_target_usize(val, self) |
260 | 260 | } |
261 | - mir::NullOp::DebugAssertions => { | |
262 | -// The checks hidden behind this are always better done by the interpreter | |
263 | -// itself, because it knows the runtime state better. | |
264 | -Scalar::from_bool(false) | |
261 | + mir::NullOp::UbCheck(kind) => { | |
262 | +// We want to enable checks for library UB, because the interpreter doesn't | |
263 | +// know about those on its own. | |
264 | +// But we want to disable checks for language UB, because the interpreter | |
265 | +// has its own better checks for that. | |
266 | +let should_check = match kind { | |
267 | + mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions, | |
268 | + mir::UbKind::LanguageUb => false, | |
269 | +}; | |
270 | +Scalar::from_bool(should_check) | |
265 | 271 | } |
266 | 272 | }; |
267 | 273 | 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> { | ||||
558 | 558 | Rvalue::Cast(_, _, _) => {} | ||
559 | 559 | |||
560 | 560 | Rvalue::NullaryOp( | ||
561 | -NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, | |
561 | +NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), | |
562 | 562 | _, | ||
563 | 563 | ) => {} | ||
564 | 564 | Rvalue::ShallowInitBox(_, _) => {} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | ||||
1157 | 1157 | Rvalue::Repeat(_, _) | ||
1158 | 1158 | | Rvalue::ThreadLocalRef(_) | ||
1159 | 1159 | | Rvalue::AddressOf(_, _) | ||
1160 | - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::DebugAssertions, _) | |
1160 | + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _) | |
1161 | 1161 | | Rvalue::Discriminant(_) => {} | ||
1162 | 1162 | } | ||
1163 | 1163 | self.super_rvalue(rvalue, location); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -127,7 +127,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | ||
127 | 127 | | sym::variant_count |
128 | 128 | | sym::is_val_statically_known |
129 | 129 | | sym::ptr_mask |
130 | - | sym::debug_assertions | |
130 | + | sym::check_language_ub | |
131 | + | sym::check_library_ub | |
131 | 132 | | sym::fadd_algebraic |
132 | 133 | | sym::fsub_algebraic |
133 | 134 | | sym::fmul_algebraic |
@@ -584,7 +585,7 @@ pub fn check_intrinsic_type( | ||
584 | 585 | (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) |
585 | 586 | } |
586 | 587 | |
587 | - sym::debug_assertions => (0, 1, Vec::new(), tcx.types.bool), | |
588 | + sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool), | |
588 | 589 | |
589 | 590 | sym::simd_eq |
590 | 591 | | sym::simd_ne |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -915,7 +915,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { | ||
915 | 915 | NullOp::SizeOf => write!(fmt, "SizeOf({t})"), |
916 | 916 | NullOp::AlignOf => write!(fmt, "AlignOf({t})"), |
917 | 917 | NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), |
918 | -NullOp::DebugAssertions => write!(fmt, "cfg!(debug_assertions)"), | |
918 | +NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"), | |
919 | 919 | } |
920 | 920 | } |
921 | 921 | ThreadLocalRef(did) => ty::tls::with(|tcx |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1366,8 +1366,16 @@ pub enum NullOp<'tcx> { | ||
1366 | 1366 | AlignOf, |
1367 | 1367 | /// Returns the offset of a field |
1368 | 1368 | OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), |
1369 | -/// cfg!(debug_assertions), but expanded in codegen | |
1370 | - DebugAssertions, | |
1369 | +/// Returns whether we want to check for library UB or language UB at monomorphization time. | |
1370 | + /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in | |
1371 | + /// const-eval/Miri, because the interpreter has its own better checks for language UB. | |
1372 | + UbCheck(UbKind), | |
1373 | +} | |
1374 | + | |
1375 | +#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] | |
1376 | +pub enum UbKind { | |
1377 | +LanguageUb, | |
1378 | +LibraryUb, | |
1371 | 1379 | } |
1372 | 1380 | |
1373 | 1381 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
@@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> { | |||
194 | 194 | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { |
195 | 195 | tcx.types.usize | |
196 | 196 | } | |
197 | -Rvalue::NullaryOp(NullOp::DebugAssertions, _) => tcx.types.bool, | ||
197 | +Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool, | ||
198 | 198 | Rvalue::Aggregate(ref ak, ref ops) => match **ak { | |
199 | 199 | AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), | |
200 | 200 | AggregateKind::Tuple => { |
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> { | ||||
433 | 433 | | Rvalue::Discriminant(..) | ||
434 | 434 | | Rvalue::Len(..) | ||
435 | 435 | | Rvalue::NullaryOp( | ||
436 | -NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::DebugAssertions, | |
436 | +NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_), | |
437 | 437 | _, | ||
438 | 438 | ) => {} | ||
439 | 439 | } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -488,7 +488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { | ||
488 | 488 | NullOp::OffsetOf(fields) => { |
489 | 489 | layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() |
490 | 490 | } |
491 | -NullOp::DebugAssertions => return None, | |
491 | +NullOp::UbCheck(_) => return None, | |
492 | 492 | }; |
493 | 493 | let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); |
494 | 494 | let imm = ImmTy::try_from_uint(val, usize_layout)?; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | ||
639 | 639 | NullOp::OffsetOf(fields) => { |
640 | 640 | op_layout.offset_of_subfield(self, fields.iter()).bytes() |
641 | 641 | } |
642 | -NullOp::DebugAssertions => return None, | |
642 | +NullOp::UbCheck(_) => return None, | |
643 | 643 | }; |
644 | 644 | ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() |
645 | 645 | } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -20,13 +20,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { | ||
20 | 20 | sym::unreachable => { |
21 | 21 | terminator.kind = TerminatorKind::Unreachable; |
22 | 22 | } |
23 | - sym::debug_assertions => { | |
23 | + sym::check_language_ub => { | |
24 | 24 | let target = target.unwrap(); |
25 | 25 | block.statements.push(Statement { |
26 | 26 | source_info: terminator.source_info, |
27 | 27 | kind: StatementKind::Assign(Box::new(( |
28 | 28 | *destination, |
29 | -Rvalue::NullaryOp(NullOp::DebugAssertions, tcx.types.bool), | |
29 | +Rvalue::NullaryOp( | |
30 | +NullOp::UbCheck(UbKind::LanguageUb), | |
31 | + tcx.types.bool, | |
32 | +), | |
33 | +))), | |
34 | +}); | |
35 | + terminator.kind = TerminatorKind::Goto { target }; | |
36 | +} | |
37 | + sym::check_library_ub => { | |
38 | +let target = target.unwrap(); | |
39 | + block.statements.push(Statement { | |
40 | +source_info: terminator.source_info, | |
41 | +kind: StatementKind::Assign(Box::new(( | |
42 | +*destination, | |
43 | +Rvalue::NullaryOp( | |
44 | +NullOp::UbCheck(UbKind::LibraryUb), | |
45 | + tcx.types.bool, | |
46 | +), | |
30 | 47 | ))), |
31 | 48 | }); |
32 | 49 | terminator.kind = TerminatorKind::Goto { target }; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> { | ||
446 | 446 | NullOp::SizeOf => {} |
447 | 447 | NullOp::AlignOf => {} |
448 | 448 | NullOp::OffsetOf(_) => {} |
449 | -NullOp::DebugAssertions => {} | |
449 | +NullOp::UbCheck(_) => {} | |
450 | 450 | }, |
451 | 451 | |
452 | 452 | Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -251,13 +251,19 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { | ||
251 | 251 | type T = stable_mir::mir::NullOp; |
252 | 252 | fn stable(&self, tables: &mut Tables<'_>) -> Self::T { |
253 | 253 | use rustc_middle::mir::NullOp::*; |
254 | +use rustc_middle::mir::UbKind; | |
254 | 255 | match self { |
255 | 256 | SizeOf => stable_mir::mir::NullOp::SizeOf, |
256 | 257 | AlignOf => stable_mir::mir::NullOp::AlignOf, |
257 | 258 | OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( |
258 | 259 | indices.iter().map(|idx |
259 | 260 | ), |
260 | -DebugAssertions => stable_mir::mir::NullOp::DebugAssertions, | |
261 | +UbCheck(UbKind::LanguageUb) => { | |
262 | + stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb) | |
263 | +} | |
264 | +UbCheck(UbKind::LibraryUb) => { | |
265 | + stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb) | |
266 | +} | |
261 | 267 | } |
262 | 268 | } |
263 | 269 | } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -518,6 +518,8 @@ symbols! { | ||
518 | 518 | cfi, |
519 | 519 | cfi_encoding, |
520 | 520 | char, |
521 | + check_language_ub, | |
522 | + check_library_ub, | |
521 | 523 | client, |
522 | 524 | clippy, |
523 | 525 | clobber_abi, |
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
@@ -639,7 +639,7 @@ impl Rvalue { | |||
639 | 639 | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { |
640 | 640 | Ok(Ty::usize_ty()) | |
641 | 641 | } | |
642 | -Rvalue::NullaryOp(NullOp::DebugAssertions, _) => Ok(Ty::bool_ty()), | ||
642 | +Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()), | ||
643 | 643 | Rvalue::Aggregate(ak, ops) => match *ak { | |
644 | 644 | AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), | |
645 | 645 | AggregateKind::Tuple => Ok(Ty::new_tuple( | |
@@ -1007,7 +1007,13 @@ pub enum NullOp { | |||
1007 | 1007 | /// Returns the offset of a field. | |
1008 | 1008 | OffsetOf(Vec<(VariantIdx, FieldIdx)>), | |
1009 | 1009 | /// cfg!(debug_assertions), but at codegen time | |
1010 | - DebugAssertions, | ||
1010 | + UbCheck(UbKind), | ||
1011 | +} | ||
1012 | + | ||
1013 | +#[derive(Clone, Debug, Eq, PartialEq)] | ||
1014 | +pub enum UbKind { | ||
1015 | +LanguageUb, | ||
1016 | +LibraryUb, | ||
1011 | 1017 | } | |
1012 | 1018 | ||
1013 | 1019 | impl Operand { |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -237,7 +237,6 @@ | ||
237 | 237 | |
238 | 238 | use crate::cmp::Ordering; |
239 | 239 | use crate::fmt::{self, Debug, Display}; |
240 | -use crate::intrinsics; | |
241 | 240 | use crate:📑:{PhantomData, Unsize}; |
242 | 241 | use crate::mem::{self, size_of}; |
243 | 242 | use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn}; |
@@ -435,8 +434,13 @@ impl Cell { | ||
435 | 434 | #[inline] |
436 | 435 | #[stable(feature = "move_cell", since = "1.17.0")] |
437 | 436 | pub fn swap(&self, other: &Self) { |
437 | +// This function documents that it *will* panic, and intrinsics::is_nonoverlapping doesn't | |
438 | +// do the check in const, so trying to use it here would be inviting unnecessary fragility. | |
438 | 439 | fn is_nonoverlapping<T>(src: *const T, dst: *const T) -> bool { |
439 | - intrinsics::is_nonoverlapping(src.cast(), dst.cast(), size_of::<T>(), 1) | |
440 | +let src_usize = src.addr(); | |
441 | +let dst_usize = dst.addr(); | |
442 | +let diff = src_usize.abs_diff(dst_usize); | |
443 | + diff >= size_of::<T>() | |
440 | 444 | } |
441 | 445 | |
442 | 446 | if ptr::eq(self, other) { |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -26,6 +26,7 @@ pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { | ||
26 | 26 | // SAFETY: the caller must guarantee that `i` is a valid char value. |
27 | 27 | unsafe { |
28 | 28 | assert_unsafe_precondition!( |
29 | + check_language_ub, | |
29 | 30 | "invalid value for `char`", |
30 | 31 | (i: u32 = i) => char_try_from_u32(i).is_ok() |
31 | 32 | ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -98,12 +98,14 @@ use crate::intrinsics; | ||
98 | 98 | #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] |
99 | 99 | #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces |
100 | 100 | pub const unsafe fn unreachable_unchecked() -> ! { |
101 | + intrinsics::assert_unsafe_precondition!( | |
102 | + check_language_ub, | |
103 | +"hint::unreachable_unchecked must never be reached", | |
104 | +() => false | |
105 | +); | |
101 | 106 | // SAFETY: the safety contract for `intrinsics::unreachable` must |
102 | 107 | // be upheld by the caller. |
103 | -unsafe { | |
104 | - intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false); | |
105 | - intrinsics::unreachable() | |
106 | -} | |
108 | +unsafe { intrinsics::unreachable() } | |
107 | 109 | } |
108 | 110 | |
109 | 111 | /// Makes a *soundness* promise to the compiler that `cond` holds. |
@@ -147,6 +149,7 @@ pub const unsafe fn assert_unchecked(cond: bool) { | ||
147 | 149 | // SAFETY: The caller promised `cond` is true. |
148 | 150 | unsafe { |
149 | 151 | intrinsics::assert_unsafe_precondition!( |
152 | + check_language_ub, | |
150 | 153 | "hint::assert_unchecked must never be called when the condition is false", |
151 | 154 | (cond: bool = cond) => cond, |
152 | 155 | ); |