Skip to content

Commit 5197c96

Browse files
committed
Auto merge of #101483 - oli-obk:guaranteed_opt, r=fee1-dead
The `<*const T>::guaranteed_*` methods now return an option for the unknown case cc #53020 (comment) I chose `0` for "not equal" and `1` for "equal" and left `2` for the unknown case so backends can just forward to raw pointer equality and it works ✨ r? `@fee1-dead` or `@lcnr` cc `@rust-lang/wg-const-eval`
2 parents db9d86b + f632dbe commit 5197c96

File tree

12 files changed

+119
-138
lines changed

12 files changed

+119
-138
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -816,20 +816,13 @@ fn codegen_regular_intrinsic_call<'tcx>(
816816
ret.write_cvalue(fx, val);
817817
}
818818

819-
sym::ptr_guaranteed_eq => {
819+
sym::ptr_guaranteed_cmp => {
820820
intrinsic_args!(fx, args => (a, b); intrinsic);
821821

822822
let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b);
823823
ret.write_cvalue(fx, val);
824824
}
825825

826-
sym::ptr_guaranteed_ne => {
827-
intrinsic_args!(fx, args => (a, b); intrinsic);
828-
829-
let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b);
830-
ret.write_cvalue(fx, val);
831-
}
832-
833826
sym::caller_location => {
834827
intrinsic_args!(fx, args => (); intrinsic);
835828

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -551,14 +551,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
551551
return;
552552
}
553553

554-
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
554+
sym::ptr_guaranteed_cmp => {
555555
let a = args[0].immediate();
556556
let b = args[1].immediate();
557-
if name == sym::ptr_guaranteed_eq {
558-
bx.icmp(IntPredicate::IntEQ, a, b)
559-
} else {
560-
bx.icmp(IntPredicate::IntNE, a, b)
561-
}
557+
bx.icmp(IntPredicate::IntEQ, a, b)
562558
}
563559

564560
sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {

compiler/rustc_const_eval/src/const_eval/machine.rs

+23-26
Original file line numberDiff line numberDiff line change
@@ -191,34 +191,35 @@ impl interpret::MayLeak for ! {
191191
}
192192

193193
impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
194-
fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, bool> {
194+
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
195+
fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
195196
Ok(match (a, b) {
196197
// Comparisons between integers are always known.
197-
(Scalar::Int { .. }, Scalar::Int { .. }) => a == b,
198-
// Equality with integers can never be known for sure.
199-
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => false,
200-
// FIXME: return `true` for when both sides are the same pointer, *except* that
201-
// some things (like functions and vtables) do not have stable addresses
202-
// so we need to be careful around them (see e.g. #73722).
203-
(Scalar::Ptr(..), Scalar::Ptr(..)) => false,
204-
})
205-
}
206-
207-
fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, bool> {
208-
Ok(match (a, b) {
209-
// Comparisons between integers are always known.
210-
(Scalar::Int(_), Scalar::Int(_)) => a != b,
198+
(Scalar::Int { .. }, Scalar::Int { .. }) => {
199+
if a == b {
200+
1
201+
} else {
202+
0
203+
}
204+
}
211205
// Comparisons of abstract pointers with null pointers are known if the pointer
212206
// is in bounds, because if they are in bounds, the pointer can't be null.
213207
// Inequality with integers other than null can never be known for sure.
214208
(Scalar::Int(int), ptr @ Scalar::Ptr(..))
215-
| (ptr @ Scalar::Ptr(..), Scalar::Int(int)) => {
216-
int.is_null() && !self.scalar_may_be_null(ptr)?
209+
| (ptr @ Scalar::Ptr(..), Scalar::Int(int))
210+
if int.is_null() && !self.scalar_may_be_null(ptr)? =>
211+
{
212+
0
217213
}
218-
// FIXME: return `true` for at least some comparisons where we can reliably
214+
// Equality with integers can never be known for sure.
215+
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => 2,
216+
// FIXME: return a `1` for when both sides are the same pointer, *except* that
217+
// some things (like functions and vtables) do not have stable addresses
218+
// so we need to be careful around them (see e.g. #73722).
219+
// FIXME: return `0` for at least some comparisons where we can reliably
219220
// determine the result of runtime inequality tests at compile-time.
220221
// Examples include comparison of addresses in different static items.
221-
(Scalar::Ptr(..), Scalar::Ptr(..)) => false,
222+
(Scalar::Ptr(..), Scalar::Ptr(..)) => 2,
222223
})
223224
}
224225
}
@@ -329,15 +330,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
329330
throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time");
330331
};
331332
match intrinsic_name {
332-
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
333+
sym::ptr_guaranteed_cmp => {
333334
let a = ecx.read_scalar(&args[0])?;
334335
let b = ecx.read_scalar(&args[1])?;
335-
let cmp = if intrinsic_name == sym::ptr_guaranteed_eq {
336-
ecx.guaranteed_eq(a, b)?
337-
} else {
338-
ecx.guaranteed_ne(a, b)?
339-
};
340-
ecx.write_scalar(Scalar::from_bool(cmp), dest)?;
336+
let cmp = ecx.guaranteed_cmp(a, b)?;
337+
ecx.write_scalar(Scalar::from_u8(cmp), dest)?;
341338
}
342339
sym::const_allocate => {
343340
let size = ecx.read_scalar(&args[0])?.to_machine_usize(ecx)?;

compiler/rustc_const_eval/src/const_eval/valtrees.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
9797
}
9898

9999
// Raw pointers are not allowed in type level constants, as we cannot properly test them for
100-
// equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
100+
// equality at compile-time (see `ptr_guaranteed_cmp`).
101101
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
102102
// agree with runtime equality tests.
103103
ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),

compiler/rustc_span/src/symbol.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1117,8 +1117,7 @@ symbols! {
11171117
profiler_builtins,
11181118
profiler_runtime,
11191119
ptr,
1120-
ptr_guaranteed_eq,
1121-
ptr_guaranteed_ne,
1120+
ptr_guaranteed_cmp,
11221121
ptr_mask,
11231122
ptr_null,
11241123
ptr_null_mut,

compiler/rustc_typeck/src/check/intrinsic.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
9595
| sym::type_id
9696
| sym::likely
9797
| sym::unlikely
98-
| sym::ptr_guaranteed_eq
99-
| sym::ptr_guaranteed_ne
98+
| sym::ptr_guaranteed_cmp
10099
| sym::minnumf32
101100
| sym::minnumf64
102101
| sym::maxnumf32
@@ -302,8 +301,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
302301
(1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
303302
}
304303

305-
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
306-
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
304+
sym::ptr_guaranteed_cmp => {
305+
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
307306
}
308307

309308
sym::const_allocate => {

library/core/src/intrinsics.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -2013,21 +2013,24 @@ extern "rust-intrinsic" {
20132013
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
20142014

20152015
/// See documentation of `<*const T>::guaranteed_eq` for details.
2016+
/// Returns `2` if the result is unknown.
2017+
/// Returns `1` if the pointers are guaranteed equal
2018+
/// Returns `0` if the pointers are guaranteed inequal
20162019
///
20172020
/// Note that, unlike most intrinsics, this is safe to call;
20182021
/// it does not require an `unsafe` block.
20192022
/// Therefore, implementations must not require the user to uphold
20202023
/// any safety invariants.
20212024
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
2025+
#[cfg(not(bootstrap))]
2026+
pub fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8;
2027+
2028+
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
2029+
#[cfg(bootstrap)]
20222030
pub fn ptr_guaranteed_eq<T>(ptr: *const T, other: *const T) -> bool;
20232031

2024-
/// See documentation of `<*const T>::guaranteed_ne` for details.
2025-
///
2026-
/// Note that, unlike most intrinsics, this is safe to call;
2027-
/// it does not require an `unsafe` block.
2028-
/// Therefore, implementations must not require the user to uphold
2029-
/// any safety invariants.
20302032
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
2033+
#[cfg(bootstrap)]
20312034
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
20322035

20332036
/// Allocates a block of memory at compile time.
@@ -2213,6 +2216,16 @@ pub(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, count: usize) -
22132216
diff >= size
22142217
}
22152218

2219+
#[cfg(bootstrap)]
2220+
pub const fn ptr_guaranteed_cmp(a: *const (), b: *const ()) -> u8 {
2221+
match (ptr_guaranteed_eq(a, b), ptr_guaranteed_ne(a, b)) {
2222+
(false, false) => 2,
2223+
(true, false) => 1,
2224+
(false, true) => 0,
2225+
(true, true) => unreachable!(),
2226+
}
2227+
}
2228+
22162229
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
22172230
/// and destination must *not* overlap.
22182231
///

library/core/src/ptr/const_ptr.rs

+28-27
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ impl<T: ?Sized> *const T {
3636
pub const fn is_null(self) -> bool {
3737
// Compare via a cast to a thin pointer, so fat pointers are only
3838
// considering their "data" part for null-ness.
39-
(self as *const u8).guaranteed_eq(null())
39+
match (self as *const u8).guaranteed_eq(null()) {
40+
None => false,
41+
Some(res) => res,
42+
}
4043
}
4144

4245
/// Casts to a pointer of another type.
@@ -770,20 +773,16 @@ impl<T: ?Sized> *const T {
770773

771774
/// Returns whether two pointers are guaranteed to be equal.
772775
///
773-
/// At runtime this function behaves like `self == other`.
776+
/// At runtime this function behaves like `Some(self == other)`.
774777
/// However, in some contexts (e.g., compile-time evaluation),
775778
/// it is not always possible to determine equality of two pointers, so this function may
776-
/// spuriously return `false` for pointers that later actually turn out to be equal.
777-
/// But when it returns `true`, the pointers are guaranteed to be equal.
778-
///
779-
/// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer
780-
/// comparisons for which both functions return `false`.
779+
/// spuriously return `None` for pointers that later actually turn out to have its equality known.
780+
/// But when it returns `Some`, the pointers' equality is guaranteed to be known.
781781
///
782-
/// [`guaranteed_ne`]: #method.guaranteed_ne
783-
///
784-
/// The return value may change depending on the compiler version and unsafe code must not
782+
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
783+
/// version and unsafe code must not
785784
/// rely on the result of this function for soundness. It is suggested to only use this function
786-
/// for performance optimizations where spurious `false` return values by this function do not
785+
/// for performance optimizations where spurious `None` return values by this function do not
787786
/// affect the outcome, but just the performance.
788787
/// The consequences of using this method to make runtime and compile-time code behave
789788
/// differently have not been explored. This method should not be used to introduce such
@@ -792,29 +791,28 @@ impl<T: ?Sized> *const T {
792791
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
793792
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
794793
#[inline]
795-
pub const fn guaranteed_eq(self, other: *const T) -> bool
794+
pub const fn guaranteed_eq(self, other: *const T) -> Option<bool>
796795
where
797796
T: Sized,
798797
{
799-
intrinsics::ptr_guaranteed_eq(self, other)
798+
match intrinsics::ptr_guaranteed_cmp(self as _, other as _) {
799+
2 => None,
800+
other => Some(other == 1),
801+
}
800802
}
801803

802-
/// Returns whether two pointers are guaranteed to be unequal.
804+
/// Returns whether two pointers are guaranteed to be inequal.
803805
///
804-
/// At runtime this function behaves like `self != other`.
806+
/// At runtime this function behaves like `Some(self == other)`.
805807
/// However, in some contexts (e.g., compile-time evaluation),
806-
/// it is not always possible to determine the inequality of two pointers, so this function may
807-
/// spuriously return `false` for pointers that later actually turn out to be unequal.
808-
/// But when it returns `true`, the pointers are guaranteed to be unequal.
808+
/// it is not always possible to determine inequality of two pointers, so this function may
809+
/// spuriously return `None` for pointers that later actually turn out to have its inequality known.
810+
/// But when it returns `Some`, the pointers' inequality is guaranteed to be known.
809811
///
810-
/// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer
811-
/// comparisons for which both functions return `false`.
812-
///
813-
/// [`guaranteed_eq`]: #method.guaranteed_eq
814-
///
815-
/// The return value may change depending on the compiler version and unsafe code must not
812+
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
813+
/// version and unsafe code must not
816814
/// rely on the result of this function for soundness. It is suggested to only use this function
817-
/// for performance optimizations where spurious `false` return values by this function do not
815+
/// for performance optimizations where spurious `None` return values by this function do not
818816
/// affect the outcome, but just the performance.
819817
/// The consequences of using this method to make runtime and compile-time code behave
820818
/// differently have not been explored. This method should not be used to introduce such
@@ -823,11 +821,14 @@ impl<T: ?Sized> *const T {
823821
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
824822
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
825823
#[inline]
826-
pub const fn guaranteed_ne(self, other: *const T) -> bool
824+
pub const fn guaranteed_ne(self, other: *const T) -> Option<bool>
827825
where
828826
T: Sized,
829827
{
830-
intrinsics::ptr_guaranteed_ne(self, other)
828+
match self.guaranteed_eq(other) {
829+
None => None,
830+
Some(eq) => Some(!eq),
831+
}
831832
}
832833

833834
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).

library/core/src/ptr/mut_ptr.rs

+22-27
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ impl<T: ?Sized> *mut T {
3535
pub const fn is_null(self) -> bool {
3636
// Compare via a cast to a thin pointer, so fat pointers are only
3737
// considering their "data" part for null-ness.
38-
(self as *mut u8).guaranteed_eq(null_mut())
38+
match (self as *mut u8).guaranteed_eq(null_mut()) {
39+
None => false,
40+
Some(res) => res,
41+
}
3942
}
4043

4144
/// Casts to a pointer of another type.
@@ -697,20 +700,16 @@ impl<T: ?Sized> *mut T {
697700

698701
/// Returns whether two pointers are guaranteed to be equal.
699702
///
700-
/// At runtime this function behaves like `self == other`.
703+
/// At runtime this function behaves like `Some(self == other)`.
701704
/// However, in some contexts (e.g., compile-time evaluation),
702705
/// it is not always possible to determine equality of two pointers, so this function may
703-
/// spuriously return `false` for pointers that later actually turn out to be equal.
704-
/// But when it returns `true`, the pointers are guaranteed to be equal.
705-
///
706-
/// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer
707-
/// comparisons for which both functions return `false`.
708-
///
709-
/// [`guaranteed_ne`]: #method.guaranteed_ne
706+
/// spuriously return `None` for pointers that later actually turn out to have its equality known.
707+
/// But when it returns `Some`, the pointers' equality is guaranteed to be known.
710708
///
711-
/// The return value may change depending on the compiler version and unsafe code might not
709+
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
710+
/// version and unsafe code must not
712711
/// rely on the result of this function for soundness. It is suggested to only use this function
713-
/// for performance optimizations where spurious `false` return values by this function do not
712+
/// for performance optimizations where spurious `None` return values by this function do not
714713
/// affect the outcome, but just the performance.
715714
/// The consequences of using this method to make runtime and compile-time code behave
716715
/// differently have not been explored. This method should not be used to introduce such
@@ -719,29 +718,25 @@ impl<T: ?Sized> *mut T {
719718
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
720719
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
721720
#[inline]
722-
pub const fn guaranteed_eq(self, other: *mut T) -> bool
721+
pub const fn guaranteed_eq(self, other: *mut T) -> Option<bool>
723722
where
724723
T: Sized,
725724
{
726-
intrinsics::ptr_guaranteed_eq(self as *const _, other as *const _)
725+
(self as *const T).guaranteed_eq(other as _)
727726
}
728727

729-
/// Returns whether two pointers are guaranteed to be unequal.
728+
/// Returns whether two pointers are guaranteed to be inequal.
730729
///
731-
/// At runtime this function behaves like `self != other`.
730+
/// At runtime this function behaves like `Some(self == other)`.
732731
/// However, in some contexts (e.g., compile-time evaluation),
733-
/// it is not always possible to determine the inequality of two pointers, so this function may
734-
/// spuriously return `false` for pointers that later actually turn out to be unequal.
735-
/// But when it returns `true`, the pointers are guaranteed to be unequal.
736-
///
737-
/// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer
738-
/// comparisons for which both functions return `false`.
739-
///
740-
/// [`guaranteed_eq`]: #method.guaranteed_eq
732+
/// it is not always possible to determine inequality of two pointers, so this function may
733+
/// spuriously return `None` for pointers that later actually turn out to have its inequality known.
734+
/// But when it returns `Some`, the pointers' inequality is guaranteed to be known.
741735
///
742-
/// The return value may change depending on the compiler version and unsafe code might not
736+
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
737+
/// version and unsafe code must not
743738
/// rely on the result of this function for soundness. It is suggested to only use this function
744-
/// for performance optimizations where spurious `false` return values by this function do not
739+
/// for performance optimizations where spurious `None` return values by this function do not
745740
/// affect the outcome, but just the performance.
746741
/// The consequences of using this method to make runtime and compile-time code behave
747742
/// differently have not been explored. This method should not be used to introduce such
@@ -750,11 +745,11 @@ impl<T: ?Sized> *mut T {
750745
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
751746
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
752747
#[inline]
753-
pub const unsafe fn guaranteed_ne(self, other: *mut T) -> bool
748+
pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
754749
where
755750
T: Sized,
756751
{
757-
intrinsics::ptr_guaranteed_ne(self as *const _, other as *const _)
752+
(self as *const T).guaranteed_ne(other as _)
758753
}
759754

760755
/// Calculates the distance between two pointers. The returned value is in

0 commit comments

Comments
 (0)