-
Notifications
You must be signed in to change notification settings - Fork 13.4k
A way forward for pointer equality in const eval #73398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9245ba8
e09b620
84f1d73
9e88b48
53686b9
98e97a4
e465b22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -295,6 +295,72 @@ impl<T: ?Sized> *const T { | |
intrinsics::ptr_offset_from(self, origin) | ||
} | ||
|
||
/// Returns whether two pointers are guaranteed to be equal. | ||
/// | ||
/// At runtime this function behaves like `self == other`. | ||
/// However, in some contexts (e.g., compile-time evaluation), | ||
/// it is not always possible to determine equality of two pointers, so this function may | ||
/// spuriously return `false` for pointers that later actually turn out to be equal. | ||
/// But when it returns `true`, the pointers are guaranteed to be equal. | ||
/// | ||
/// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer | ||
/// comparisons for which both functions return `false`. | ||
/// | ||
/// [`guaranteed_ne`]: #method.guaranteed_ne | ||
/// | ||
/// The return value may change depending on the compiler version and unsafe code may not | ||
/// rely on the result of this function for soundness. It is suggested to only use this function | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can unsafe code rely on a return value of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it can. The comment says
I thought that was clear enough? |
||
/// for performance optimizations where spurious `false` return values by this function do not | ||
/// affect the outcome, but just the performance. | ||
/// The consequences of using this method to make runtime and compile-time code behave | ||
/// differently have not been explored. This method should not be used to introduce such | ||
/// differences, and it should also not be stabilized before we have a better understanding | ||
/// of this issue. | ||
/// ``` | ||
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] | ||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] | ||
#[inline] | ||
#[cfg(not(bootstrap))] | ||
pub const fn guaranteed_eq(self, other: *const T) -> bool | ||
where | ||
T: Sized, | ||
{ | ||
intrinsics::ptr_guaranteed_eq(self, other) | ||
} | ||
|
||
/// Returns whether two pointers are guaranteed to be inequal. | ||
/// | ||
/// At runtime this function behaves like `self != other`. | ||
/// However, in some contexts (e.g., compile-time evaluation), | ||
/// it is not always possible to determine the inequality of two pointers, so this function may | ||
/// spuriously return `false` for pointers that later actually turn out to be inequal. | ||
/// But when it returns `true`, the pointers are guaranteed to be inequal. | ||
/// | ||
/// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer | ||
/// comparisons for which both functions return `false`. | ||
/// | ||
/// [`guaranteed_eq`]: #method.guaranteed_eq | ||
/// | ||
/// The return value may change depending on the compiler version and unsafe code may not | ||
/// rely on the result of this function for soundness. It is suggested to only use this function | ||
/// for performance optimizations where spurious `false` return values by this function do not | ||
/// affect the outcome, but just the performance. | ||
/// The consequences of using this method to make runtime and compile-time code behave | ||
/// differently have not been explored. This method should not be used to introduce such | ||
/// differences, and it should also not be stabilized before we have a better understanding | ||
/// of this issue. | ||
/// ``` | ||
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] | ||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] | ||
#[inline] | ||
#[cfg(not(bootstrap))] | ||
pub const fn guaranteed_ne(self, other: *const T) -> bool | ||
where | ||
T: Sized, | ||
{ | ||
intrinsics::ptr_guaranteed_ne(self, other) | ||
} | ||
|
||
/// Calculates the distance between two pointers. The returned value is in | ||
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. | ||
/// | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5956,10 +5956,18 @@ where | |
return false; | ||
} | ||
|
||
#[cfg(bootstrap)] | ||
if self.as_ptr() == other.as_ptr() { | ||
return true; | ||
} | ||
|
||
// While performance would suffer if `guaranteed_eq` just returned `false` | ||
// for all arguments, correctness and return value of this function are not affected. | ||
#[cfg(not(bootstrap))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this changed? This function is not const. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not yet, we're working towards that. I have a local branch where I'm trying to run this in |
||
if self.as_ptr().guaranteed_eq(other.as_ptr()) { | ||
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return true; | ||
} | ||
|
||
self.iter().zip(other.iter()).all(|(x, y)| x == y) | ||
} | ||
} | ||
|
@@ -5973,9 +5981,18 @@ where | |
if self.len() != other.len() { | ||
return false; | ||
} | ||
|
||
#[cfg(bootstrap)] | ||
if self.as_ptr() == other.as_ptr() { | ||
return true; | ||
} | ||
|
||
// While performance would suffer if `guaranteed_eq` just returned `false` | ||
// for all arguments, correctness and return value of this function are not affected. | ||
#[cfg(not(bootstrap))] | ||
if self.as_ptr().guaranteed_eq(other.as_ptr()) { | ||
return true; | ||
} | ||
unsafe { | ||
let size = mem::size_of_val(self); | ||
memcmp(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -291,6 +291,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); | ||
self.write_scalar(offset_ptr, dest)?; | ||
} | ||
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realized that this implementation will also be used by Miri, which is a bug. Can we somehow make it be used only during CTCE? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doesn't miri fall back to this only if it has no own impl? So we can just give miri an impl that behaves differently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Miri prefers the CTFE impls, to make sure we test those properly. I am working on a PR that adds these to Miri in a way that it prefers its own impls. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here it is: rust-lang/miri#1459 |
||
// FIXME: return `true` for at least some comparisons where we can reliably | ||
// determine the result of runtime (in)equality tests at compile-time. | ||
self.write_scalar(Scalar::from_bool(false), dest)?; | ||
} | ||
sym::ptr_offset_from => { | ||
let a = self.read_immediate(args[0])?.to_scalar()?; | ||
let b = self.read_immediate(args[1])?.to_scalar()?; | ||
|
Uh oh!
There was an error while loading. Please reload this page.