Constants of primitive types are always deterministic.

This commit is contained in:
Camille Gillot
2025-11-06 23:00:23 +00:00
parent 8c907860f7
commit 4953a8bc12
9 changed files with 74 additions and 97 deletions
+10 -10
View File
@@ -478,6 +478,11 @@ pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
/// Return true if any evaluation of this constant always returns the same value,
/// taking into account even pointer identity tests.
pub fn is_deterministic(&self) -> bool {
// Primitive types cannot contain provenance and always have the same value.
if self.ty().is_primitive() {
return true;
}
// Some constants may generate fresh allocations for pointers they contain,
// so using the same constant twice can yield two different results.
// Notably, valtrees purposefully generate new allocations.
@@ -487,24 +492,19 @@ pub fn is_deterministic(&self) -> bool {
// A valtree may be a reference. Valtree references correspond to a
// different allocation each time they are evaluated. Valtrees for primitive
// types are fine though.
ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
ty::ConstKind::Value(..)
| ty::ConstKind::Expr(..)
| ty::ConstKind::Unevaluated(..)
// This can happen if evaluation of a constant failed. The result does not matter
// much since compilation is doomed.
ty::ConstKind::Error(..) => false,
| ty::ConstKind::Error(..) => false,
// Should not appear in runtime MIR.
ty::ConstKind::Infer(..)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Placeholder(..) => bug!(),
},
Const::Unevaluated(..) => false,
Const::Val(
ConstValue::Slice { .. }
| ConstValue::ZeroSized
| ConstValue::Scalar(_)
| ConstValue::Indirect { .. },
_,
) => true,
Const::Val(..) => true,
}
}
}
@@ -6,7 +6,7 @@ fn drop_generic(_1: *mut Box<T>) -> () {
scope 1 (inlined drop_in_place::<Box<T>> - shim(Some(Box<T>))) {
scope 2 (inlined <Box<T> as Drop>::drop) {
let _2: std::ptr::NonNull<T>;
let _7: ();
let _5: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
@@ -24,7 +24,7 @@ fn drop_generic(_1: *mut Box<T>) -> () {
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _6: *mut u8;
let mut _4: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
@@ -44,8 +44,7 @@ fn drop_generic(_1: *mut Box<T>) -> () {
}
}
scope 7 (inlined Layout::for_value_raw::<T>) {
let mut _3: usize;
let mut _5: std::ptr::Alignment;
let mut _3: std::ptr::Alignment;
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
@@ -53,7 +52,6 @@ fn drop_generic(_1: *mut Box<T>) -> () {
scope 9 (inlined size_of_val_raw::<T>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<T>) {
let _4: usize;
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
@@ -72,26 +70,26 @@ fn drop_generic(_1: *mut Box<T>) -> () {
bb0: {
_2 = copy (((*_1).0: std::ptr::Unique<T>).0: std::ptr::NonNull<T>);
_3 = const <T as std::mem::SizedTypeProperties>::SIZE;
StorageLive(_4);
_4 = const <T as std::mem::SizedTypeProperties>::ALIGN;
_5 = copy _4 as std::ptr::Alignment (Transmute);
StorageDead(_4);
switchInt(copy _3) -> [0: bb3, otherwise: bb1];
_3 = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
switchInt(const <T as std::mem::SizedTypeProperties>::SIZE) -> [0: bb4, otherwise: bb1];
}
bb1: {
StorageLive(_6);
_6 = copy _2 as *mut u8 (Transmute);
_7 = alloc::alloc::__rust_dealloc(move _6, move _3, move _5) -> [return: bb2, unwind unreachable];
switchInt(const <T as std::mem::SizedTypeProperties>::SIZE) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageDead(_6);
goto -> bb3;
StorageLive(_4);
_4 = copy _2 as *mut u8 (Transmute);
_5 = alloc::alloc::__rust_dealloc(move _4, const <T as std::mem::SizedTypeProperties>::SIZE, move _3) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_4);
goto -> bb4;
}
bb4: {
return;
}
}
@@ -6,7 +6,7 @@ fn drop_generic(_1: *mut Box<T>) -> () {
scope 1 (inlined drop_in_place::<Box<T>> - shim(Some(Box<T>))) {
scope 2 (inlined <Box<T> as Drop>::drop) {
let _2: std::ptr::NonNull<T>;
let _7: ();
let _5: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
@@ -24,7 +24,7 @@ fn drop_generic(_1: *mut Box<T>) -> () {
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _6: *mut u8;
let mut _4: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
@@ -44,8 +44,7 @@ fn drop_generic(_1: *mut Box<T>) -> () {
}
}
scope 7 (inlined Layout::for_value_raw::<T>) {
let mut _3: usize;
let mut _5: std::ptr::Alignment;
let mut _3: std::ptr::Alignment;
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
@@ -53,7 +52,6 @@ fn drop_generic(_1: *mut Box<T>) -> () {
scope 9 (inlined size_of_val_raw::<T>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<T>) {
let _4: usize;
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
@@ -72,26 +70,26 @@ fn drop_generic(_1: *mut Box<T>) -> () {
bb0: {
_2 = copy (((*_1).0: std::ptr::Unique<T>).0: std::ptr::NonNull<T>);
_3 = const <T as std::mem::SizedTypeProperties>::SIZE;
StorageLive(_4);
_4 = const <T as std::mem::SizedTypeProperties>::ALIGN;
_5 = copy _4 as std::ptr::Alignment (Transmute);
StorageDead(_4);
switchInt(copy _3) -> [0: bb3, otherwise: bb1];
_3 = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
switchInt(const <T as std::mem::SizedTypeProperties>::SIZE) -> [0: bb4, otherwise: bb1];
}
bb1: {
StorageLive(_6);
_6 = copy _2 as *mut u8 (Transmute);
_7 = alloc::alloc::__rust_dealloc(move _6, move _3, move _5) -> [return: bb2, unwind unreachable];
switchInt(const <T as std::mem::SizedTypeProperties>::SIZE) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageDead(_6);
goto -> bb3;
StorageLive(_4);
_4 = copy _2 as *mut u8 (Transmute);
_5 = alloc::alloc::__rust_dealloc(move _4, const <T as std::mem::SizedTypeProperties>::SIZE, move _3) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_4);
goto -> bb4;
}
bb4: {
return;
}
}
@@ -6,10 +6,8 @@
// EMIT_MIR drop_box_of_sized.drop_generic.PreCodegen.after.mir
pub unsafe fn drop_generic<T: Copy>(x: *mut Box<T>) {
// CHECK-LABEL: fn drop_generic
// CHECK: [[SIZE:_.+]] = const <T as std::mem::SizedTypeProperties>::SIZE;
// CHECK: [[ALIGN:_.+]] = const <T as std::mem::SizedTypeProperties>::ALIGN;
// CHECK: [[ALIGNMENT:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute)
// CHECK: alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[ALIGNMENT]])
// CHECK: [[ALIGNMENT:_.+]] = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute)
// CHECK: alloc::alloc::__rust_dealloc({{.+}}, const <T as std::mem::SizedTypeProperties>::SIZE, move [[ALIGNMENT]])
std::ptr::drop_in_place(x)
}
@@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
let _2: std::ptr::NonNull<[T]>;
let mut _3: *mut [T];
let mut _4: *const [T];
let _9: ();
let _8: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
@@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _8: *mut u8;
let mut _7: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
@@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
scope 7 (inlined Layout::for_value_raw::<[T]>) {
let mut _5: usize;
let mut _7: std::ptr::Alignment;
let mut _6: std::ptr::Alignment;
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
@@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 9 (inlined size_of_val_raw::<[T]>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) {
let _6: usize;
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
@@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
bb1: {
StorageLive(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN;
_7 = copy _6 as std::ptr::Alignment (Transmute);
StorageDead(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
StorageDead(_4);
switchInt(copy _5) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageLive(_8);
_8 = copy _3 as *mut u8 (PtrToPtr);
_9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable];
StorageLive(_7);
_7 = copy _3 as *mut u8 (PtrToPtr);
_8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
goto -> bb4;
}
@@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
let _2: std::ptr::NonNull<[T]>;
let mut _3: *mut [T];
let mut _4: *const [T];
let _9: ();
let _8: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
@@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _8: *mut u8;
let mut _7: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
@@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
scope 7 (inlined Layout::for_value_raw::<[T]>) {
let mut _5: usize;
let mut _7: std::ptr::Alignment;
let mut _6: std::ptr::Alignment;
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
@@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 9 (inlined size_of_val_raw::<[T]>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) {
let _6: usize;
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
@@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
bb1: {
StorageLive(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN;
_7 = copy _6 as std::ptr::Alignment (Transmute);
StorageDead(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
StorageDead(_4);
switchInt(copy _5) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageLive(_8);
_8 = copy _3 as *mut u8 (PtrToPtr);
_9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable];
StorageLive(_7);
_7 = copy _3 as *mut u8 (PtrToPtr);
_8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
goto -> bb4;
}
@@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
let _2: std::ptr::NonNull<[T]>;
let mut _3: *mut [T];
let mut _4: *const [T];
let _9: ();
let _8: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
@@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _8: *mut u8;
let mut _7: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
@@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
scope 7 (inlined Layout::for_value_raw::<[T]>) {
let mut _5: usize;
let mut _7: std::ptr::Alignment;
let mut _6: std::ptr::Alignment;
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
@@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 9 (inlined size_of_val_raw::<[T]>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) {
let _6: usize;
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
@@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
bb1: {
StorageLive(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN;
_7 = copy _6 as std::ptr::Alignment (Transmute);
StorageDead(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
StorageDead(_4);
switchInt(copy _5) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageLive(_8);
_8 = copy _3 as *mut u8 (PtrToPtr);
_9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable];
StorageLive(_7);
_7 = copy _3 as *mut u8 (PtrToPtr);
_8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
goto -> bb4;
}
@@ -8,7 +8,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
let _2: std::ptr::NonNull<[T]>;
let mut _3: *mut [T];
let mut _4: *const [T];
let _9: ();
let _8: ();
scope 3 {
scope 4 {
scope 17 (inlined Layout::size) {
@@ -26,7 +26,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 23 (inlined <std::alloc::Global as Allocator>::deallocate) {
scope 24 (inlined std::alloc::Global::deallocate_impl) {
scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
let mut _8: *mut u8;
let mut _7: *mut u8;
scope 26 (inlined Layout::size) {
}
scope 27 (inlined NonNull::<u8>::as_ptr) {
@@ -47,7 +47,7 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
scope 7 (inlined Layout::for_value_raw::<[T]>) {
let mut _5: usize;
let mut _7: std::ptr::Alignment;
let mut _6: std::ptr::Alignment;
scope 8 {
scope 16 (inlined #[track_caller] Layout::from_size_alignment_unchecked) {
}
@@ -55,7 +55,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
scope 9 (inlined size_of_val_raw::<[T]>) {
}
scope 10 (inlined std::ptr::Alignment::of_val_raw::<[T]>) {
let _6: usize;
scope 11 {
scope 13 (inlined #[track_caller] std::ptr::Alignment::new_unchecked) {
scope 14 (inlined core::ub_checks::check_language_ub) {
@@ -82,22 +81,19 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
}
bb1: {
StorageLive(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN;
_7 = copy _6 as std::ptr::Alignment (Transmute);
StorageDead(_6);
_6 = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
StorageDead(_4);
switchInt(copy _5) -> [0: bb4, otherwise: bb2];
}
bb2: {
StorageLive(_8);
_8 = copy _3 as *mut u8 (PtrToPtr);
_9 = alloc::alloc::__rust_dealloc(move _8, move _5, move _7) -> [return: bb3, unwind unreachable];
StorageLive(_7);
_7 = copy _3 as *mut u8 (PtrToPtr);
_8 = alloc::alloc::__rust_dealloc(move _7, move _5, move _6) -> [return: bb3, unwind unreachable];
}
bb3: {
StorageDead(_8);
StorageDead(_7);
goto -> bb4;
}
@@ -9,8 +9,7 @@ pub unsafe fn generic_in_place<T: Copy>(ptr: *mut Box<[T]>) {
// CHECK-LABEL: fn generic_in_place(_1: *mut Box<[T]>)
// CHECK: (inlined <Box<[T]> as Drop>::drop)
// CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]>
// CHECK: [[ALIGN:_.+]] = const <T as std::mem::SizedTypeProperties>::ALIGN;
// CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute);
// CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[B]]) ->
// CHECK: [[ALIGN:_.+]] = const <T as std::mem::SizedTypeProperties>::ALIGN as std::ptr::Alignment (Transmute);
// CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[ALIGN]]) ->
std::ptr::drop_in_place(ptr)
}