diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index dbaa9e5bcfab0..eb0d82a5b9d44 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -628,6 +628,10 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( span, "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics", + ), + NonDivergingIntrinsic::UbCheck { .. } => span_bug!( + span, + "Unexpected UbCheck, should only appear after lower_intrinsics", ) } // Only relevant for mir typeck diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 10941cadcbb25..9ec262ef6a829 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -62,6 +62,18 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { self.consume_operand(location, dst); self.consume_operand(location, count); } + StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { + kind: _, + func, + args, + destination, + source_info: _, + fn_span: _, + }) => { + self.consume_operand(location, func); + self.consume_operand(location, args); + self.mutate_place(location, *destination, Shallow(None)); + } // Only relevant for mir typeck StatementKind::AscribeUserType(..) // Only relevant for liveness and unsafeck diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 75cc28bcab0bc..dae8ecf0b40a2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1367,6 +1367,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { stmt.source_info.span, "Unexpected NonDivergingIntrinsic::CopyNonOverlapping, should only appear after lowering_intrinsics", ), + NonDivergingIntrinsic::UbCheck { .. } => span_bug!( + stmt.source_info.span, + "Unexpected NonDivergingIntrinsic::UbCheck, should only appear after lowering_intrinsics", + ), }, StatementKind::FakeRead(..) | StatementKind::StorageLive(..) @@ -2001,7 +2005,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } - &Rvalue::NullaryOp(NullOp::DebugAssertions, _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index a7e76fbc128ea..441509c2df8fa 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -767,15 +767,6 @@ fn codegen_stmt<'tcx>( NullOp::OffsetOf(fields) => { layout.offset_of_subfield(fx, fields.iter()).bytes() } - NullOp::DebugAssertions => { - let val = fx.tcx.sess.opts.debug_assertions; - let val = CValue::by_val( - fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), - fx.layout_of(fx.tcx.types.bool), - ); - lval.write_cvalue(fx, val); - return; - } }; let val = CValue::by_val( fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()), @@ -845,6 +836,9 @@ fn codegen_stmt<'tcx>( }; fx.bcx.call_memcpy(fx.target_config, dst, src, bytes); } + NonDivergingIntrinsic::UbCheck { .. } => { + todo!() // FIXME + } }, } } diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index b6de688130c79..8be8e171af5a9 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -504,6 +504,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( StatementKind::Intrinsic(ref intrinsic) => match **intrinsic { NonDivergingIntrinsic::CopyNonOverlapping(..) => return None, NonDivergingIntrinsic::Assume(..) => {} + NonDivergingIntrinsic::UbCheck { .. } => {} }, // conservative handling StatementKind::Assign(_) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 9c7aadb81f828..2ec534b39bf56 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -28,19 +28,19 @@ use std::cmp; // can happen when BB jumps directly to its successor and the successor has no // other predecessors. #[derive(Debug, PartialEq)] -enum MergingSucc { +pub(crate) enum MergingSucc { False, True, } /// Used by `FunctionCx::codegen_terminator` for emitting common patterns /// e.g., creating a basic block, calling a function, etc. -struct TerminatorCodegenHelper<'tcx> { - bb: mir::BasicBlock, - terminator: &'tcx mir::Terminator<'tcx>, +pub(crate) struct TerminatorCodegenHelper<'term, 'tcx> { + pub(crate) bb: mir::BasicBlock, + pub(crate) terminator: &'term mir::Terminator<'tcx>, } -impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { +impl<'term, 'a, 'tcx> TerminatorCodegenHelper<'term, 'tcx> { /// Returns the appropriate `Funclet` for the current funclet, if on MSVC, /// either already previously cached, or newly created, by `landing_pad_for`. fn funclet<'b, Bx: BuilderMethods<'a, 'tcx>>( @@ -98,6 +98,10 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { fx: &mut FunctionCx<'a, 'tcx, Bx>, target: mir::BasicBlock, ) -> (bool, bool) { + if self.bb > fx.mir.basic_blocks.len().into() { + return (false, false); + } + if let Some(ref cleanup_kinds) = fx.cleanup_kinds { let funclet_bb = cleanup_kinds[self.bb].funclet_bb(self.bb); let target_funclet = cleanup_kinds[target].funclet_bb(target); @@ -226,8 +230,10 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { MergingSucc::False } else { let llret = bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx)); - if fx.mir[self.bb].is_cleanup { - bx.apply_attrs_to_cleanup_callsite(llret); + if let Some(bb) = fx.mir.basic_blocks.get(self.bb) { + if bb.is_cleanup { + bx.apply_attrs_to_cleanup_callsite(llret); + } } if let Some((ret_dest, target)) = destination { @@ -296,7 +302,11 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { /// Codegen implementations for some terminator variants. impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Generates code for a `Resume` terminator. - fn codegen_resume_terminator(&mut self, helper: TerminatorCodegenHelper<'tcx>, bx: &mut Bx) { + fn codegen_resume_terminator( + &mut self, + helper: TerminatorCodegenHelper<'_, 'tcx>, + bx: &mut Bx, + ) { if let Some(funclet) = helper.funclet(self) { bx.cleanup_ret(funclet, None); } else { @@ -313,7 +323,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_switchint_terminator( &mut self, - helper: TerminatorCodegenHelper<'tcx>, + helper: TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, discr: &mir::Operand<'tcx>, targets: &SwitchTargets, @@ -451,7 +461,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[tracing::instrument(level = "trace", skip(self, helper, bx))] fn codegen_drop_terminator( &mut self, - helper: TerminatorCodegenHelper<'tcx>, + helper: TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, location: mir::Place<'tcx>, target: mir::BasicBlock, @@ -569,7 +579,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_assert_terminator( &mut self, - helper: TerminatorCodegenHelper<'tcx>, + helper: TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, terminator: &mir::Terminator<'tcx>, cond: &mir::Operand<'tcx>, @@ -649,7 +659,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_terminate_terminator( &mut self, - helper: TerminatorCodegenHelper<'tcx>, + helper: TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, terminator: &mir::Terminator<'tcx>, reason: UnwindTerminateReason, @@ -678,7 +688,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Returns `Some` if this is indeed a panic intrinsic and codegen is done. fn codegen_panic_intrinsic( &mut self, - helper: &TerminatorCodegenHelper<'tcx>, + helper: &TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, intrinsic: Option, instance: Option>, @@ -744,9 +754,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn codegen_call_terminator( + pub(crate) fn codegen_call_terminator( &mut self, - helper: TerminatorCodegenHelper<'tcx>, + helper: TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, terminator: &mir::Terminator<'tcx>, func: &mir::Operand<'tcx>, @@ -1068,7 +1078,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn codegen_asm_terminator( &mut self, - helper: TerminatorCodegenHelper<'tcx>, + helper: TerminatorCodegenHelper<'_, 'tcx>, bx: &mut Bx, terminator: &mir::Terminator<'tcx>, template: &[ast::InlineAsmTemplatePiece], diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 5c6060a7159aa..0912982754fa8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -684,10 +684,6 @@ 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::DebugAssertions => { - let val = bx.tcx().sess.opts.debug_assertions; - bx.cx().const_bool(val) - } }; let tcx = self.cx.tcx(); OperandRef { diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index ac7dfbb261dec..7926797faec54 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -1,9 +1,11 @@ use rustc_middle::mir; use rustc_middle::mir::NonDivergingIntrinsic; use rustc_session::config::OptLevel; +use rustc_span::source_map::respan; use super::FunctionCx; use super::LocalRef; +use crate::mir::block::TerminatorCodegenHelper; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -90,6 +92,44 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let src = src_val.immediate(); bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty()); } + mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { + kind: _, + ref func, + ref args, + destination, + fn_span, + source_info, + }) => { + if bx.tcx().sess.opts.debug_assertions { + let bb = mir::BasicBlock::MAX; + let args = vec![respan(source_info.span, args.clone())]; + let terminator = mir::Terminator { + source_info, + kind: mir::TerminatorKind::Call { + func: func.clone(), + args: args.clone(), + destination, + target: Some(bb), + unwind: mir::UnwindAction::Unreachable, + call_source: mir::CallSource::Normal, + fn_span, + }, + }; + let helper = TerminatorCodegenHelper { bb, terminator: &terminator }; + self.codegen_call_terminator( + helper, + bx, + &terminator, + func, + args.as_slice(), + destination, + Some(bb), + mir::UnwindAction::Unreachable, + fn_span, + true, // mergeable_succ + ); + } + } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 946ffc05cc14f..98843efcfcdb5 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -317,6 +317,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { dest, ret, mir::UnwindAction::Unreachable, + false, )?; Ok(ControlFlow::Break(())) } else { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index cb308ab53ecfb..7965b874a3275 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -155,6 +155,9 @@ pub enum StackPopCleanup { /// wants them leaked to intern what they need (and just throw away /// the entire `ecx` when it is done). Root { cleanup: bool }, + + /// FIXME: We're returning from a UbCheck call, don't do anything. + Nothing, } /// State of a local variable including a memoized layout @@ -928,6 +931,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let cleanup = match return_to_block { StackPopCleanup::Goto { .. } => true, StackPopCleanup::Root { cleanup, .. } => cleanup, + StackPopCleanup::Nothing => true, }; if cleanup { // We need to take the locals out, since we need to mutate while iterating. @@ -964,6 +968,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { StackPopCleanup::Root { .. } => { panic!("encountered StackPopCleanup::Root when unwinding!") } + StackPopCleanup::Nothing => { + panic!("encountered StackPopCleanup::Nothing when unwinding!") + } }; // This must be the very last thing that happens, since it can in fact push a new stack frame. self.unwind_to_block(unwind) @@ -978,6 +985,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); Ok(()) } + StackPopCleanup::Nothing => { + // Advance the program counter. + self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1; + Ok(()) + } } } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 1cb991b38f7e9..eaea2beb542e4 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -458,6 +458,74 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let count = self.eval_operand(count, None)?; self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true) } + NonDivergingIntrinsic::UbCheck { + kind, + func, + args, + destination, + source_info, + fn_span: _, + } => { + use crate::interpret::FnVal; + use crate::rustc_middle::ty::layout::FnAbiOf; + use rustc_span::source_map::respan; + + // 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 => true, + mir::UbKind::LanguageUb => false, + }; + if !should_check { + return Ok(()); + } + + let old_stack = self.frame_idx(); + let old_loc = self.frame().loc; + let func = self.eval_operand(func, None)?; + let args = + self.eval_fn_call_arguments(&[respan(source_info.span, args.clone())])?; + + let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); + let fn_sig = + self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = + self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); + + let (fn_val, fn_abi, with_caller_location) = match *func.layout.ty.kind() { + ty::FnDef(def_id, args) => { + let instance = self.resolve(def_id, args)?; + ( + FnVal::Instance(instance), + self.fn_abi_of_instance(instance, extra_args)?, + instance.def.requires_caller_location(*self.tcx), + ) + } + _ => span_bug!(source_info.span, "invalid callee of type {}", func.layout.ty), + }; + + let destination = self.eval_place(*destination)?; + self.eval_fn_call( + fn_val, + (fn_sig.abi, fn_abi), + &args, + with_caller_location, + &destination, + None, // target + mir::UnwindAction::Unreachable, + true, + )?; + trace!("{:?}", self.frame().locals); + // Sanity-check that `eval_fn_call` either pushed a new frame or + // did a jump to another block. + if self.frame_idx() == old_stack && self.frame().loc == old_loc { + span_bug!(source_info.span, "evaluating this call made no progress"); + } + Ok(()) + } } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index d4c96f4573d96..3cb7b7fa8e940 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -6,6 +6,9 @@ use either::Either; use rustc_index::IndexSlice; use rustc_middle::mir; +use rustc_middle::mir::NonDivergingIntrinsic; +use rustc_middle::mir::StatementKind; +use rustc_middle::mir::UbKind; use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; @@ -35,11 +38,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(stmt) = basic_block.statements.get(loc.statement_index) { let old_frames = self.frame_idx(); + info!("{:?}", self.frame().loc); + trace!("{:?}", self.frame().locals); self.statement(stmt)?; - // Make sure we are not updating `statement_index` of the wrong frame. - assert_eq!(old_frames, self.frame_idx()); - // Advance the program counter. - self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1; + // We're stepping into a UbCheck, so we expect stack depth to increase by 1 + if let StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { + kind: UbKind::LibraryUb, + .. + }) = stmt.kind + { + } else { + // Make sure we are not updating `statement_index` of the wrong frame. + assert_eq!(old_frames, self.frame_idx()); + // Advance the program counter. + self.frame_mut().loc.as_mut().left().unwrap().statement_index += 1; + } return Ok(true); } @@ -258,11 +271,6 @@ 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::DebugAssertions => { - // The checks hidden behind this are always better done by the interpreter - // itself, because it knows the runtime state better. - Scalar::from_bool(false) - } }; self.write_scalar(val, &dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index e72ace8be3559..28aab7540797f 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -162,6 +162,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &destination, target, if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable }, + false, )?; // Sanity-check that `eval_fn_call` either pushed a new frame or // did a jump to another block. @@ -506,6 +507,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { destination: &PlaceTy<'tcx, M::Provenance>, target: Option, mut unwind: mir::UnwindAction, + is_ubcheck: bool, ) -> InterpResult<'tcx> { trace!("eval_fn_call: {:#?}", fn_val); @@ -591,12 +593,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { unwind = mir::UnwindAction::Unreachable; } - self.push_stack_frame( - instance, - body, - destination, - StackPopCleanup::Goto { ret: target, unwind }, - )?; + if is_ubcheck { + self.push_stack_frame(instance, body, destination, StackPopCleanup::Nothing)?; + info!("{:?}", self.frame().loc); + } else { + self.push_stack_frame( + instance, + body, + destination, + StackPopCleanup::Goto { ret: target, unwind }, + )?; + } // If an error is raised here, pop the frame again to get an accurate backtrace. // To this end, we wrap it all in a `try` block. @@ -880,6 +887,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { destination, target, unwind, + false, ) } } @@ -956,6 +964,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &ret.into(), Some(target), unwind, + false, ) } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index effaedd0820c3..b123c66c46d55 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -557,10 +557,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} - Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, - _, - ) => {} + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) => {} Rvalue::ShallowInitBox(_, _) => {} Rvalue::UnaryOp(_, operand) => { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cc49e8ea247f7..298ba1afcc1df 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::AddressOf(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::DebugAssertions, _) + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); @@ -1249,6 +1249,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({op_cnt_ty:?} != usize)")) } } + StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { + kind: _, + func: _, + args: _, + destination: _, + source_info: _, + fn_span: _, + }) => { + // FIXME + } StatementKind::SetDiscriminant { place, .. } => { if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3b7a7817279fc..0383ac6886715 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -123,7 +123,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::debug_assertions + | sym::check_language_ub + | sym::check_library_ub | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -508,7 +509,9 @@ pub fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) } - sym::debug_assertions => (0, 1, Vec::new(), tcx.types.bool), + sym::check_language_ub | sym::check_library_ub => { + (2, 1, vec![param(0), param(1)], tcx.types.unit) + } sym::simd_eq | sym::simd_ne diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 5638b575b319c..52e2584e42820 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -697,6 +697,9 @@ impl Display for NonDivergingIntrinsic<'_> { Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => { write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})") } + Self::UbCheck { kind, func, args, .. } => { + write!(f, "UbCheck({kind:?}): {func:?}({args:?})") + } } } } @@ -909,7 +912,6 @@ 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::DebugAssertions => write!(fmt, "cfg!(debug_assertions)"), } } 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 5c9857b9c53d8..acd2d9124a904 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -6,6 +6,7 @@ use super::{BasicBlock, Const, Local, UserTypeProjection}; use crate::mir::coverage::CoverageKind; +use crate::mir::SourceInfo; use crate::traits::Reveal; use crate::ty::adjustment::PointerCoercion; use crate::ty::GenericArgsRef; @@ -444,6 +445,33 @@ pub enum NonDivergingIntrinsic<'tcx> { /// **Needs clarification**: Is this typed or not, ie is there a typed load and store involved? /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(CopyNonOverlapping<'tcx>), + + UbCheck { + kind: UbKind, + func: Operand<'tcx>, + args: Operand<'tcx>, + destination: Place<'tcx>, + source_info: SourceInfo, + fn_span: Span, + }, +} + +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + TyEncodable, + TyDecodable, + Hash, + HashStable, + TypeFoldable, + TypeVisitable +)] +pub enum UbKind { + LanguageUb, + LibraryUb, } /// Describes what kind of retag is to be performed. @@ -1361,8 +1389,6 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// cfg!(debug_assertions), but expanded in codegen - DebugAssertions, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 5bc151de659c3..4780042a51090 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -194,7 +194,6 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::DebugAssertions, _) => 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 => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 2c5ca82a4cd39..faeaa526be467 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -444,6 +444,13 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } + NonDivergingIntrinsic::UbCheck { kind: _, func, args, destination: _, source_info: _, fn_span: _ } => { + self.visit_operand(func, location); + self.visit_operand(args, location); + // FIXME destination is always a ZST so this probably won't make + // things explode, but we don't have a PlaceContext for this + // situation that won't make ssa.rs ICE. + } } } StatementKind::ConstEvalCounter => {} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index c6ec1b5aee454..8c2690574e543 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -432,10 +432,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::AddressOf(..) | Rvalue::Discriminant(..) | Rvalue::Len(..) - | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::DebugAssertions, - _, - ) => {} + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {} } } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 2b2af6ee7da3e..20e38b0f3ae10 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -136,6 +136,16 @@ pub trait ValueAnalysis<'tcx> { }) => { // This statement represents `*dst = *src`, `count` times. } + NonDivergingIntrinsic::UbCheck { + kind: _, + func: _, + args: _, + destination: _, + source_info: _, + fn_span: _, + } => { + // FIXME: Why are the above allowed to ignore everything? + } } } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 2c692c9500303..1ae5dce6203ed 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -47,7 +47,8 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Deinit(_) - | StatementKind::Nop => {} + | StatementKind::Nop + | StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { .. }) => {} _ => self.cost += INSTR_COST, } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index a080e2423d47b..8ab065fdd56f9 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -488,7 +488,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { NullOp::OffsetOf(fields) => { layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() } - NullOp::DebugAssertions => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::try_from_uint(val, usize_layout)?; diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index ad8f21ffbdaa1..1bec5b52ed8d4 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -332,6 +332,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { | StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) // copy_nonoverlapping takes pointers and mutated the pointed-to value. | StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { .. }) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 7cab665099482..31966b44a2454 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -639,7 +639,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { NullOp::OffsetOf(fields) => { op_layout.offset_of_subfield(self, fields.iter()).bytes() } - NullOp::DebugAssertions => 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 a0af902c4e109..c5dbc2024041e 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -11,8 +11,15 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { let local_decls = &body.local_decls; for block in body.basic_blocks.as_mut() { let terminator = block.terminator.as_mut().unwrap(); - if let TerminatorKind::Call { func, args, destination, target, .. } = - &mut terminator.kind + if let TerminatorKind::Call { + func, + args, + destination, + target, + call_source: _, + fn_span, + unwind, + } = &mut terminator.kind && let ty::FnDef(def_id, generic_args) = *func.ty(local_decls, tcx).kind() && let Some(intrinsic_name) = tcx.intrinsic(def_id) { @@ -20,15 +27,44 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } - sym::debug_assertions => { + sym::check_language_ub => { + assert_eq!(*unwind, UnwindAction::Unreachable); + let mut args = args.drain(..); let target = target.unwrap(); block.statements.push(Statement { source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp(NullOp::DebugAssertions, tcx.types.bool), - ))), + kind: StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::UbCheck { + kind: UbKind::LanguageUb, + func: args.next().unwrap().node, + args: args.next().unwrap().node, + source_info: terminator.source_info, + destination: *destination, + fn_span: *fn_span, + }, + )), }); + drop(args); + terminator.kind = TerminatorKind::Goto { target }; + } + sym::check_library_ub => { + assert_eq!(*unwind, UnwindAction::Unreachable); + let mut args = args.drain(..); + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::UbCheck { + kind: UbKind::LibraryUb, + func: args.next().unwrap().node, + args: args.next().unwrap().node, + source_info: terminator.source_info, + destination: *destination, + fn_span: *fn_span, + }, + )), + }); + drop(args); terminator.kind = TerminatorKind::Goto { target }; } sym::forget => { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 2e11da4d585ef..d67fe6fa18463 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -446,7 +446,6 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::SizeOf => {} NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} - NullOp::DebugAssertions => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5593de607844d..b83d399b95222 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -833,6 +833,23 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { MirVisitor::visit_ty(self, const_.ty(), TyContext::Location(location)); } + fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, location: Location) { + let tcx = self.tcx; + let source = self.body.source_info(location).span; + + if let mir::StatementKind::Intrinsic(box mir::NonDivergingIntrinsic::UbCheck { + func, .. + }) = &statement.kind + { + let callee_ty = func.ty(self.body, tcx); + let callee_ty = self.monomorphize(callee_ty); + // self.check_fn_args_move_size(callee_ty, args, *fn_span, location); + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) + } + + self.super_statement(statement, location); + } + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { debug!("visiting terminator {:?} @ {:?}", terminator, location); let source = self.body.source_info(location).span; diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 7f36ae91f1a03..aa3dc352f9460 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,6 @@ #![feature(array_windows)] #![feature(is_sorted)] +#![feature(box_patterns)] #![allow(rustc::potential_query_instability)] #[macro_use] diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 501d6f7d304ef..4a72eba670a5e 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -257,7 +257,6 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( indices.iter().map(|idx| idx.stable(tables)).collect(), ), - DebugAssertions => stable_mir::mir::NullOp::DebugAssertions, } } } @@ -424,6 +423,22 @@ impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> { count: copy_non_overlapping.count.stable(tables), }) } + NonDivergingIntrinsic::UbCheck { + kind, + func, + args, + destination, + source_info: _, + fn_span: _, + } => stable_mir::mir::NonDivergingIntrinsic::UbCheck { + kind: match kind { + rustc_middle::mir::UbKind::LibraryUb => stable_mir::mir::UbKind::LibraryUb, + rustc_middle::mir::UbKind::LanguageUb => stable_mir::mir::UbKind::LanguageUb, + }, + func: func.stable(tables), + args: args.stable(tables), + destination: destination.stable(tables), + }, } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4d8475bef54c8..a5bd1d88304ad 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,6 +515,8 @@ symbols! { cfi, cfi_encoding, char, + check_language_ub, + check_library_ub, client, clippy, clobber_abi, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index be727f024c609..e805b1f090217 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -462,6 +462,13 @@ pub struct CopyNonOverlapping { pub enum NonDivergingIntrinsic { Assume(Operand), CopyNonOverlapping(CopyNonOverlapping), + UbCheck { kind: UbKind, func: Operand, args: Operand, destination: Place }, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum UbKind { + LanguageUb, + LibraryUb, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -639,7 +646,6 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::DebugAssertions, _) => 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( @@ -1006,8 +1012,6 @@ pub enum NullOp { AlignOf, /// Returns the offset of a field. OffsetOf(Vec<(VariantIdx, FieldIdx)>), - /// cfg!(debug_assertions), but at codegen time - DebugAssertions, } impl Operand { diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index 24296e9e8778b..245016b09ad46 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -228,6 +228,11 @@ pub trait MirVisitor { self.visit_operand(dst, location); self.visit_operand(count, location); } + NonDivergingIntrinsic::UbCheck { kind: _, func, args, destination } => { + self.visit_operand(func, location); + self.visit_operand(args, location); + self.visit_place(destination, PlaceContext::MUTATING, location); + } }, StatementKind::ConstEvalCounter => {} StatementKind::Nop => {} diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 7bd592492a5c3..70b9e89f9ea93 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -26,6 +26,7 @@ pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { assert_unsafe_precondition!( + check_language_ub, "invalid value for `char`", (i: u32 = i) => char_try_from_u32(i).is_ok() ); diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 97c3c9e6fae95..68719eb84a507 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -101,7 +101,11 @@ pub const unsafe fn unreachable_unchecked() -> ! { // SAFETY: the safety contract for `intrinsics::unreachable` must // be upheld by the caller. unsafe { - intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false); + intrinsics::assert_unsafe_precondition!( + check_language_ub, + "hint::unreachable_unchecked must never be reached", + () => false + ); intrinsics::unreachable() } } @@ -147,6 +151,7 @@ pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { intrinsics::assert_unsafe_precondition!( + check_language_ub, "hint::assert_unchecked must never be called when the condition is false", (cond: bool = cond) => cond, ); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 96e667d63c5f3..dd9730aea301f 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2625,25 +2625,53 @@ pub const fn is_val_statically_known(_arg: T) -> bool { false } -/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in -/// macro expansion. -/// -/// This always returns `false` in const eval and Miri. The interpreter provides better -/// diagnostics than the checks that this is used to implement. However, this means -/// you should only be using this intrinsic to guard requirements that, if violated, -/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those -/// checks entirely. -/// -/// Since this is evaluated after monomorphization, branching on this value can be used to -/// implement debug assertions that are included in the precompiled standard library, but can -/// be optimized out by builds that monomorphize the standard library code with debug +/// Whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` +/// in codegen, and `true` in const-eval/Miri. +/// +/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can +/// be used to implement debug assertions that are included in the precompiled standard library, +/// but can be optimized out by builds that monomorphize the standard library code with debug /// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. -#[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")] +/// +/// We have separate intrinsics for library UB and language UB because checkers like the const-eval +/// interpreter and Miri already implement checks for language UB. Since such checkers do not know +/// about library preconditions, checks implemented with this intrinsic let them find more UB. +#[rustc_const_unstable(feature = "ub_checks", issue = "none")] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[inline(always)] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[rustc_nounwind] +pub(crate) const fn check_library_ub(f: F, args: Args) +where + F: Fn(Args) + Copy, + Args: Copy, +{ + if cfg!(debug_assertions) { + f(args); + } +} + +/// Whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` +/// in codegen, and `false` in const-eval/Miri. +/// +/// Since checks implemented at the source level must come strictly before the operation that +/// executes UB, if we enabled library UB checks in const-eval/Miri we would miss out on the +/// interpreter's improved diagnostics for the cases that our source-level checks catch. +/// +/// See `check_library_ub` for more information. +#[rustc_const_unstable(feature = "ub_checks", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] #[inline(always)] #[cfg_attr(not(bootstrap), rustc_intrinsic)] -pub(crate) const fn debug_assertions() -> bool { - cfg!(debug_assertions) +#[rustc_nounwind] +pub(crate) const fn check_language_ub(f: F, args: Args) +where + F: Fn(Args) + Copy, + Args: Copy, +{ + if cfg!(debug_assertions) { + f(args); + } } /// Allocates a block of memory at compile time. @@ -2656,9 +2684,9 @@ pub(crate) const fn debug_assertions() -> bool { /// - At runtime, it is not checked. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_nounwind] #[cfg_attr(not(bootstrap), rustc_intrinsic)] #[cfg_attr(bootstrap, inline)] +#[rustc_nounwind] pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { // const eval overrides this function, but runtime code should always just return null pointers. crate::ptr::null_mut() @@ -2686,11 +2714,11 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // (`transmute` also falls into this category, but it cannot be wrapped due to the // check that `T` and `U` have the same size.) -/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on, -/// and only at runtime. +/// Check that the preconditions of an unsafe function are followed, at runtime if debug assertions +/// are enabled, and in const-eval/Miri if the precondition is for library UB. /// /// This macro should be called as -/// `assert_unsafe_precondition!((expr => name: Type, expr => name: Type) => Expression)` +/// `assert_unsafe_precondition!(check_{library,lang}_ub, (expr => name: Type, expr => name: Type) => Expression)` /// where each `expr` will be evaluated and passed in as function argument `name: Type`. Then all /// those arguments are passed to a function via [`const_eval_select`]. /// @@ -2701,31 +2729,25 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) /// this macro, that monomorphization will contain the check. /// /// Since these checks cannot be optimized out in MIR, some care must be taken in both call and -/// implementation to mitigate their compile-time overhead. The runtime function that we -/// [`const_eval_select`] to is monomorphic, `#[inline(never)]`, and `#[rustc_nounwind]`. That -/// combination of properties ensures that the code for the checks is only compiled once, and has a -/// minimal impact on the caller's code size. +/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to +/// this structure: +/// ```ignore (pseudocode) +/// if ::core::intrinsics::check_language_ub() { +/// precondition_check(args) +/// } +/// ``` +/// where `precondition_check` is monomorphic, and `#[rustc_nounwind]` but `#[inline]` and +/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is +/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in +/// MIR, but *can* be inlined and fully optimized by a codegen backend. /// -/// Callers should also avoid introducing any other `let` bindings or any code outside this macro in +/// Callers should avoid introducing any other `let` bindings or any code outside this macro in /// order to call it. Since the precompiled standard library is built with full debuginfo and these /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough /// debuginfo to have a measurable compile-time impact on debug builds. -/// -/// # Safety -/// -/// Invoking this macro is only sound if the following code is already UB when the passed -/// expression evaluates to false. -/// -/// This macro expands to a check at runtime if debug_assertions is set. It has no effect at -/// compile time, but the semantics of the contained `const_eval_select` must be the same at -/// runtime and at compile time. Thus if the expression evaluates to false, this macro produces -/// different behavior at compile time and at runtime, and invoking it is incorrect. -/// -/// So in a sense it is UB if this macro is useful, but we expect callers of `unsafe fn` to make -/// the occasional mistake, and this check should help them figure things out. -#[allow_internal_unstable(const_eval_select, delayed_debug_assertions)] // permit this to be called in stably-const fn +#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn macro_rules! assert_unsafe_precondition { - ($message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { + ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { { // #[cfg(bootstrap)] (this comment) // When the standard library is compiled with debug assertions, we want the check to inline for better performance. @@ -2747,18 +2769,16 @@ macro_rules! assert_unsafe_precondition { #[cfg_attr(not(bootstrap), rustc_no_mir_inline)] #[cfg_attr(not(bootstrap), inline)] #[rustc_nounwind] - fn precondition_check($($name:$ty),*) { + #[rustc_const_unstable(feature = "ub_checks", issue = "none")] + const fn precondition_check(($($name,)*): ($($ty,)*)) { if !$e { ::core::panicking::panic_nounwind( concat!("unsafe precondition(s) violated: ", $message) ); } } - const fn comptime($(_:$ty),*) {} - if ::core::intrinsics::debug_assertions() { - ::core::intrinsics::const_eval_select(($($arg,)*), comptime, precondition_check); - } + ::core::intrinsics::$kind(precondition_check, ($($arg,)*)); } }; } @@ -2767,31 +2787,48 @@ pub(crate) use assert_unsafe_precondition; /// Checks whether `ptr` is properly aligned with respect to /// `align_of::()`. #[inline] -pub(crate) fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { +pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { !ptr.is_null() && ptr.is_aligned_to(align) } #[inline] -pub(crate) fn is_valid_allocation_size(size: usize, len: usize) -> bool { +pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; len <= max_len } /// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size` do *not* overlap. +/// `count * size` do *not* overlap. May spuriously return true. #[inline] -pub(crate) fn is_nonoverlapping(src: *const (), dst: *const (), size: usize, count: usize) -> bool { - let src_usize = src.addr(); - let dst_usize = dst.addr(); - let Some(size) = size.checked_mul(count) else { - crate::panicking::panic_nounwind( - "is_nonoverlapping: `size_of::() * count` overflows a usize", - ) - }; - let diff = src_usize.abs_diff(dst_usize); - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size +pub(crate) const fn is_nonoverlapping( + src: *const (), + dst: *const (), + size: usize, + count: usize, +) -> bool { + #[inline] + fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { + let src_usize = src.addr(); + let dst_usize = dst.addr(); + let Some(size) = size.checked_mul(count) else { + crate::panicking::panic_nounwind( + "is_nonoverlapping: `size_of::() * count` overflows a usize", + ) + }; + let diff = src_usize.abs_diff(dst_usize); + // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // they do not overlap. + diff >= size + } + + #[inline] + const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { + true + } + + // SAFETY: comptime always returns true, but this function documents that it spuriously returns + // true. + unsafe { const_eval_select((src, dst, size, count), comptime, runtime) } } /// Copies `count * size_of::()` bytes from `src` to `dst`. The source @@ -2896,6 +2933,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us // upheld by the caller. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", ( @@ -2997,6 +3035,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", ( @@ -3077,6 +3116,7 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::write_bytes requires that the destination pointer is aligned and non-null", ( addr: *const () = dst as *const (), diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 295b88361cfbb..07c3b44a59b60 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -324,8 +324,9 @@ where // SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable. unsafe { intrinsics::assert_unsafe_precondition!( - "NonZero::new_unchecked requires the argument to be non-zero", - () => false, + check_language_ub, + "NonZero::new_unchecked requires the argument to be non-zero", + () => false, ); intrinsics::unreachable() } @@ -363,8 +364,9 @@ where // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable. unsafe { intrinsics::assert_unsafe_precondition!( - "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", - () => false, + check_language_ub, + "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", + () => false, ); intrinsics::unreachable() } diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 07ea2e930d57a..b28d88fa5bfc2 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,4 +1,4 @@ -use crate::intrinsics::{unchecked_add, unchecked_sub}; +use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; @@ -19,9 +19,10 @@ impl IndexRange { /// - `start <= end` #[inline] pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { - crate::panic::debug_assert_nounwind!( - start <= end, - "IndexRange::new_unchecked requires `start <= end`" + assert_unsafe_precondition!( + check_library_ub, + "IndexRange::new_unchecked requires `start <= end`", + (start: usize = start, end: usize = end) => start <= end, ); IndexRange { start, end } } diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 2bd42a4a8cadc..b520efe93f90d 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -139,43 +139,6 @@ pub macro unreachable_2021 { ), } -/// Like `assert_unsafe_precondition!` the defining features of this macro are that its -/// checks are enabled when they are monomorphized with debug assertions enabled, and upon failure -/// a non-unwinding panic is launched so that failures cannot compromise unwind safety. -/// -/// But there are many differences from `assert_unsafe_precondition!`. This macro does not use -/// `const_eval_select` internally, and therefore it is sound to call this with an expression -/// that evaluates to `false`. Also unlike `assert_unsafe_precondition!` the condition being -/// checked here is not put in an outlined function. If the check compiles to a lot of IR, this -/// can cause code bloat if the check is monomorphized many times. But it also means that the checks -/// from this macro can be deduplicated or otherwise optimized out. -/// -/// In general, this macro should be used to check all public-facing preconditions. But some -/// preconditions may be called too often or instantiated too often to make the overhead of the -/// checks tolerable. In such cases, place `#[cfg(debug_assertions)]` on the macro call. That will -/// disable the check in our precompiled standard library, but if a user wishes, they can still -/// enable the check by recompiling the standard library with debug assertions enabled. -#[doc(hidden)] -#[unstable(feature = "panic_internals", issue = "none")] -#[allow_internal_unstable(panic_internals, delayed_debug_assertions)] -#[rustc_macro_transparency = "semitransparent"] -pub macro debug_assert_nounwind { - ($cond:expr $(,)?) => { - if $crate::intrinsics::debug_assertions() { - if !$cond { - $crate::panicking::panic_nounwind($crate::concat!("assertion failed: ", $crate::stringify!($cond))); - } - } - }, - ($cond:expr, $message:expr) => { - if $crate::intrinsics::debug_assertions() { - if !$cond { - $crate::panicking::panic_nounwind($message); - } - } - }, -} - /// An internal trait used by std to pass data from std to `panic_unwind` and /// other panic runtimes. Not intended to be stabilized any time soon, do not /// use. diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index bd58c167c2e06..609482070e5af 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,4 +1,6 @@ use crate::convert::{TryFrom, TryInto}; +#[cfg(debug_assertions)] +use crate::intrinsics::assert_unsafe_precondition; use crate::num::NonZero; use crate::{cmp, fmt, hash, mem, num}; @@ -77,9 +79,10 @@ impl Alignment { #[inline] pub const unsafe fn new_unchecked(align: usize) -> Self { #[cfg(debug_assertions)] - crate::panic::debug_assert_nounwind!( - align.is_power_of_two(), - "Alignment::new_unchecked requires a power of two" + assert_unsafe_precondition!( + check_language_ub, + "Alignment::new_unchecked requires a power of two", + (align: usize = align) => align.is_power_of_two() ); // SAFETY: By precondition, this must be a power of two, and diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 85a56d37ab75c..e2448189a3346 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -806,17 +806,26 @@ impl *const T { where T: Sized, { - // SAFETY: The comparison has no side-effects, and the intrinsic - // does this check internally in the CTFE implementation. - unsafe { - assert_unsafe_precondition!( - "ptr::sub_ptr requires `self >= origin`", - ( - this: *const () = self as *const (), - origin: *const () = origin as *const (), - ) => this >= origin - ) - }; + /// `this >= origin`, but may spuriously return true. + const fn maybe_ptr_ge(this: *const (), origin: *const ()) -> bool { + fn runtime(this: *const (), origin: *const ()) -> bool { + this >= origin + } + const fn comptime(_: *const (), _: *const ()) -> bool { + true + } + // SAFETY: Function is allowed to spuriously return true + unsafe { intrinsics::const_eval_select((this, origin), comptime, runtime) } + } + + assert_unsafe_precondition!( + check_language_ub, + "ptr::sub_ptr requires `self >= origin`", + ( + this: *const () = self as *const (), + origin: *const () = origin as *const (), + ) => maybe_ptr_ge(this, origin) + ); let pointee_size = mem::size_of::(); assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index fc5b08c9801a8..9aa6668175b14 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -991,24 +991,21 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { }; } - // SAFETY: the caller must guarantee that `x` and `y` are - // valid for writes and properly aligned. - unsafe { - assert_unsafe_precondition!( - "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", - ( - x: *mut () = x as *mut (), - y: *mut () = y as *mut (), - size: usize = size_of::(), - align: usize = align_of::(), - count: usize = count, - ) => - is_aligned_and_not_null(x, align) - && is_aligned_and_not_null(y, align) - && is_nonoverlapping(x, y, size, count) - ); - } + assert_unsafe_precondition!( + check_language_ub, + "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ + and the specified memory ranges do not overlap", + ( + x: *mut () = x as *mut (), + y: *mut () = y as *mut (), + size: usize = size_of::(), + align: usize = align_of::(), + count: usize = count, + ) => + is_aligned_and_not_null(x, align) + && is_aligned_and_not_null(y, align) + && is_nonoverlapping(x, y, size, count) + ); // Split up the slice into small power-of-two-sized chunks that LLVM is able // to vectorize (unless it's a special type with more-than-pointer alignment, @@ -1096,6 +1093,7 @@ pub const unsafe fn replace(dst: *mut T, mut src: T) -> T { // allocated object. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::replace requires that the pointer argument is aligned and non-null", ( addr: *const () = dst as *const (), @@ -1248,6 +1246,7 @@ pub const unsafe fn read(src: *const T) -> T { unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) assert_unsafe_precondition!( + check_language_ub, "ptr::read requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), @@ -1456,6 +1455,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) assert_unsafe_precondition!( + check_language_ub, "ptr::write requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), @@ -1627,6 +1627,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::read_volatile requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), @@ -1705,6 +1706,7 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { assert_unsafe_precondition!( + check_language_ub, "ptr::write_volatile requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index acb8c552a6338..9c0236c172a93 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -218,6 +218,7 @@ impl NonNull { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { assert_unsafe_precondition!( + check_language_ub, "NonNull::new_unchecked requires that the pointer is non-null", (ptr: *mut () = ptr as *mut ()) => !ptr.is_null() ); diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 0e2d45c4ada6d..3af8386bffb50 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,9 +1,9 @@ //! Indexing implementations for `[T]`. +use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::const_eval_select; use crate::intrinsics::unchecked_sub; use crate::ops; -use crate::panic::debug_assert_nounwind; use crate::ptr; #[stable(feature = "rust1", since = "1.0.0")] @@ -225,9 +225,10 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { - debug_assert_nounwind!( - self < slice.len(), - "slice::get_unchecked requires that the index is within the slice" + assert_unsafe_precondition!( + check_language_ub, + "slice::get_unchecked requires that the index is within the slice", + (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that @@ -243,9 +244,10 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { - debug_assert_nounwind!( - self < slice.len(), - "slice::get_unchecked_mut requires that the index is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked_mut requires that the index is within the slice", + (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: see comments for `get_unchecked` above. unsafe { slice.as_mut_ptr().add(self) } @@ -292,9 +294,10 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - debug_assert_nounwind!( - self.end() <= slice.len(), - "slice::get_unchecked requires that the index is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked requires that the index is within the slice", + (end: usize = self.end(), len: usize = slice.len()) => end <= len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that @@ -305,9 +308,10 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - debug_assert_nounwind!( - self.end() <= slice.len(), - "slice::get_unchecked_mut requires that the index is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked_mut requires that the index is within the slice", + (end: usize = self.end(), len: usize = slice.len()) => end <= len ); // SAFETY: see comments for `get_unchecked` above. @@ -362,9 +366,14 @@ unsafe impl SliceIndex<[T]> for ops::Range { #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { - debug_assert_nounwind!( - self.end >= self.start && self.end <= slice.len(), - "slice::get_unchecked requires that the range is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked requires that the range is within the slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it @@ -379,9 +388,14 @@ unsafe impl SliceIndex<[T]> for ops::Range { #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { - debug_assert_nounwind!( - self.end >= self.start && self.end <= slice.len(), - "slice::get_unchecked_mut requires that the range is within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::get_unchecked_mut requires that the range is within the slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len ); // SAFETY: see comments for `get_unchecked` above. unsafe { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1ad81fcfcfeed..f2a43fd8af21a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -9,11 +9,11 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; use crate::hint; +use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::exact_div; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; -use crate::panic::debug_assert_nounwind; use crate::ptr; use crate::simd::{self, Simd}; use crate::slice; @@ -945,9 +945,14 @@ impl [T] { #[unstable(feature = "slice_swap_unchecked", issue = "88539")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { - debug_assert_nounwind!( - a < self.len() && b < self.len(), - "slice::swap_unchecked requires that the indices are within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::swap_unchecked requires that the indices are within the slice", + ( + len: usize = self.len(), + a: usize = a, + b: usize = b, + ) => a < len && b < len, ); let ptr = self.as_mut_ptr(); @@ -1285,9 +1290,10 @@ impl [T] { #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { - debug_assert_nounwind!( - N != 0 && self.len() % N == 0, - "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks" + assert_unsafe_precondition!( + check_language_ub, + "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", + (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0, ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { exact_div(self.len(), N) }; @@ -1439,9 +1445,10 @@ impl [T] { #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { - debug_assert_nounwind!( - N != 0 && self.len() % N == 0, - "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks" + assert_unsafe_precondition!( + check_language_ub, + "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", + (n: usize = N, len: usize = self.len()) => n != 0 && len % n == 0 ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length let new_len = unsafe { exact_div(self.len(), N) }; @@ -1971,9 +1978,10 @@ impl [T] { let len = self.len(); let ptr = self.as_ptr(); - debug_assert_nounwind!( - mid <= len, - "slice::split_at_unchecked requires the index to be within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::split_at_unchecked requires the index to be within the slice", + (mid: usize = mid, len: usize = len) => mid <= len, ); // SAFETY: Caller has to check that `0 <= mid <= self.len()` @@ -2021,9 +2029,10 @@ impl [T] { let len = self.len(); let ptr = self.as_mut_ptr(); - debug_assert_nounwind!( - mid <= len, - "slice::split_at_mut_unchecked requires the index to be within the slice" + assert_unsafe_precondition!( + check_library_ub, + "slice::split_at_mut_unchecked requires the index to be within the slice", + (mid: usize = mid, len: usize = len) => mid <= len, ); // SAFETY: Caller has to check that `0 <= mid <= self.len()`. diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 571abc3e99907..2199614ce27e4 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -96,6 +96,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { assert_unsafe_precondition!( + check_language_ub, "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( data: *mut () = data as *mut (), @@ -149,6 +150,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { assert_unsafe_precondition!( + check_language_ub, "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( data: *mut () = data as *mut (), diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 3b9e9c9812719..ec81fd095d530 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,8 +1,8 @@ //! Trait implementations for `str`. use crate::cmp::Ordering; +use crate::intrinsics::assert_unsafe_precondition; use crate::ops; -use crate::panic::debug_assert_nounwind; use crate::ptr; use crate::slice::SliceIndex; @@ -192,15 +192,20 @@ unsafe impl SliceIndex for ops::Range { unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { let slice = slice as *const [u8]; - debug_assert_nounwind!( + assert_unsafe_precondition!( // We'd like to check that the bounds are on char boundaries, // but there's not really a way to do so without reading // behind the pointer, which has aliasing implications. // It's also not possible to move this check up to // `str::get_unchecked` without adding a special function // to `SliceIndex` just for this. - self.end >= self.start && self.end <= slice.len(), - "str::get_unchecked requires that the range is within the string slice" + check_library_ub, + "str::get_unchecked requires that the range is within the string slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len, ); // SAFETY: the caller guarantees that `self` is in bounds of `slice` @@ -213,9 +218,14 @@ unsafe impl SliceIndex for ops::Range { unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { let slice = slice as *mut [u8]; - debug_assert_nounwind!( - self.end >= self.start && self.end <= slice.len(), - "str::get_unchecked_mut requires that the range is within the string slice" + assert_unsafe_precondition!( + check_library_ub, + "str::get_unchecked_mut requires that the range is within the string slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len, ); // SAFETY: see comments for `get_unchecked`. 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 183dbe3aecc16..dd64b60f64f55 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 @@ -174,7 +174,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); @@ -221,6 +221,13 @@ fn check_statement<'tcx>( check_operand(tcx, src, span, body)?; check_operand(tcx, count, span, body) }, + StatementKind::Intrinsic(box NonDivergingIntrinsic::UbCheck { + func, args, .. + }) => { + check_operand(tcx, func, span, body)?; + check_operand(tcx, args, span, body) + }, + // These are all NOPs StatementKind::StorageLive(_) | StatementKind::StorageDead(_) diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff index e6b8d5e6c21bc..821dfdb029d8b 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -59,27 +57,15 @@ _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2: { - _10 = const {0x1 as *mut ()}; - _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable]; - } - - bb3: { + StorageLive(_9); + _9 = const {0x1 as *mut ()}; + _8 = const ({0x1 as *mut ()},); + StorageDead(_9); + UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); - _11 = const {0x1 as *const [bool; 0]}; + _10 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; @@ -87,13 +73,17 @@ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; StorageDead(_4); _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; StorageDead(_2); _0 = const (); drop(_1) -> [return: bb1, unwind unreachable]; } + + bb1: { + StorageDead(_1); + return; + } } ALLOC2 (size: 8, align: 4) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff index bd74591018bc7..abe09271b6157 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -59,31 +57,15 @@ _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2 (cleanup): { - resume; - } - - bb3: { - _10 = const {0x1 as *mut ()}; - _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable]; - } - - bb4: { + StorageLive(_9); + _9 = const {0x1 as *mut ()}; + _8 = const ({0x1 as *mut ()},); + StorageDead(_9); + UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); - _11 = const {0x1 as *const [bool; 0]}; + _10 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; @@ -91,13 +73,21 @@ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; StorageDead(_4); _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; StorageDead(_2); _0 = const (); drop(_1) -> [return: bb1, unwind: bb2]; } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } } ALLOC2 (size: 8, align: 4) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff index fdbb0b2df03ae..02d24e8645f4c 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -59,27 +57,15 @@ _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2: { - _10 = const {0x1 as *mut ()}; - _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable]; - } - - bb3: { + StorageLive(_9); + _9 = const {0x1 as *mut ()}; + _8 = const ({0x1 as *mut ()},); + StorageDead(_9); + UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); - _11 = const {0x1 as *const [bool; 0]}; + _10 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; @@ -87,13 +73,17 @@ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; StorageDead(_4); _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; StorageDead(_2); _0 = const (); drop(_1) -> [return: bb1, unwind unreachable]; } + + bb1: { + StorageDead(_1); + return; + } } ALLOC2 (size: 16, align: 8) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff index d6b5984b81dd6..d5e54f6cc6e38 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -59,31 +57,15 @@ _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2 (cleanup): { - resume; - } - - bb3: { - _10 = const {0x1 as *mut ()}; - _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable]; - } - - bb4: { + StorageLive(_9); + _9 = const {0x1 as *mut ()}; + _8 = const ({0x1 as *mut ()},); + StorageDead(_9); + UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); - _11 = const {0x1 as *const [bool; 0]}; + _10 = const {0x1 as *const [bool; 0]}; _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); _4 = const Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }}; @@ -91,13 +73,21 @@ _3 = const Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}; StorageDead(_4); _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; StorageDead(_2); _0 = const (); drop(_1) -> [return: bb1, unwind: bb2]; } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } } ALLOC2 (size: 16, align: 8) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index c7445aaee6c55..d49b283cfb6fd 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -61,31 +59,20 @@ + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2: { -- _10 = _6 as *mut () (PtrToPtr); -- _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable]; -+ _10 = const {0x1 as *mut ()}; -+ _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable]; - } - - bb3: { + StorageLive(_9); +- _9 = _6 as *mut () (PtrToPtr); +- _8 = (move _9,); ++ _9 = const {0x1 as *mut ()}; ++ _8 = const ({0x1 as *mut ()},); + StorageDead(_9); +- UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(move _8); ++ UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); -- _5 = NonNull::<[bool; 0]> { pointer: _11 }; -+ _11 = const {0x1 as *const [bool; 0]}; +- _10 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _10 }; ++ _10 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; @@ -96,7 +83,6 @@ StorageDead(_4); - _2 = Box::<[bool]>(_3, const std::alloc::Global); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); - _1 = A { foo: move _2 }; + _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; @@ -104,6 +90,11 @@ _0 = const (); drop(_1) -> [return: bb1, unwind unreachable]; } + + bb1: { + StorageDead(_1); + return; + } + } + + ALLOC2 (size: 8, align: 4) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index b8e961bc08750..d45cf916bee06 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -61,35 +59,20 @@ + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2 (cleanup): { - resume; - } - - bb3: { -- _10 = _6 as *mut () (PtrToPtr); -- _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb4, unwind unreachable]; -+ _10 = const {0x1 as *mut ()}; -+ _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable]; - } - - bb4: { + StorageLive(_9); +- _9 = _6 as *mut () (PtrToPtr); +- _8 = (move _9,); ++ _9 = const {0x1 as *mut ()}; ++ _8 = const ({0x1 as *mut ()},); + StorageDead(_9); +- UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(move _8); ++ UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); -- _5 = NonNull::<[bool; 0]> { pointer: _11 }; -+ _11 = const {0x1 as *const [bool; 0]}; +- _10 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _10 }; ++ _10 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; @@ -100,7 +83,6 @@ StorageDead(_4); - _2 = Box::<[bool]>(_3, const std::alloc::Global); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); - _1 = A { foo: move _2 }; + _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; @@ -108,6 +90,15 @@ _0 = const (); drop(_1) -> [return: bb1, unwind: bb2]; } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } + } + + ALLOC2 (size: 8, align: 4) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index 9678db90d0590..071082163530a 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -61,31 +59,20 @@ + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb3, otherwise: bb2]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2: { -- _10 = _6 as *mut () (PtrToPtr); -- _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb3, unwind unreachable]; -+ _10 = const {0x1 as *mut ()}; -+ _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb3, unwind unreachable]; - } - - bb3: { + StorageLive(_9); +- _9 = _6 as *mut () (PtrToPtr); +- _8 = (move _9,); ++ _9 = const {0x1 as *mut ()}; ++ _8 = const ({0x1 as *mut ()},); + StorageDead(_9); +- UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(move _8); ++ UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); -- _5 = NonNull::<[bool; 0]> { pointer: _11 }; -+ _11 = const {0x1 as *const [bool; 0]}; +- _10 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _10 }; ++ _10 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; @@ -96,7 +83,6 @@ StorageDead(_4); - _2 = Box::<[bool]>(_3, const std::alloc::Global); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); - _1 = A { foo: move _2 }; + _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; @@ -104,6 +90,11 @@ _0 = const (); drop(_1) -> [return: bb1, unwind unreachable]; } + + bb1: { + StorageDead(_1); + return; + } + } + + ALLOC2 (size: 16, align: 8) { diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 8aa6c9c23e9a4..7ef0e44ba0570 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -23,10 +23,9 @@ debug ptr => _6; scope 12 (inlined NonNull::<[bool; 0]>::new_unchecked) { debug ptr => _6; - let mut _8: bool; - let _9: (); - let mut _10: *mut (); - let mut _11: *const [bool; 0]; + let mut _8: (*mut (),); + let mut _9: *mut (); + let mut _10: *const [bool; 0]; scope 13 { } } @@ -50,7 +49,6 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - StorageLive(_9); StorageLive(_4); StorageLive(_5); StorageLive(_6); @@ -61,35 +59,20 @@ + _6 = const {0x1 as *mut [bool; 0]}; StorageDead(_7); StorageLive(_10); - StorageLive(_11); StorageLive(_8); - _8 = cfg!(debug_assertions); - switchInt(move _8) -> [0: bb4, otherwise: bb3]; - } - - bb1: { - StorageDead(_1); - return; - } - - bb2 (cleanup): { - resume; - } - - bb3: { -- _10 = _6 as *mut () (PtrToPtr); -- _9 = NonNull::::new_unchecked::precondition_check(move _10) -> [return: bb4, unwind unreachable]; -+ _10 = const {0x1 as *mut ()}; -+ _9 = NonNull::::new_unchecked::precondition_check(const {0x1 as *mut ()}) -> [return: bb4, unwind unreachable]; - } - - bb4: { + StorageLive(_9); +- _9 = _6 as *mut () (PtrToPtr); +- _8 = (move _9,); ++ _9 = const {0x1 as *mut ()}; ++ _8 = const ({0x1 as *mut ()},); + StorageDead(_9); +- UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(move _8); ++ UbCheck(LanguageUb): NonNull::::new_unchecked::precondition_check(const ({0x1 as *mut ()},)); StorageDead(_8); -- _11 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); -- _5 = NonNull::<[bool; 0]> { pointer: _11 }; -+ _11 = const {0x1 as *const [bool; 0]}; +- _10 = _6 as *const [bool; 0] (PointerCoercion(MutToConstPointer)); +- _5 = NonNull::<[bool; 0]> { pointer: _10 }; ++ _10 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; - StorageDead(_11); StorageDead(_10); StorageDead(_6); - _4 = Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> }; @@ -100,7 +83,6 @@ StorageDead(_4); - _2 = Box::<[bool]>(_3, const std::alloc::Global); + _2 = const Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global); - StorageDead(_9); StorageDead(_3); - _1 = A { foo: move _2 }; + _1 = const A {{ foo: Box::<[bool]>(Unique::<[bool]> {{ pointer: NonNull::<[bool]> {{ pointer: Indirect { alloc_id: ALLOC2, offset: Size(0 bytes) }: *const [bool] }}, _marker: PhantomData::<[bool]> }}, std::alloc::Global) }}; @@ -108,6 +90,15 @@ _0 = const (); drop(_1) -> [return: bb1, unwind: bb2]; } + + bb1: { + StorageDead(_1); + return; + } + + bb2 (cleanup): { + resume; + } + } + + ALLOC2 (size: 16, align: 8) { diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index 52688c2e86729..cdff53e71c668 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -8,13 +8,12 @@ + scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { + debug self => _2; + let mut _3: isize; ++ let mut _4: bool; + scope 2 { + debug val => _0; + } + scope 3 { + scope 4 (inlined unreachable_unchecked) { -+ let mut _4: bool; -+ let _5: (); + scope 5 { + } + } @@ -25,26 +24,16 @@ StorageLive(_2); _2 = move _1; - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + StorageLive(_3); -+ StorageLive(_5); -+ _3 = discriminant(_2); -+ switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1]; - } - - bb1: { -+ unreachable; -+ } -+ -+ bb2: { + StorageLive(_4); -+ _4 = cfg!(debug_assertions); -+ assume(_4); -+ _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; -+ } -+ -+ bb3: { ++ _3 = discriminant(_2); ++ _4 = Eq(_3, const 1_isize); ++ assume(move _4); + _0 = move ((_2 as Some).0: T); -+ StorageDead(_5); ++ StorageDead(_4); + StorageDead(_3); StorageDead(_2); return; diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index fd83f1cb33152..bab5fc4539e60 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -8,13 +8,12 @@ + scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { + debug self => _2; + let mut _3: isize; ++ let mut _4: bool; + scope 2 { + debug val => _0; + } + scope 3 { + scope 4 (inlined unreachable_unchecked) { -+ let mut _4: bool; -+ let _5: (); + scope 5 { + } + } @@ -25,33 +24,23 @@ StorageLive(_2); _2 = move _1; - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2]; +- } +- +- bb1: { + StorageLive(_3); -+ StorageLive(_5); -+ _3 = discriminant(_2); -+ switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1]; - } - - bb1: { -- StorageDead(_2); -- return; -+ unreachable; - } - -- bb2 (cleanup): { -- resume; -+ bb2: { + StorageLive(_4); -+ _4 = cfg!(debug_assertions); -+ assume(_4); -+ _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; -+ } -+ -+ bb3: { ++ _3 = discriminant(_2); ++ _4 = Eq(_3, const 1_isize); ++ assume(move _4); + _0 = move ((_2 as Some).0: T); -+ StorageDead(_5); ++ StorageDead(_4); + StorageDead(_3); -+ StorageDead(_2); -+ return; + StorageDead(_2); + return; +- } +- +- bb2 (cleanup): { +- resume; } } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 8ec65935c6612..3799119b5ef91 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -6,13 +6,12 @@ fn unwrap_unchecked(_1: Option) -> T { scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { debug self => _1; let mut _2: isize; + let mut _3: bool; scope 2 { debug val => _0; } scope 3 { scope 4 (inlined unreachable_unchecked) { - let mut _3: bool; - let _4: (); scope 5 { } } @@ -21,24 +20,13 @@ fn unwrap_unchecked(_1: Option) -> T { bb0: { StorageLive(_2); - _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; - } - - bb1: { StorageLive(_3); - _3 = cfg!(debug_assertions); - assume(_3); - _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; - } - - bb2: { + _2 = discriminant(_1); + _3 = Eq(_2, const 1_isize); + assume(move _3); _0 = ((_1 as Some).0: T); + StorageDead(_3); StorageDead(_2); return; } - - bb3: { - unreachable; - } } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 8ec65935c6612..3799119b5ef91 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -6,13 +6,12 @@ fn unwrap_unchecked(_1: Option) -> T { scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { debug self => _1; let mut _2: isize; + let mut _3: bool; scope 2 { debug val => _0; } scope 3 { scope 4 (inlined unreachable_unchecked) { - let mut _3: bool; - let _4: (); scope 5 { } } @@ -21,24 +20,13 @@ fn unwrap_unchecked(_1: Option) -> T { bb0: { StorageLive(_2); - _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; - } - - bb1: { StorageLive(_3); - _3 = cfg!(debug_assertions); - assume(_3); - _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; - } - - bb2: { + _2 = discriminant(_1); + _3 = Eq(_2, const 1_isize); + assume(move _3); _0 = ((_1 as Some).0: T); + StorageDead(_3); StorageDead(_2); return; } - - bb3: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index 7c038b0ee88e7..8a4b9be00540a 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -4,31 +4,17 @@ fn ub_if_b(_1: Thing) -> Thing { debug t => _1; let mut _0: Thing; let mut _2: isize; + let mut _3: bool; scope 1 (inlined unreachable_unchecked) { - let mut _3: bool; - let _4: (); scope 2 { } } bb0: { _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; - } - - bb1: { + _3 = Eq(_2, const 0_isize); + assume(move _3); _0 = move _1; return; } - - bb2: { - StorageLive(_3); - _3 = cfg!(debug_assertions); - assume(_3); - _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; - } - - bb3: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir index 153505b1bbb43..b8405ffd1d558 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-abort.mir @@ -7,13 +7,94 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { scope 1 (inlined core::slice::::get_mut::) { debug self => _1; debug index => _2; + scope 2 (inlined >::get_mut) { + debug self => _2; + debug slice => _1; + let mut _3: usize; + let mut _4: bool; + let mut _5: *mut [u32]; + let mut _11: *mut u32; + let mut _12: &mut u32; + scope 3 { + scope 4 (inlined >::get_unchecked_mut) { + debug self => _2; + debug slice => _5; + let mut _8: usize; + let mut _9: (usize, usize); + let mut _10: *mut u32; + scope 5 { + scope 9 (inlined std::ptr::mut_ptr::::as_mut_ptr) { + debug self => _5; + } + scope 10 (inlined std::ptr::mut_ptr::::add) { + debug self => _10; + debug count => _2; + scope 11 { + } + } + } + scope 6 (inlined std::ptr::mut_ptr::::len) { + debug self => _5; + let mut _6: *const [u32]; + scope 7 (inlined std::ptr::metadata::<[u32]>) { + debug ptr => _6; + let mut _7: std::ptr::metadata::PtrRepr<[u32]>; + scope 8 { + } + } + } + } + } + } } bb0: { - _0 = >::get_mut(move _2, move _1) -> [return: bb1, unwind unreachable]; + StorageLive(_11); + StorageLive(_4); + StorageLive(_3); + _3 = Len((*_1)); + _4 = Lt(_2, move _3); + switchInt(move _4) -> [0: bb1, otherwise: bb2]; } bb1: { + StorageDead(_3); + _0 = const Option::<&mut u32>::None; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + StorageLive(_12); + StorageLive(_5); + _5 = &raw mut (*_1); + StorageLive(_9); + StorageLive(_8); + StorageLive(_6); + _6 = _5 as *const [u32] (PointerCoercion(MutToConstPointer)); + StorageLive(_7); + _7 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _6 }; + _8 = ((_7.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + StorageDead(_7); + StorageDead(_6); + _9 = (_2, move _8); + StorageDead(_8); + UbCheck(LibraryUb): >::get_unchecked_mut::precondition_check(move _9); + StorageDead(_9); + StorageLive(_10); + _10 = _5 as *mut u32 (PtrToPtr); + _11 = Offset(_10, _2); + StorageDead(_10); + StorageDead(_5); + _12 = &mut (*_11); + _0 = Option::<&mut u32>::Some(move _12); + StorageDead(_12); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_11); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir index d37ee783117d8..b8405ffd1d558 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir @@ -7,13 +7,94 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { scope 1 (inlined core::slice::::get_mut::) { debug self => _1; debug index => _2; + scope 2 (inlined >::get_mut) { + debug self => _2; + debug slice => _1; + let mut _3: usize; + let mut _4: bool; + let mut _5: *mut [u32]; + let mut _11: *mut u32; + let mut _12: &mut u32; + scope 3 { + scope 4 (inlined >::get_unchecked_mut) { + debug self => _2; + debug slice => _5; + let mut _8: usize; + let mut _9: (usize, usize); + let mut _10: *mut u32; + scope 5 { + scope 9 (inlined std::ptr::mut_ptr::::as_mut_ptr) { + debug self => _5; + } + scope 10 (inlined std::ptr::mut_ptr::::add) { + debug self => _10; + debug count => _2; + scope 11 { + } + } + } + scope 6 (inlined std::ptr::mut_ptr::::len) { + debug self => _5; + let mut _6: *const [u32]; + scope 7 (inlined std::ptr::metadata::<[u32]>) { + debug ptr => _6; + let mut _7: std::ptr::metadata::PtrRepr<[u32]>; + scope 8 { + } + } + } + } + } + } } bb0: { - _0 = >::get_mut(move _2, move _1) -> [return: bb1, unwind continue]; + StorageLive(_11); + StorageLive(_4); + StorageLive(_3); + _3 = Len((*_1)); + _4 = Lt(_2, move _3); + switchInt(move _4) -> [0: bb1, otherwise: bb2]; } bb1: { + StorageDead(_3); + _0 = const Option::<&mut u32>::None; + goto -> bb3; + } + + bb2: { + StorageDead(_3); + StorageLive(_12); + StorageLive(_5); + _5 = &raw mut (*_1); + StorageLive(_9); + StorageLive(_8); + StorageLive(_6); + _6 = _5 as *const [u32] (PointerCoercion(MutToConstPointer)); + StorageLive(_7); + _7 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _6 }; + _8 = ((_7.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + StorageDead(_7); + StorageDead(_6); + _9 = (_2, move _8); + StorageDead(_8); + UbCheck(LibraryUb): >::get_unchecked_mut::precondition_check(move _9); + StorageDead(_9); + StorageLive(_10); + _10 = _5 as *mut u32 (PtrToPtr); + _11 = Offset(_10, _2); + StorageDead(_10); + StorageDead(_5); + _12 = &mut (*_11); + _0 = Option::<&mut u32>::Some(move _12); + StorageDead(_12); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + StorageDead(_11); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir index bcc540ae6fc04..6fc50c38a0c72 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir @@ -4,24 +4,111 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> debug slice => _1; debug index => _2; let mut _0: &mut [u32]; + let mut _3: usize; + let mut _4: usize; scope 1 (inlined core::slice::::get_unchecked_mut::>) { debug self => _1; - debug index => _2; - let mut _3: *mut [u32]; - let mut _4: *mut [u32]; + debug ((index: std::ops::Range).0: usize) => _3; + debug ((index: std::ops::Range).1: usize) => _4; + let mut _5: *mut [u32]; + let mut _17: *mut [u32]; scope 2 { + scope 3 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { + debug ((self: std::ops::Range).0: usize) => _3; + debug ((self: std::ops::Range).1: usize) => _4; + debug slice => _5; + let mut _8: usize; + let mut _9: (usize, usize, usize); + let mut _11: *mut u32; + let mut _12: *mut u32; + scope 4 { + let _10: usize; + scope 5 { + debug new_len => _10; + scope 9 (inlined std::ptr::mut_ptr::::as_mut_ptr) { + debug self => _5; + } + scope 10 (inlined std::ptr::mut_ptr::::add) { + debug self => _11; + debug count => _3; + scope 11 { + } + } + scope 12 (inlined slice_from_raw_parts_mut::) { + debug data => _12; + debug len => _10; + let mut _13: *mut (); + scope 13 (inlined std::ptr::mut_ptr::::cast::<()>) { + debug self => _12; + } + scope 14 (inlined std::ptr::from_raw_parts_mut::<[u32]>) { + debug data_pointer => _13; + debug metadata => _10; + let mut _14: *const (); + let mut _15: std::ptr::metadata::PtrComponents<[u32]>; + let mut _16: std::ptr::metadata::PtrRepr<[u32]>; + scope 15 { + } + } + } + } + } + scope 6 (inlined std::ptr::mut_ptr::::len) { + debug self => _5; + let mut _6: *const [u32]; + scope 7 (inlined std::ptr::metadata::<[u32]>) { + debug ptr => _6; + let mut _7: std::ptr::metadata::PtrRepr<[u32]>; + scope 8 { + } + } + } + } } } bb0: { - StorageLive(_3); - _3 = &raw mut (*_1); - _4 = as SliceIndex<[u32]>>::get_unchecked_mut(move _2, move _3) -> [return: bb1, unwind unreachable]; - } - - bb1: { - StorageDead(_3); - _0 = &mut (*_4); + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = &raw mut (*_1); + StorageLive(_10); + StorageLive(_9); + StorageLive(_8); + StorageLive(_6); + _6 = _5 as *const [u32] (PointerCoercion(MutToConstPointer)); + StorageLive(_7); + _7 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _6 }; + _8 = ((_7.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + StorageDead(_7); + StorageDead(_6); + _9 = (_3, _4, move _8); + StorageDead(_8); + UbCheck(LibraryUb): as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(move _9); + StorageDead(_9); + _10 = SubUnchecked(_4, _3); + StorageLive(_12); + StorageLive(_11); + _11 = _5 as *mut u32 (PtrToPtr); + _12 = Offset(_11, _3); + StorageDead(_11); + StorageLive(_13); + _13 = _12 as *mut () (PtrToPtr); + StorageLive(_16); + StorageLive(_15); + StorageLive(_14); + _14 = _12 as *const () (PtrToPtr); + _15 = std::ptr::metadata::PtrComponents::<[u32]> { data_pointer: move _14, metadata: _10 }; + StorageDead(_14); + _16 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _15 }; + StorageDead(_15); + _17 = (_16.1: *mut [u32]); + StorageDead(_16); + StorageDead(_13); + StorageDead(_12); + StorageDead(_10); + StorageDead(_5); + _0 = &mut (*_17); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir index 1fe7da7d2fdcc..6fc50c38a0c72 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir @@ -4,24 +4,111 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range) -> debug slice => _1; debug index => _2; let mut _0: &mut [u32]; + let mut _3: usize; + let mut _4: usize; scope 1 (inlined core::slice::::get_unchecked_mut::>) { debug self => _1; - debug index => _2; - let mut _3: *mut [u32]; - let mut _4: *mut [u32]; + debug ((index: std::ops::Range).0: usize) => _3; + debug ((index: std::ops::Range).1: usize) => _4; + let mut _5: *mut [u32]; + let mut _17: *mut [u32]; scope 2 { + scope 3 (inlined as SliceIndex<[u32]>>::get_unchecked_mut) { + debug ((self: std::ops::Range).0: usize) => _3; + debug ((self: std::ops::Range).1: usize) => _4; + debug slice => _5; + let mut _8: usize; + let mut _9: (usize, usize, usize); + let mut _11: *mut u32; + let mut _12: *mut u32; + scope 4 { + let _10: usize; + scope 5 { + debug new_len => _10; + scope 9 (inlined std::ptr::mut_ptr::::as_mut_ptr) { + debug self => _5; + } + scope 10 (inlined std::ptr::mut_ptr::::add) { + debug self => _11; + debug count => _3; + scope 11 { + } + } + scope 12 (inlined slice_from_raw_parts_mut::) { + debug data => _12; + debug len => _10; + let mut _13: *mut (); + scope 13 (inlined std::ptr::mut_ptr::::cast::<()>) { + debug self => _12; + } + scope 14 (inlined std::ptr::from_raw_parts_mut::<[u32]>) { + debug data_pointer => _13; + debug metadata => _10; + let mut _14: *const (); + let mut _15: std::ptr::metadata::PtrComponents<[u32]>; + let mut _16: std::ptr::metadata::PtrRepr<[u32]>; + scope 15 { + } + } + } + } + } + scope 6 (inlined std::ptr::mut_ptr::::len) { + debug self => _5; + let mut _6: *const [u32]; + scope 7 (inlined std::ptr::metadata::<[u32]>) { + debug ptr => _6; + let mut _7: std::ptr::metadata::PtrRepr<[u32]>; + scope 8 { + } + } + } + } } } bb0: { - StorageLive(_3); - _3 = &raw mut (*_1); - _4 = as SliceIndex<[u32]>>::get_unchecked_mut(move _2, move _3) -> [return: bb1, unwind continue]; - } - - bb1: { - StorageDead(_3); - _0 = &mut (*_4); + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = &raw mut (*_1); + StorageLive(_10); + StorageLive(_9); + StorageLive(_8); + StorageLive(_6); + _6 = _5 as *const [u32] (PointerCoercion(MutToConstPointer)); + StorageLive(_7); + _7 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _6 }; + _8 = ((_7.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + StorageDead(_7); + StorageDead(_6); + _9 = (_3, _4, move _8); + StorageDead(_8); + UbCheck(LibraryUb): as SliceIndex<[T]>>::get_unchecked_mut::precondition_check(move _9); + StorageDead(_9); + _10 = SubUnchecked(_4, _3); + StorageLive(_12); + StorageLive(_11); + _11 = _5 as *mut u32 (PtrToPtr); + _12 = Offset(_11, _3); + StorageDead(_11); + StorageLive(_13); + _13 = _12 as *mut () (PtrToPtr); + StorageLive(_16); + StorageLive(_15); + StorageLive(_14); + _14 = _12 as *const () (PtrToPtr); + _15 = std::ptr::metadata::PtrComponents::<[u32]> { data_pointer: move _14, metadata: _10 }; + StorageDead(_14); + _16 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _15 }; + StorageDead(_15); + _17 = (_16.1: *mut [u32]); + StorageDead(_16); + StorageDead(_13); + StorageDead(_12); + StorageDead(_10); + StorageDead(_5); + _0 = &mut (*_17); return; } }