diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index ed40901ac9b8b..31b37053cdb9f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -749,7 +749,10 @@ fn codegen_regular_intrinsic_call<'tcx>( let res = CValue::by_val(res, arg.layout()); ret.write_cvalue(fx, res); } - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { + sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid + | sym::assert_zst => { intrinsic_args!(fx, args => (); intrinsic); let ty = generic_args.type_at(0); @@ -777,6 +780,11 @@ fn codegen_regular_intrinsic_call<'tcx>( "attempted to zero-initialize type `{}`, which is invalid", ty ) + } else if intrinsic == sym::assert_zst { + format!( + "attempted to conjure type `{}` when it is not zero-sized", + ty + ) } else { format!( "attempted to leave type `{}` uninitialized, which is invalid", diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index befa00c6861ed..eef21ca1c999e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -114,6 +114,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid | sym::assert_inhabited + | sym::assert_zst | sym::ub_checks | sym::contract_checks | sym::atomic_fence diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fccb6b171b1c2..ae33ccb478446 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -557,7 +557,8 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // We handle these here since Miri does not want to have them. sym::assert_inhabited | sym::assert_zero_valid - | sym::assert_mem_uninitialized_valid => { + | sym::assert_mem_uninitialized_valid + | sym::assert_zst => { let ty = instance.args.type_at(0); let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap(); @@ -582,6 +583,9 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ValidityRequirement::UninitMitigated0x01Fill => format!( "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid" ), + ValidityRequirement::Zst => format!( + "aborted execution: attempted to conjure type `{ty}` when it is not zero-sized" + ), ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"), }; diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 1dea7e4252d7f..187d6eac6ac22 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -28,9 +28,11 @@ pub fn check_validity_requirement<'tcx>( ) -> Result> { let layout = tcx.layout_of(input)?; - // There is nothing strict or lax about inhabitedness. + // There is nothing strict or lax about inhabitedness, or being a ZST. if kind == ValidityRequirement::Inhabited { return Ok(!layout.is_uninhabited()); + } else if kind == ValidityRequirement::Zst { + return Ok(!layout.is_uninhabited() && layout.is_zst()); } let layout_cx = LayoutCx::new(tcx, input.typing_env); @@ -91,6 +93,9 @@ fn check_validity_requirement_lax<'tcx>( ValidityRequirement::Inhabited => { bug!("ValidityRequirement::Inhabited should have been handled above") } + ValidityRequirement::Zst => { + bug!("ValidityRequirement::Zst should have been handled above") + } ValidityRequirement::Zero => { // The range must contain 0. s.valid_range(cx).contains(0) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bc3448be5823e..1ff5b8f6711f9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -73,6 +73,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::assert_inhabited | sym::assert_mem_uninitialized_valid | sym::assert_zero_valid + | sym::assert_zst | sym::autodiff | sym::bitreverse | sym::black_box @@ -289,9 +290,10 @@ pub(crate) fn check_intrinsic_type( } sym::rustc_peek => (1, 0, vec![param(0)], param(0)), sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()), - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { - (1, 0, vec![], tcx.types.unit) - } + sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_mem_uninitialized_valid + | sym::assert_zst => (1, 0, vec![], tcx.types.unit), sym::forget => (1, 0, vec![param(0)], tcx.types.unit), sym::transmute | sym::transmute_unchecked => (2, 0, vec![param(0)], param(1)), sym::prefetch_read_data diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 507a25a432594..25cff61e295c0 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -194,6 +194,7 @@ pub enum ValidityRequirement { UninitMitigated0x01Fill, /// True uninitialized memory. Uninit, + Zst, } impl ValidityRequirement { @@ -202,6 +203,7 @@ impl ValidityRequirement { sym::assert_inhabited => Some(Self::Inhabited), sym::assert_zero_valid => Some(Self::Zero), sym::assert_mem_uninitialized_valid => Some(Self::UninitMitigated0x01Fill), + sym::assert_zst => Some(Self::Zst), _ => None, } } @@ -214,6 +216,7 @@ impl fmt::Display for ValidityRequirement { Self::Zero => f.write_str("allows being left zeroed"), Self::UninitMitigated0x01Fill => f.write_str("allows being filled with 0x01"), Self::Uninit => f.write_str("allows being left uninitialized"), + Self::Zst => f.write_str("is zero-sized"), } } } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 378d71cb1fcd3..073ec3f8d1232 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -945,10 +945,11 @@ fn visit_instance_use<'tcx>( collect_autodiff_fn(tcx, instance, intrinsic, output); if let Some(_requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { - // The intrinsics assert_inhabited, assert_zero_valid, and assert_mem_uninitialized_valid will - // be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any - // of those intrinsics, we need to include a mono item for panic_nounwind, else we may try to - // codegen a call to that function without generating code for the function itself. + // The intrinsics assert_inhabited, assert_zero_valid, assert_mem_uninitialized_valid, + // and assert_zst will be lowered in codegen to nothing or a call to panic_nounwind. + // So if we encounter any of those intrinsics, we need to include a mono item for + // panic_nounwind, else we may try to codegen a call to that function without generating + // code for the function itself. let def_id = tcx.require_lang_item(LangItem::PanicNounwind, source); let panic_instance = Instance::mono(tcx, def_id); if tcx.should_codegen_locally(panic_instance) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b34a64108e3b4..ca20238bf8927 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -488,6 +488,7 @@ symbols! { assert_ne_macro, assert_receiver_is_total_eq, assert_zero_valid, + assert_zst, asserting, associated_const_equality, associated_consts, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index cef700be9ea1f..d1c033e41c57b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -516,6 +516,16 @@ pub const fn assert_zero_valid(); #[rustc_intrinsic] pub const fn assert_mem_uninitialized_valid(); +/// A guard for `std::mem::conjure_zst`. This will statically either panic, or do nothing. It does +/// not *guarantee* to ever panic, and should only be called if an assertion failure will imply +/// language UB in the following code. +/// +/// This intrinsic does not have a stable counterpart. +#[rustc_intrinsic_const_stable_indirect] +#[rustc_nounwind] +#[rustc_intrinsic] +pub const fn assert_zst(); + /// Gets a reference to a static `Location` indicating where it was called. /// /// Note that, unlike most intrinsics, this is safe to call; diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index a80b939d84ea9..6c6ff232a016e 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -185,7 +185,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } - "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" => { + "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" | "assert_zst" => { // Make these a NOP, so we get the better Miri-native error messages. } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 40d76bf42e9e5..9790da1863067 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1111,7 +1111,7 @@ impl Evaluator<'_> { let ans = ptr + offset * size; destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) } - "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assume" => { + "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assert_zst" | "assume" => { // FIXME: We should actually implement these checks Ok(()) } diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.generic.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/intrinsic_asserts.generic.InstSimplify-after-simplifycfg.diff index d35844b21d160..7e23e44f987cd 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.generic.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.generic.InstSimplify-after-simplifycfg.diff @@ -6,6 +6,7 @@ let _1: (); let _2: (); let _3: (); + let _4: (); bb0: { StorageLive(_1); @@ -15,17 +16,23 @@ bb1: { StorageDead(_1); StorageLive(_2); - _2 = assert_zero_valid::() -> [return: bb2, unwind unreachable]; + _2 = assert_zst::() -> [return: bb2, unwind unreachable]; } bb2: { StorageDead(_2); StorageLive(_3); - _3 = assert_mem_uninitialized_valid::() -> [return: bb3, unwind unreachable]; + _3 = assert_zero_valid::() -> [return: bb3, unwind unreachable]; } bb3: { StorageDead(_3); + StorageLive(_4); + _4 = assert_mem_uninitialized_valid::() -> [return: bb4, unwind unreachable]; + } + + bb4: { + StorageDead(_4); _0 = const (); return; } diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.panics.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/intrinsic_asserts.panics.InstSimplify-after-simplifycfg.diff index 1b7aa124c36e1..153cc5adfb6f9 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.panics.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.panics.InstSimplify-after-simplifycfg.diff @@ -6,6 +6,7 @@ let _1: (); let _2: (); let _3: (); + let _4: (); bb0: { StorageLive(_1); @@ -16,19 +17,26 @@ bb1: { StorageDead(_1); StorageLive(_2); -- _2 = assert_zero_valid::<&u8>() -> [return: bb2, unwind unreachable]; -+ _2 = assert_zero_valid::<&u8>() -> unwind unreachable; +- _2 = assert_zst::<&()>() -> [return: bb2, unwind unreachable]; ++ _2 = assert_zst::<&()>() -> unwind unreachable; } bb2: { StorageDead(_2); StorageLive(_3); -- _3 = assert_mem_uninitialized_valid::<&u8>() -> [return: bb3, unwind unreachable]; -+ _3 = assert_mem_uninitialized_valid::<&u8>() -> unwind unreachable; +- _3 = assert_zero_valid::<&u8>() -> [return: bb3, unwind unreachable]; ++ _3 = assert_zero_valid::<&u8>() -> unwind unreachable; } bb3: { StorageDead(_3); + StorageLive(_4); +- _4 = assert_mem_uninitialized_valid::<&u8>() -> [return: bb4, unwind unreachable]; ++ _4 = assert_mem_uninitialized_valid::<&u8>() -> unwind unreachable; + } + + bb4: { + StorageDead(_4); _0 = const (); return; } diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff index 20e046d8e194b..7dbcc6749928f 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.removable.InstSimplify-after-simplifycfg.diff @@ -6,6 +6,7 @@ let _1: (); let _2: (); let _3: (); + let _4: (); bb0: { StorageLive(_1); @@ -16,19 +17,26 @@ bb1: { StorageDead(_1); StorageLive(_2); -- _2 = assert_zero_valid::() -> [return: bb2, unwind unreachable]; +- _2 = assert_zst::<()>() -> [return: bb2, unwind unreachable]; + goto -> bb2; } bb2: { StorageDead(_2); StorageLive(_3); -- _3 = assert_mem_uninitialized_valid::() -> [return: bb3, unwind unreachable]; +- _3 = assert_zero_valid::() -> [return: bb3, unwind unreachable]; + goto -> bb3; } bb3: { StorageDead(_3); + StorageLive(_4); +- _4 = assert_mem_uninitialized_valid::() -> [return: bb4, unwind unreachable]; ++ goto -> bb4; + } + + bb4: { + StorageDead(_4); _0 = const (); return; } diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.rs b/tests/mir-opt/instsimplify/intrinsic_asserts.rs index c71e08b9f1f75..e95ffdc32f2b4 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.rs +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.rs @@ -8,9 +8,11 @@ pub fn removable() { // CHECK-LABEL: fn removable( // CHECK-NOT: assert_inhabited + // CHECK-NOT: assert_zst // CHECK-NOT: assert_zero_valid // CHECK-NOT: assert_mem_uninitialized_valid core::intrinsics::assert_inhabited::<()>(); + core::intrinsics::assert_zst::<()>(); core::intrinsics::assert_zero_valid::(); core::intrinsics::assert_mem_uninitialized_valid::(); } @@ -22,9 +24,11 @@ enum Never {} pub fn panics() { // CHECK-LABEL: fn panics( // CHECK: assert_inhabited::() -> unwind + // CHECK: assert_zst::<&()>() -> unwind // CHECK: assert_zero_valid::<&u8>() -> unwind // CHECK: assert_mem_uninitialized_valid::<&u8>() -> unwind core::intrinsics::assert_inhabited::(); + core::intrinsics::assert_zst::<&()>(); core::intrinsics::assert_zero_valid::<&u8>(); core::intrinsics::assert_mem_uninitialized_valid::<&u8>(); } @@ -34,9 +38,11 @@ pub fn panics() { pub fn generic() { // CHECK-LABEL: fn generic( // CHECK: assert_inhabited::() -> [return: + // CHECK: assert_zst::() -> [return: // CHECK: assert_zero_valid::() -> [return: // CHECK: assert_mem_uninitialized_valid::() -> [return: core::intrinsics::assert_inhabited::(); + core::intrinsics::assert_zst::(); core::intrinsics::assert_zero_valid::(); core::intrinsics::assert_mem_uninitialized_valid::(); }