From cba8a23d7cfc7bbadd4d2e107baf9156bc7b5845 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 6 Nov 2025 23:10:28 +0000 Subject: [PATCH 1/5] Try harder to evaluate constants. --- compiler/rustc_mir_transform/src/gvn.rs | 41 ++++++++++++------- .../boxes.main.GVN.panic-abort.diff | 3 +- .../boxes.main.GVN.panic-unwind.diff | 3 +- ...onential_common.GVN.32bit.panic-abort.diff | 4 +- ...nential_common.GVN.32bit.panic-unwind.diff | 4 +- ...onential_common.GVN.64bit.panic-abort.diff | 4 +- ...nential_common.GVN.64bit.panic-unwind.diff | 4 +- ...onst_eval_polymorphic.no_optimize.GVN.diff | 3 +- tests/mir-opt/gvn_const_eval_polymorphic.rs | 2 +- ...ecked_shl.PreCodegen.after.panic-abort.mir | 2 +- ...cked_shl.PreCodegen.after.panic-unwind.mir | 2 +- ...p_forward.PreCodegen.after.panic-abort.mir | 2 +- ..._forward.PreCodegen.after.panic-unwind.mir | 2 +- 13 files changed, 46 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 92c74e7fc2763..c876384a22c72 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1006,16 +1006,16 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { operand: &mut Operand<'tcx>, location: Location, ) -> Option { - match *operand { - Operand::Constant(ref constant) => Some(self.insert_constant(constant.const_)), + let value = match *operand { + Operand::Constant(ref constant) => self.insert_constant(constant.const_), Operand::Copy(ref mut place) | Operand::Move(ref mut place) => { - let value = self.simplify_place_value(place, location)?; - if let Some(const_) = self.try_as_constant(value) { - *operand = Operand::Constant(Box::new(const_)); - } - Some(value) + self.simplify_place_value(place, location)? } + }; + if let Some(const_) = self.try_as_constant(value) { + *operand = Operand::Constant(Box::new(const_)); } + Some(value) } #[instrument(level = "trace", skip(self), ret)] @@ -1790,14 +1790,28 @@ impl<'tcx> VnState<'_, '_, 'tcx> { /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR. fn try_as_constant(&mut self, index: VnIndex) -> Option> { - // This was already constant in MIR, do not change it. If the constant is not - // deterministic, adding an additional mention of it in MIR will not give the same value as - // the former mention. - if let Value::Constant { value, disambiguator: None } = self.get(index) { - debug_assert!(value.is_deterministic()); + let value = self.get(index); + + // This was already an *evaluated* constant in MIR, do not change it. + if let Value::Constant { value, disambiguator: None } = value + && let Const::Val(..) = value + { return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } + if let Some(value) = self.try_as_evaluated_constant(index) { + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); + } + + // We failed to provide an evaluated form, fallback to using the unevaluated constant. + if let Value::Constant { value, disambiguator: None } = value { + return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); + } + + None + } + + fn try_as_evaluated_constant(&mut self, index: VnIndex) -> Option> { let op = self.eval_to_const(index)?; if op.layout.is_unsized() { // Do not attempt to propagate unsized locals. @@ -1811,8 +1825,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. assert!(!value.may_have_provenance(self.tcx, op.layout.size)); - let const_ = Const::Val(value, op.layout.ty); - Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ }) + Some(Const::Val(value, op.layout.ty)) } /// Construct a place which holds the same value as `index` and for which all locals strictly diff --git a/tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff b/tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff index 95eaf18b4703b..c68805f119ea7 100644 --- a/tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff +++ b/tests/mir-opt/const_prop/boxes.main.GVN.panic-abort.diff @@ -22,7 +22,8 @@ - StorageLive(_2); + nop; StorageLive(_3); - _4 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind unreachable]; +- _4 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind unreachable]; ++ _4 = alloc::alloc::exchange_malloc(const 4_usize, const 4_usize) -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/const_prop/boxes.main.GVN.panic-unwind.diff b/tests/mir-opt/const_prop/boxes.main.GVN.panic-unwind.diff index 6d8d3a0dcfe29..7fef8af08d400 100644 --- a/tests/mir-opt/const_prop/boxes.main.GVN.panic-unwind.diff +++ b/tests/mir-opt/const_prop/boxes.main.GVN.panic-unwind.diff @@ -22,7 +22,8 @@ - StorageLive(_2); + nop; StorageLive(_3); - _4 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind continue]; +- _4 = alloc::alloc::exchange_malloc(const ::SIZE, const ::ALIGN) -> [return: bb1, unwind continue]; ++ _4 = alloc::alloc::exchange_malloc(const 4_usize, const 4_usize) -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff index 6baa902b6f4bd..40630e7ad9712 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-abort.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(move _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff index 36540e038654f..29a5b06ae7ec8 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.32bit.panic-unwind.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(move _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff index 41c350f3eaeb5..56e08fcce8082 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-abort.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(move _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff index b839bf81eaf45..dd4c5cf5d65d6 100644 --- a/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/funky_arms.float_to_exponential_common.GVN.64bit.panic-unwind.diff @@ -47,7 +47,7 @@ StorageLive(_20); StorageLive(_21); _21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _20 = BitAnd(move _21, const core::fmt::flags::SIGN_PLUS_FLAG); + _20 = BitAnd(move _21, const 2097152_u32); StorageDead(_21); _4 = Ne(move _20, const 0_u32); StorageDead(_20); @@ -72,7 +72,7 @@ StorageLive(_22); StorageLive(_23); _23 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32); - _22 = BitAnd(move _23, const core::fmt::flags::PRECISION_FLAG); + _22 = BitAnd(move _23, const 268435456_u32); StorageDead(_23); switchInt(move _22) -> [0: bb10, otherwise: bb11]; } diff --git a/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff b/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff index a91561ba304b8..92158682c9993 100644 --- a/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff +++ b/tests/mir-opt/gvn_const_eval_polymorphic.no_optimize.GVN.diff @@ -5,7 +5,8 @@ let mut _0: bool; bb0: { - _0 = Eq(const no_optimize::::{constant#0}, const no_optimize::::{constant#1}); +- _0 = Eq(const no_optimize::::{constant#0}, const no_optimize::::{constant#1}); ++ _0 = Eq(const no_optimize::::{constant#0}, const true); return; } } diff --git a/tests/mir-opt/gvn_const_eval_polymorphic.rs b/tests/mir-opt/gvn_const_eval_polymorphic.rs index 7320ad947ff2e..722720b2a6f07 100644 --- a/tests/mir-opt/gvn_const_eval_polymorphic.rs +++ b/tests/mir-opt/gvn_const_eval_polymorphic.rs @@ -51,7 +51,7 @@ fn optimize_false() -> bool { // EMIT_MIR gvn_const_eval_polymorphic.no_optimize.GVN.diff fn no_optimize() -> bool { // CHECK-LABEL: fn no_optimize( - // CHECK: _0 = Eq(const no_optimize::::{constant#0}, const no_optimize::::{constant#1}); + // CHECK: _0 = Eq(const no_optimize::::{constant#0}, const true); // CHECK-NEXT: return; (const { type_name_contains_i32(&generic::) }) == const { true } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir index 18eeb8e4d3b61..c97c9b45d6ad5 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir @@ -17,7 +17,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { bb0: { StorageLive(_3); - _3 = Lt(copy _2, const core::num::::BITS); + _3 = Lt(copy _2, const 32_u32); switchInt(move _3) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir index 18eeb8e4d3b61..c97c9b45d6ad5 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir @@ -17,7 +17,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { bb0: { StorageLive(_3); - _3 = Lt(copy _2, const core::num::::BITS); + _3 = Lt(copy _2, const 32_u32); switchInt(move _3) -> [0: bb1, otherwise: bb2]; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir index 83478e60b5d4e..7b681946bee1e 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir @@ -72,7 +72,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { } bb6: { - assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u16::MAX, const 1_u16) -> [success: bb7, unwind unreachable]; } bb7: { diff --git a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir index ac7a6e0445191..a0137b23ec4f4 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir @@ -72,7 +72,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 { } bb6: { - assert(!const true, "attempt to compute `{} + {}`, which would overflow", const core::num::::MAX, const 1_u16) -> [success: bb7, unwind continue]; + assert(!const true, "attempt to compute `{} + {}`, which would overflow", const u16::MAX, const 1_u16) -> [success: bb7, unwind continue]; } bb7: { From 52a620ace26138fe0c71b1f10db3be579f3962c2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 6 Nov 2025 23:00:23 +0000 Subject: [PATCH 2/5] Constants of primitive types are always deterministic. --- compiler/rustc_middle/src/mir/consts.rs | 20 ++++++------- ...ace.PreCodegen.after.32bit.panic-abort.mir | 30 ++++++++----------- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 30 ++++++++----------- ...ace.PreCodegen.after.64bit.panic-abort.mir | 30 ++++++++----------- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 30 ++++++++----------- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 5 ++-- 6 files changed, 64 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index fe352df3b9f04..92da847bcb9e7 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -493,6 +493,11 @@ impl<'tcx> Const<'tcx> { /// 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. @@ -502,24 +507,19 @@ impl<'tcx> Const<'tcx> { // 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, } } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index 791d6b71a6f78..233f3dc373fc6 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -8,9 +8,9 @@ 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 _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _7: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -25,13 +25,13 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } } scope 18 (inlined ::deallocate) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { @@ -47,10 +47,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; scope 8 { scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; } } scope 9 (inlined size_of_val_raw::<[T]>) { @@ -68,32 +67,29 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = const ::ALIGN; - StorageLive(_7); - _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + StorageLive(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); + _7 = move (_6.0: std::ptr::alignment::AlignmentEnum); StorageDead(_6); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = discriminant(_7); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index 791d6b71a6f78..233f3dc373fc6 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -8,9 +8,9 @@ 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 _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _7: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -25,13 +25,13 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } } scope 18 (inlined ::deallocate) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { @@ -47,10 +47,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; scope 8 { scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; } } scope 9 (inlined size_of_val_raw::<[T]>) { @@ -68,32 +67,29 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = const ::ALIGN; - StorageLive(_7); - _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + StorageLive(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); + _7 = move (_6.0: std::ptr::alignment::AlignmentEnum); StorageDead(_6); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = discriminant(_7); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index 791d6b71a6f78..233f3dc373fc6 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -8,9 +8,9 @@ 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 _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _7: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -25,13 +25,13 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } } scope 18 (inlined ::deallocate) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { @@ -47,10 +47,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; scope 8 { scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; } } scope 9 (inlined size_of_val_raw::<[T]>) { @@ -68,32 +67,29 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = const ::ALIGN; - StorageLive(_7); - _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + StorageLive(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); + _7 = move (_6.0: std::ptr::alignment::AlignmentEnum); StorageDead(_6); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = discriminant(_7); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index 791d6b71a6f78..233f3dc373fc6 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -8,9 +8,9 @@ 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 _11: (); + let _10: (); scope 3 { - let _8: std::ptr::alignment::AlignmentEnum; + let _7: std::ptr::alignment::AlignmentEnum; scope 4 { scope 12 (inlined Layout::size) { } @@ -25,13 +25,13 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } } scope 18 (inlined ::deallocate) { - let mut _9: *mut u8; + let mut _8: *mut u8; scope 19 (inlined Layout::size) { } scope 20 (inlined NonNull::::as_ptr) { } scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; + let mut _9: usize; scope 22 (inlined Layout::size) { } scope 23 (inlined Layout::align) { @@ -47,10 +47,9 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } scope 7 (inlined Layout::for_value_raw::<[T]>) { let mut _5: usize; - let mut _6: usize; scope 8 { scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { - let mut _7: std::ptr::Alignment; + let mut _6: std::ptr::Alignment; } } scope 9 (inlined size_of_val_raw::<[T]>) { @@ -68,32 +67,29 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { StorageLive(_4); _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); - StorageLive(_6); _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = const ::ALIGN; - StorageLive(_7); - _7 = copy _6 as std::ptr::Alignment (Transmute); - _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); - StorageDead(_7); + StorageLive(_6); + _6 = const ::ALIGN as std::ptr::Alignment (Transmute); + _7 = move (_6.0: std::ptr::alignment::AlignmentEnum); StorageDead(_6); StorageDead(_4); switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } bb2: { + StorageLive(_8); + _8 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_9); - _9 = copy _3 as *mut u8 (PtrToPtr); - StorageLive(_10); - _10 = discriminant(_8); - _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable]; + _9 = discriminant(_7); + _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable]; } bb3: { - StorageDead(_10); StorageDead(_9); + StorageDead(_8); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index 83b10f8bd6888..15d3af8e7a5ba 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -9,9 +9,8 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK-LABEL: fn generic_in_place(_1: *mut Box<[T]>) // CHECK: (inlined as Drop>::drop) // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> - // CHECK: [[ALIGN:_.+]] = const ::ALIGN; - // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); - // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[ALIGN:_.+]] = const ::ALIGN as std::ptr::Alignment (Transmute); + // CHECK: [[C:_.+]] = move ([[ALIGN]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) -> std::ptr::drop_in_place(ptr) From 00225ace2ba22b3b790dca7a90a051aaa0448645 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 28 Nov 2025 23:27:21 +0000 Subject: [PATCH 3/5] Bless incremental. --- tests/incremental/hashes/enum_constructors.rs | 2 +- tests/incremental/hashes/struct_constructors.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/incremental/hashes/enum_constructors.rs b/tests/incremental/hashes/enum_constructors.rs index dee485681e447..5962727b3af0b 100644 --- a/tests/incremental/hashes/enum_constructors.rs +++ b/tests/incremental/hashes/enum_constructors.rs @@ -65,7 +65,7 @@ pub fn change_field_order_struct_like() -> Enum { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck,optimized_mir")] +#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail6")] // FIXME(michaelwoerister):Interesting. I would have thought that that changes the MIR. And it // would if it were not all constants diff --git a/tests/incremental/hashes/struct_constructors.rs b/tests/incremental/hashes/struct_constructors.rs index da7abe049d982..b2f8d116b1e94 100644 --- a/tests/incremental/hashes/struct_constructors.rs +++ b/tests/incremental/hashes/struct_constructors.rs @@ -62,7 +62,7 @@ pub fn change_field_order_regular_struct() -> RegularStruct { #[cfg(not(any(cfail1,cfail4)))] #[rustc_clean(cfg="cfail2", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail3")] -#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck,optimized_mir")] +#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes,typeck")] #[rustc_clean(cfg="cfail6")] pub fn change_field_order_regular_struct() -> RegularStruct { RegularStruct { From 1d83adba279eb80453e5ff4bfeeb8a2ecc32b498 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Dec 2025 01:14:00 +0000 Subject: [PATCH 4/5] Elaborate comments. --- compiler/rustc_middle/src/mir/consts.rs | 52 +++++++++---------------- compiler/rustc_mir_transform/src/gvn.rs | 2 +- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 92da847bcb9e7..6a68c50768947 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -183,21 +183,16 @@ impl ConstValue { /// Check if a constant may contain provenance information. This is used by MIR opts. /// Can return `true` even if there is no provenance. - pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool { + pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Option) -> bool { match *self { ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, - ConstValue::Scalar(Scalar::Ptr(..)) => return true, - // It's hard to find out the part of the allocation we point to; - // just conservatively check everything. - ConstValue::Slice { alloc_id, meta: _ } => { - !tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() + ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, + ConstValue::Indirect { alloc_id, offset } => { + let allocation = tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let end = if let Some(size) = size { offset + size } else { allocation.size() }; + let provenance_map = allocation.provenance(); + !provenance_map.range_empty(AllocRange::from(offset..end), &tcx) } - ConstValue::Indirect { alloc_id, offset } => !tcx - .global_alloc(alloc_id) - .unwrap_memory() - .inner() - .provenance() - .range_empty(AllocRange::from(offset..offset + size), &tcx), } } @@ -490,35 +485,26 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } - /// Return true if any evaluation of this constant always returns the same value, - /// taking into account even pointer identity tests. + /// Return true if any evaluation of this constant in the same MIR body + /// always returns the same value, taking into account even pointer identity tests. + /// + /// In other words, this answers: is "cloning" the mir::ConstOperand ok? 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. match self { - Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Param(..) => true, - // 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(..) - | 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, - // Should not appear in runtime MIR. - ty::ConstKind::Infer(..) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(..) => bug!(), - }, + // 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. + Const::Ty(..) => false, + // We do not know the contents, so don't attempt to do anything clever. Const::Unevaluated(..) => false, + // When an evaluated contant contains provenance, it is encoded as an `AllocId`. + // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR + // body, this same `AllocId` will result in the same pointer in codegen. Const::Val(..) => true, } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index c876384a22c72..89248616bcd2b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1823,7 +1823,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - assert!(!value.may_have_provenance(self.tcx, op.layout.size)); + assert!(!value.may_have_provenance(self.tcx, Some(op.layout.size))); Some(Const::Val(value, op.layout.ty)) } From c8e16530b992bb6d7c74b65b42bad0bb05e88894 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 17 Dec 2025 00:37:19 +0000 Subject: [PATCH 5/5] Move methods to gvn.rs. --- compiler/rustc_middle/src/mir/consts.rs | 39 ------------- compiler/rustc_mir_transform/src/gvn.rs | 75 +++++++++++++++++++++---- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 6a68c50768947..6d15581d0c39a 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -181,21 +181,6 @@ impl ConstValue { Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end)) } - /// Check if a constant may contain provenance information. This is used by MIR opts. - /// Can return `true` even if there is no provenance. - pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Option) -> bool { - match *self { - ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, - ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, - ConstValue::Indirect { alloc_id, offset } => { - let allocation = tcx.global_alloc(alloc_id).unwrap_memory().inner(); - let end = if let Some(size) = size { offset + size } else { allocation.size() }; - let provenance_map = allocation.provenance(); - !provenance_map.range_empty(AllocRange::from(offset..end), &tcx) - } - } - } - /// Check if a constant only contains uninitialized bytes. pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool { let ConstValue::Indirect { alloc_id, .. } = self else { @@ -484,30 +469,6 @@ impl<'tcx> Const<'tcx> { let val = ConstValue::Scalar(s); Self::Val(val, ty) } - - /// Return true if any evaluation of this constant in the same MIR body - /// always returns the same value, taking into account even pointer identity tests. - /// - /// In other words, this answers: is "cloning" the mir::ConstOperand ok? - pub fn is_deterministic(&self) -> bool { - // Primitive types cannot contain provenance and always have the same value. - if self.ty().is_primitive() { - return true; - } - - match self { - // 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. - Const::Ty(..) => false, - // We do not know the contents, so don't attempt to do anything clever. - Const::Unevaluated(..) => false, - // When an evaluated contant contains provenance, it is encoded as an `AllocId`. - // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR - // body, this same `AllocId` will result in the same pointer in codegen. - Const::Val(..) => true, - } - } } /// An unevaluated (potentially generic) constant used in MIR. diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 89248616bcd2b..70138ad581d49 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -61,10 +61,10 @@ //! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed. //! //! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have -//! different runtime values each time they are evaluated. This is the case with -//! `Const::Slice` which have a new pointer each time they are evaluated, and constants that -//! contain a fn pointer (`AllocId` pointing to a `GlobalAlloc::Function`) pointing to a different -//! symbol in each codegen unit. +//! different runtime values each time they are evaluated. This used to be the case with +//! `ConstValue::Slice` which have a new pointer each time they are evaluated, and is still the +//! case with valtrees that generate a new allocation each time they are used. This is checked by +//! `is_deterministic`. //! //! Meanwhile, we want to be able to read indirect constants. For instance: //! ``` @@ -81,8 +81,11 @@ //! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not //! merge the constants. See `duplicate_slice` test in `gvn.rs`. //! -//! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const` -//! that contain `AllocId`s. +//! Conversely, some constants cannot cross crate boundaries, which could happen because of +//! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a +//! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this, +//! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is +//! checked by `may_have_provenance`. use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -103,7 +106,7 @@ use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; -use rustc_middle::mir::interpret::GlobalAlloc; +use rustc_middle::mir::interpret::{AllocRange, GlobalAlloc}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::layout::HasTypingEnv; @@ -486,7 +489,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - let (index, new) = if value.is_deterministic() { + let (index, new) = if is_deterministic(value) { // The constant is deterministic, no need to disambiguate. let constant = Value::Constant { value, disambiguator: None }; self.values.insert(value.ty(), constant) @@ -529,14 +532,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); - debug_assert!(value.is_deterministic()); + debug_assert!(is_deterministic(value)); self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); - debug_assert!(value.is_deterministic()); + debug_assert!(is_deterministic(value)); self.insert(ty, Value::Constant { value, disambiguator: None }) } @@ -1692,6 +1695,45 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { } } +/// Return true if any evaluation of this constant in the same MIR body +/// always returns the same value, taking into account even pointer identity tests. +/// +/// In other words, this answers: is "cloning" the `Const` ok? +fn is_deterministic(c: Const<'_>) -> bool { + // Primitive types cannot contain provenance and always have the same value. + if c.ty().is_primitive() { + return true; + } + + match c { + // 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. + Const::Ty(..) => false, + // We do not know the contents, so don't attempt to do anything clever. + Const::Unevaluated(..) => false, + // When an evaluated constant contains provenance, it is encoded as an `AllocId`. + // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR + // body, this same `AllocId` will result in the same pointer in codegen. + Const::Val(..) => true, + } +} + +/// Check if a constant may contain provenance information. +/// Can return `true` even if there is no provenance. +fn may_have_provenance(tcx: TyCtxt<'_>, value: ConstValue, size: Size) -> bool { + match value { + ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false, + ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true, + ConstValue::Indirect { alloc_id, offset } => !tcx + .global_alloc(alloc_id) + .unwrap_memory() + .inner() + .provenance() + .range_empty(AllocRange::from(offset..offset + size), &tcx), + } +} + fn op_to_prop_const<'tcx>( ecx: &mut InterpCx<'tcx, DummyMachine>, op: &OpTy<'tcx>, @@ -1767,7 +1809,16 @@ fn op_to_prop_const<'tcx>( // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - if ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() { + if ecx + .tcx + .global_alloc(alloc_id) + .unwrap_memory() + .inner() + .provenance() + .provenances() + .next() + .is_none() + { return Some(value); } @@ -1823,7 +1874,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> { // Check that we do not leak a pointer. // Those pointers may lose part of their identity in codegen. // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed. - assert!(!value.may_have_provenance(self.tcx, Some(op.layout.size))); + assert!(!may_have_provenance(self.tcx, value, op.layout.size)); Some(Const::Val(value, op.layout.ty)) }