Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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"),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ pub fn check_validity_requirement<'tcx>(
) -> Result<bool, &'tcx LayoutError<'tcx>> {
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);
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ pub enum ValidityRequirement {
UninitMitigated0x01Fill,
/// True uninitialized memory.
Uninit,
Zst,
}

impl ValidityRequirement {
Expand All @@ -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,
}
}
Expand All @@ -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"),
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ symbols! {
assert_ne_macro,
assert_receiver_is_total_eq,
assert_zero_valid,
assert_zst,
asserting,
associated_const_equality,
associated_consts,
Expand Down
10 changes: 10 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,16 @@ pub const fn assert_zero_valid<T>();
#[rustc_intrinsic]
pub const fn assert_mem_uninitialized_valid<T>();

/// 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<T>();

/// Gets a reference to a static `Location` indicating where it was called.
///
/// Note that, unlike most intrinsics, this is safe to call;
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
let _1: ();
let _2: ();
let _3: ();
let _4: ();

bb0: {
StorageLive(_1);
Expand All @@ -15,17 +16,23 @@
bb1: {
StorageDead(_1);
StorageLive(_2);
_2 = assert_zero_valid::<T>() -> [return: bb2, unwind unreachable];
_2 = assert_zst::<T>() -> [return: bb2, unwind unreachable];
}

bb2: {
StorageDead(_2);
StorageLive(_3);
_3 = assert_mem_uninitialized_valid::<T>() -> [return: bb3, unwind unreachable];
_3 = assert_zero_valid::<T>() -> [return: bb3, unwind unreachable];
}

bb3: {
StorageDead(_3);
StorageLive(_4);
_4 = assert_mem_uninitialized_valid::<T>() -> [return: bb4, unwind unreachable];
}

bb4: {
StorageDead(_4);
_0 = const ();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
let _1: ();
let _2: ();
let _3: ();
let _4: ();

bb0: {
StorageLive(_1);
Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
let _1: ();
let _2: ();
let _3: ();
let _4: ();

bb0: {
StorageLive(_1);
Expand All @@ -16,19 +17,26 @@
bb1: {
StorageDead(_1);
StorageLive(_2);
- _2 = assert_zero_valid::<u8>() -> [return: bb2, unwind unreachable];
- _2 = assert_zst::<()>() -> [return: bb2, unwind unreachable];
+ goto -> bb2;
}

bb2: {
StorageDead(_2);
StorageLive(_3);
- _3 = assert_mem_uninitialized_valid::<u8>() -> [return: bb3, unwind unreachable];
- _3 = assert_zero_valid::<u8>() -> [return: bb3, unwind unreachable];
+ goto -> bb3;
}

bb3: {
StorageDead(_3);
StorageLive(_4);
- _4 = assert_mem_uninitialized_valid::<u8>() -> [return: bb4, unwind unreachable];
+ goto -> bb4;
}

bb4: {
StorageDead(_4);
_0 = const ();
return;
}
Expand Down
6 changes: 6 additions & 0 deletions tests/mir-opt/instsimplify/intrinsic_asserts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u8>();
core::intrinsics::assert_mem_uninitialized_valid::<u8>();
}
Expand All @@ -22,9 +24,11 @@ enum Never {}
pub fn panics() {
// CHECK-LABEL: fn panics(
// CHECK: assert_inhabited::<Never>() -> unwind
// CHECK: assert_zst::<&()>() -> unwind
// CHECK: assert_zero_valid::<&u8>() -> unwind
// CHECK: assert_mem_uninitialized_valid::<&u8>() -> unwind
core::intrinsics::assert_inhabited::<Never>();
core::intrinsics::assert_zst::<&()>();
core::intrinsics::assert_zero_valid::<&u8>();
core::intrinsics::assert_mem_uninitialized_valid::<&u8>();
}
Expand All @@ -34,9 +38,11 @@ pub fn panics() {
pub fn generic<T>() {
// CHECK-LABEL: fn generic(
// CHECK: assert_inhabited::<T>() -> [return:
// CHECK: assert_zst::<T>() -> [return:
// CHECK: assert_zero_valid::<T>() -> [return:
// CHECK: assert_mem_uninitialized_valid::<T>() -> [return:
core::intrinsics::assert_inhabited::<T>();
core::intrinsics::assert_zst::<T>();
core::intrinsics::assert_zero_valid::<T>();
core::intrinsics::assert_mem_uninitialized_valid::<T>();
}
Expand Down
Loading