mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-09 08:27:38 +00:00
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 https://github.com/rust-lang/rust/issues/53020#issuecomment-1236932443 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`
This commit is contained in:
commit
5197c96c49
@ -816,20 +816,13 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
|||||||
ret.write_cvalue(fx, val);
|
ret.write_cvalue(fx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
sym::ptr_guaranteed_eq => {
|
sym::ptr_guaranteed_cmp => {
|
||||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
intrinsic_args!(fx, args => (a, b); intrinsic);
|
||||||
|
|
||||||
let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b);
|
let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b);
|
||||||
ret.write_cvalue(fx, val);
|
ret.write_cvalue(fx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
sym::ptr_guaranteed_ne => {
|
|
||||||
intrinsic_args!(fx, args => (a, b); intrinsic);
|
|
||||||
|
|
||||||
let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b);
|
|
||||||
ret.write_cvalue(fx, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
sym::caller_location => {
|
sym::caller_location => {
|
||||||
intrinsic_args!(fx, args => (); intrinsic);
|
intrinsic_args!(fx, args => (); intrinsic);
|
||||||
|
|
||||||
|
@ -551,14 +551,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
|
sym::ptr_guaranteed_cmp => {
|
||||||
let a = args[0].immediate();
|
let a = args[0].immediate();
|
||||||
let b = args[1].immediate();
|
let b = args[1].immediate();
|
||||||
if name == sym::ptr_guaranteed_eq {
|
|
||||||
bx.icmp(IntPredicate::IntEQ, a, b)
|
bx.icmp(IntPredicate::IntEQ, a, b)
|
||||||
} else {
|
|
||||||
bx.icmp(IntPredicate::IntNE, a, b)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
|
sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
|
||||||
|
@ -191,34 +191,35 @@ impl interpret::MayLeak for ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
|
impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
|
||||||
fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, bool> {
|
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
|
||||||
|
fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
|
||||||
Ok(match (a, b) {
|
Ok(match (a, b) {
|
||||||
// Comparisons between integers are always known.
|
// Comparisons between integers are always known.
|
||||||
(Scalar::Int { .. }, Scalar::Int { .. }) => a == b,
|
(Scalar::Int { .. }, Scalar::Int { .. }) => {
|
||||||
// Equality with integers can never be known for sure.
|
if a == b {
|
||||||
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => false,
|
1
|
||||||
// FIXME: return `true` for when both sides are the same pointer, *except* that
|
} else {
|
||||||
// some things (like functions and vtables) do not have stable addresses
|
0
|
||||||
// so we need to be careful around them (see e.g. #73722).
|
}
|
||||||
(Scalar::Ptr(..), Scalar::Ptr(..)) => false,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, bool> {
|
|
||||||
Ok(match (a, b) {
|
|
||||||
// Comparisons between integers are always known.
|
|
||||||
(Scalar::Int(_), Scalar::Int(_)) => a != b,
|
|
||||||
// Comparisons of abstract pointers with null pointers are known if the pointer
|
// Comparisons of abstract pointers with null pointers are known if the pointer
|
||||||
// is in bounds, because if they are in bounds, the pointer can't be null.
|
// is in bounds, because if they are in bounds, the pointer can't be null.
|
||||||
// Inequality with integers other than null can never be known for sure.
|
// Inequality with integers other than null can never be known for sure.
|
||||||
(Scalar::Int(int), ptr @ Scalar::Ptr(..))
|
(Scalar::Int(int), ptr @ Scalar::Ptr(..))
|
||||||
| (ptr @ Scalar::Ptr(..), Scalar::Int(int)) => {
|
| (ptr @ Scalar::Ptr(..), Scalar::Int(int))
|
||||||
int.is_null() && !self.scalar_may_be_null(ptr)?
|
if int.is_null() && !self.scalar_may_be_null(ptr)? =>
|
||||||
|
{
|
||||||
|
0
|
||||||
}
|
}
|
||||||
// FIXME: return `true` for at least some comparisons where we can reliably
|
// Equality with integers can never be known for sure.
|
||||||
|
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => 2,
|
||||||
|
// FIXME: return a `1` for when both sides are the same pointer, *except* that
|
||||||
|
// some things (like functions and vtables) do not have stable addresses
|
||||||
|
// so we need to be careful around them (see e.g. #73722).
|
||||||
|
// FIXME: return `0` for at least some comparisons where we can reliably
|
||||||
// determine the result of runtime inequality tests at compile-time.
|
// determine the result of runtime inequality tests at compile-time.
|
||||||
// Examples include comparison of addresses in different static items.
|
// Examples include comparison of addresses in different static items.
|
||||||
(Scalar::Ptr(..), Scalar::Ptr(..)) => false,
|
(Scalar::Ptr(..), Scalar::Ptr(..)) => 2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,15 +330,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||||||
throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time");
|
throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time");
|
||||||
};
|
};
|
||||||
match intrinsic_name {
|
match intrinsic_name {
|
||||||
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
|
sym::ptr_guaranteed_cmp => {
|
||||||
let a = ecx.read_scalar(&args[0])?;
|
let a = ecx.read_scalar(&args[0])?;
|
||||||
let b = ecx.read_scalar(&args[1])?;
|
let b = ecx.read_scalar(&args[1])?;
|
||||||
let cmp = if intrinsic_name == sym::ptr_guaranteed_eq {
|
let cmp = ecx.guaranteed_cmp(a, b)?;
|
||||||
ecx.guaranteed_eq(a, b)?
|
ecx.write_scalar(Scalar::from_u8(cmp), dest)?;
|
||||||
} else {
|
|
||||||
ecx.guaranteed_ne(a, b)?
|
|
||||||
};
|
|
||||||
ecx.write_scalar(Scalar::from_bool(cmp), dest)?;
|
|
||||||
}
|
}
|
||||||
sym::const_allocate => {
|
sym::const_allocate => {
|
||||||
let size = ecx.read_scalar(&args[0])?.to_machine_usize(ecx)?;
|
let size = ecx.read_scalar(&args[0])?.to_machine_usize(ecx)?;
|
||||||
|
@ -97,7 +97,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Raw pointers are not allowed in type level constants, as we cannot properly test them for
|
// Raw pointers are not allowed in type level constants, as we cannot properly test them for
|
||||||
// equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
|
// equality at compile-time (see `ptr_guaranteed_cmp`).
|
||||||
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
|
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
|
||||||
// agree with runtime equality tests.
|
// agree with runtime equality tests.
|
||||||
ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),
|
ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),
|
||||||
|
@ -1117,8 +1117,7 @@ symbols! {
|
|||||||
profiler_builtins,
|
profiler_builtins,
|
||||||
profiler_runtime,
|
profiler_runtime,
|
||||||
ptr,
|
ptr,
|
||||||
ptr_guaranteed_eq,
|
ptr_guaranteed_cmp,
|
||||||
ptr_guaranteed_ne,
|
|
||||||
ptr_mask,
|
ptr_mask,
|
||||||
ptr_null,
|
ptr_null,
|
||||||
ptr_null_mut,
|
ptr_null_mut,
|
||||||
|
@ -95,8 +95,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
|
|||||||
| sym::type_id
|
| sym::type_id
|
||||||
| sym::likely
|
| sym::likely
|
||||||
| sym::unlikely
|
| sym::unlikely
|
||||||
| sym::ptr_guaranteed_eq
|
| sym::ptr_guaranteed_cmp
|
||||||
| sym::ptr_guaranteed_ne
|
|
||||||
| sym::minnumf32
|
| sym::minnumf32
|
||||||
| sym::minnumf64
|
| sym::minnumf64
|
||||||
| sym::maxnumf32
|
| sym::maxnumf32
|
||||||
@ -302,8 +301,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
|
|||||||
(1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
|
(1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
|
||||||
}
|
}
|
||||||
|
|
||||||
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
|
sym::ptr_guaranteed_cmp => {
|
||||||
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
|
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
sym::const_allocate => {
|
sym::const_allocate => {
|
||||||
|
@ -2013,21 +2013,24 @@ extern "rust-intrinsic" {
|
|||||||
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
|
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
|
||||||
|
|
||||||
/// See documentation of `<*const T>::guaranteed_eq` for details.
|
/// See documentation of `<*const T>::guaranteed_eq` for details.
|
||||||
|
/// Returns `2` if the result is unknown.
|
||||||
|
/// Returns `1` if the pointers are guaranteed equal
|
||||||
|
/// Returns `0` if the pointers are guaranteed inequal
|
||||||
///
|
///
|
||||||
/// Note that, unlike most intrinsics, this is safe to call;
|
/// Note that, unlike most intrinsics, this is safe to call;
|
||||||
/// it does not require an `unsafe` block.
|
/// it does not require an `unsafe` block.
|
||||||
/// Therefore, implementations must not require the user to uphold
|
/// Therefore, implementations must not require the user to uphold
|
||||||
/// any safety invariants.
|
/// any safety invariants.
|
||||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
pub fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8;
|
||||||
|
|
||||||
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
|
#[cfg(bootstrap)]
|
||||||
pub fn ptr_guaranteed_eq<T>(ptr: *const T, other: *const T) -> bool;
|
pub fn ptr_guaranteed_eq<T>(ptr: *const T, other: *const T) -> bool;
|
||||||
|
|
||||||
/// See documentation of `<*const T>::guaranteed_ne` for details.
|
|
||||||
///
|
|
||||||
/// Note that, unlike most intrinsics, this is safe to call;
|
|
||||||
/// it does not require an `unsafe` block.
|
|
||||||
/// Therefore, implementations must not require the user to uphold
|
|
||||||
/// any safety invariants.
|
|
||||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
|
#[cfg(bootstrap)]
|
||||||
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
|
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;
|
||||||
|
|
||||||
/// Allocates a block of memory at compile time.
|
/// 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) -
|
|||||||
diff >= size
|
diff >= size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(bootstrap)]
|
||||||
|
pub const fn ptr_guaranteed_cmp(a: *const (), b: *const ()) -> u8 {
|
||||||
|
match (ptr_guaranteed_eq(a, b), ptr_guaranteed_ne(a, b)) {
|
||||||
|
(false, false) => 2,
|
||||||
|
(true, false) => 1,
|
||||||
|
(false, true) => 0,
|
||||||
|
(true, true) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
|
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
|
||||||
/// and destination must *not* overlap.
|
/// and destination must *not* overlap.
|
||||||
///
|
///
|
||||||
|
@ -36,7 +36,10 @@ impl<T: ?Sized> *const T {
|
|||||||
pub const fn is_null(self) -> bool {
|
pub const fn is_null(self) -> bool {
|
||||||
// Compare via a cast to a thin pointer, so fat pointers are only
|
// Compare via a cast to a thin pointer, so fat pointers are only
|
||||||
// considering their "data" part for null-ness.
|
// considering their "data" part for null-ness.
|
||||||
(self as *const u8).guaranteed_eq(null())
|
match (self as *const u8).guaranteed_eq(null()) {
|
||||||
|
None => false,
|
||||||
|
Some(res) => res,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Casts to a pointer of another type.
|
/// Casts to a pointer of another type.
|
||||||
@ -770,20 +773,16 @@ impl<T: ?Sized> *const T {
|
|||||||
|
|
||||||
/// Returns whether two pointers are guaranteed to be equal.
|
/// Returns whether two pointers are guaranteed to be equal.
|
||||||
///
|
///
|
||||||
/// At runtime this function behaves like `self == other`.
|
/// At runtime this function behaves like `Some(self == other)`.
|
||||||
/// However, in some contexts (e.g., compile-time evaluation),
|
/// However, in some contexts (e.g., compile-time evaluation),
|
||||||
/// it is not always possible to determine equality of two pointers, so this function may
|
/// 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.
|
/// spuriously return `None` for pointers that later actually turn out to have its equality known.
|
||||||
/// But when it returns `true`, the pointers are guaranteed to be equal.
|
/// But when it returns `Some`, the pointers' equality is guaranteed to be known.
|
||||||
///
|
///
|
||||||
/// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer
|
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
|
||||||
/// comparisons for which both functions return `false`.
|
/// version and unsafe code must not
|
||||||
///
|
|
||||||
/// [`guaranteed_ne`]: #method.guaranteed_ne
|
|
||||||
///
|
|
||||||
/// The return value may change depending on the compiler version and unsafe code must not
|
|
||||||
/// rely on the result of this function for soundness. It is suggested to only use this function
|
/// 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
|
/// for performance optimizations where spurious `None` return values by this function do not
|
||||||
/// affect the outcome, but just the performance.
|
/// affect the outcome, but just the performance.
|
||||||
/// The consequences of using this method to make runtime and compile-time code behave
|
/// 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
|
/// differently have not been explored. This method should not be used to introduce such
|
||||||
@ -792,29 +791,28 @@ impl<T: ?Sized> *const T {
|
|||||||
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn guaranteed_eq(self, other: *const T) -> bool
|
pub const fn guaranteed_eq(self, other: *const T) -> Option<bool>
|
||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
intrinsics::ptr_guaranteed_eq(self, other)
|
match intrinsics::ptr_guaranteed_cmp(self as _, other as _) {
|
||||||
|
2 => None,
|
||||||
|
other => Some(other == 1),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether two pointers are guaranteed to be unequal.
|
/// Returns whether two pointers are guaranteed to be inequal.
|
||||||
///
|
///
|
||||||
/// At runtime this function behaves like `self != other`.
|
/// At runtime this function behaves like `Some(self == other)`.
|
||||||
/// However, in some contexts (e.g., compile-time evaluation),
|
/// 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
|
/// it is not always possible to determine inequality of two pointers, so this function may
|
||||||
/// spuriously return `false` for pointers that later actually turn out to be unequal.
|
/// spuriously return `None` for pointers that later actually turn out to have its inequality known.
|
||||||
/// But when it returns `true`, the pointers are guaranteed to be unequal.
|
/// But when it returns `Some`, the pointers' inequality is guaranteed to be known.
|
||||||
///
|
///
|
||||||
/// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer
|
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
|
||||||
/// comparisons for which both functions return `false`.
|
/// version and unsafe code must not
|
||||||
///
|
|
||||||
/// [`guaranteed_eq`]: #method.guaranteed_eq
|
|
||||||
///
|
|
||||||
/// The return value may change depending on the compiler version and unsafe code must not
|
|
||||||
/// rely on the result of this function for soundness. It is suggested to only use this function
|
/// 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
|
/// for performance optimizations where spurious `None` return values by this function do not
|
||||||
/// affect the outcome, but just the performance.
|
/// affect the outcome, but just the performance.
|
||||||
/// The consequences of using this method to make runtime and compile-time code behave
|
/// 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
|
/// differently have not been explored. This method should not be used to introduce such
|
||||||
@ -823,11 +821,14 @@ impl<T: ?Sized> *const T {
|
|||||||
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn guaranteed_ne(self, other: *const T) -> bool
|
pub const fn guaranteed_ne(self, other: *const T) -> Option<bool>
|
||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
intrinsics::ptr_guaranteed_ne(self, other)
|
match self.guaranteed_eq(other) {
|
||||||
|
None => None,
|
||||||
|
Some(eq) => Some(!eq),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
|
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
|
||||||
|
@ -35,7 +35,10 @@ impl<T: ?Sized> *mut T {
|
|||||||
pub const fn is_null(self) -> bool {
|
pub const fn is_null(self) -> bool {
|
||||||
// Compare via a cast to a thin pointer, so fat pointers are only
|
// Compare via a cast to a thin pointer, so fat pointers are only
|
||||||
// considering their "data" part for null-ness.
|
// considering their "data" part for null-ness.
|
||||||
(self as *mut u8).guaranteed_eq(null_mut())
|
match (self as *mut u8).guaranteed_eq(null_mut()) {
|
||||||
|
None => false,
|
||||||
|
Some(res) => res,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Casts to a pointer of another type.
|
/// Casts to a pointer of another type.
|
||||||
@ -697,20 +700,16 @@ impl<T: ?Sized> *mut T {
|
|||||||
|
|
||||||
/// Returns whether two pointers are guaranteed to be equal.
|
/// Returns whether two pointers are guaranteed to be equal.
|
||||||
///
|
///
|
||||||
/// At runtime this function behaves like `self == other`.
|
/// At runtime this function behaves like `Some(self == other)`.
|
||||||
/// However, in some contexts (e.g., compile-time evaluation),
|
/// However, in some contexts (e.g., compile-time evaluation),
|
||||||
/// it is not always possible to determine equality of two pointers, so this function may
|
/// 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.
|
/// spuriously return `None` for pointers that later actually turn out to have its equality known.
|
||||||
/// But when it returns `true`, the pointers are guaranteed to be equal.
|
/// But when it returns `Some`, the pointers' equality is guaranteed to be known.
|
||||||
///
|
///
|
||||||
/// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer
|
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
|
||||||
/// comparisons for which both functions return `false`.
|
/// version and unsafe code must not
|
||||||
///
|
|
||||||
/// [`guaranteed_ne`]: #method.guaranteed_ne
|
|
||||||
///
|
|
||||||
/// The return value may change depending on the compiler version and unsafe code might not
|
|
||||||
/// rely on the result of this function for soundness. It is suggested to only use this function
|
/// 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
|
/// for performance optimizations where spurious `None` return values by this function do not
|
||||||
/// affect the outcome, but just the performance.
|
/// affect the outcome, but just the performance.
|
||||||
/// The consequences of using this method to make runtime and compile-time code behave
|
/// 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
|
/// differently have not been explored. This method should not be used to introduce such
|
||||||
@ -719,29 +718,25 @@ impl<T: ?Sized> *mut T {
|
|||||||
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn guaranteed_eq(self, other: *mut T) -> bool
|
pub const fn guaranteed_eq(self, other: *mut T) -> Option<bool>
|
||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
intrinsics::ptr_guaranteed_eq(self as *const _, other as *const _)
|
(self as *const T).guaranteed_eq(other as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether two pointers are guaranteed to be unequal.
|
/// Returns whether two pointers are guaranteed to be inequal.
|
||||||
///
|
///
|
||||||
/// At runtime this function behaves like `self != other`.
|
/// At runtime this function behaves like `Some(self == other)`.
|
||||||
/// However, in some contexts (e.g., compile-time evaluation),
|
/// 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
|
/// it is not always possible to determine inequality of two pointers, so this function may
|
||||||
/// spuriously return `false` for pointers that later actually turn out to be unequal.
|
/// spuriously return `None` for pointers that later actually turn out to have its inequality known.
|
||||||
/// But when it returns `true`, the pointers are guaranteed to be unequal.
|
/// But when it returns `Some`, the pointers' inequality is guaranteed to be known.
|
||||||
///
|
///
|
||||||
/// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer
|
/// The return value may change from `Some` to `None` and vice versa depending on the compiler
|
||||||
/// comparisons for which both functions return `false`.
|
/// version and unsafe code must not
|
||||||
///
|
|
||||||
/// [`guaranteed_eq`]: #method.guaranteed_eq
|
|
||||||
///
|
|
||||||
/// The return value may change depending on the compiler version and unsafe code might not
|
|
||||||
/// rely on the result of this function for soundness. It is suggested to only use this function
|
/// 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
|
/// for performance optimizations where spurious `None` return values by this function do not
|
||||||
/// affect the outcome, but just the performance.
|
/// affect the outcome, but just the performance.
|
||||||
/// The consequences of using this method to make runtime and compile-time code behave
|
/// 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
|
/// differently have not been explored. This method should not be used to introduce such
|
||||||
@ -750,11 +745,11 @@ impl<T: ?Sized> *mut T {
|
|||||||
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const unsafe fn guaranteed_ne(self, other: *mut T) -> bool
|
pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
|
||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
intrinsics::ptr_guaranteed_ne(self as *const _, other as *const _)
|
(self as *const T).guaranteed_ne(other as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the distance between two pointers. The returned value is in
|
/// Calculates the distance between two pointers. The returned value is in
|
||||||
|
@ -4,14 +4,10 @@
|
|||||||
#![feature(const_raw_ptr_comparison)]
|
#![feature(const_raw_ptr_comparison)]
|
||||||
|
|
||||||
const EMPTY_SLICE: &[i32] = &[];
|
const EMPTY_SLICE: &[i32] = &[];
|
||||||
const EMPTY_EQ: bool = EMPTY_SLICE.as_ptr().guaranteed_eq(&[] as *const _);
|
const EMPTY_EQ: Option<bool> = EMPTY_SLICE.as_ptr().guaranteed_eq(&[] as *const _);
|
||||||
const EMPTY_EQ2: bool = EMPTY_SLICE.as_ptr().guaranteed_ne(&[] as *const _);
|
const EMPTY_EQ2: Option<bool> = EMPTY_SLICE.as_ptr().guaranteed_eq(&[1] as *const _);
|
||||||
const EMPTY_NE: bool = EMPTY_SLICE.as_ptr().guaranteed_ne(&[1] as *const _);
|
|
||||||
const EMPTY_NE2: bool = EMPTY_SLICE.as_ptr().guaranteed_eq(&[1] as *const _);
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
assert!(!EMPTY_EQ);
|
assert!(EMPTY_EQ.is_none());
|
||||||
assert!(!EMPTY_EQ2);
|
assert!(EMPTY_EQ2.is_none());
|
||||||
assert!(!EMPTY_NE);
|
|
||||||
assert!(!EMPTY_NE2);
|
|
||||||
}
|
}
|
||||||
|
@ -14,38 +14,30 @@ const FOO: &usize = &42;
|
|||||||
macro_rules! check {
|
macro_rules! check {
|
||||||
(eq, $a:expr, $b:expr) => {
|
(eq, $a:expr, $b:expr) => {
|
||||||
pub const _: () =
|
pub const _: () =
|
||||||
assert!(std::intrinsics::ptr_guaranteed_eq($a as *const u8, $b as *const u8));
|
assert!(std::intrinsics::ptr_guaranteed_cmp($a as *const u8, $b as *const u8) == 1);
|
||||||
};
|
};
|
||||||
(ne, $a:expr, $b:expr) => {
|
(ne, $a:expr, $b:expr) => {
|
||||||
pub const _: () =
|
pub const _: () =
|
||||||
assert!(std::intrinsics::ptr_guaranteed_ne($a as *const u8, $b as *const u8));
|
assert!(std::intrinsics::ptr_guaranteed_cmp($a as *const u8, $b as *const u8) == 0);
|
||||||
};
|
};
|
||||||
(!eq, $a:expr, $b:expr) => {
|
(!, $a:expr, $b:expr) => {
|
||||||
pub const _: () =
|
pub const _: () =
|
||||||
assert!(!std::intrinsics::ptr_guaranteed_eq($a as *const u8, $b as *const u8));
|
assert!(std::intrinsics::ptr_guaranteed_cmp($a as *const u8, $b as *const u8) == 2);
|
||||||
};
|
|
||||||
(!ne, $a:expr, $b:expr) => {
|
|
||||||
pub const _: () =
|
|
||||||
assert!(!std::intrinsics::ptr_guaranteed_ne($a as *const u8, $b as *const u8));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
check!(eq, 0, 0);
|
check!(eq, 0, 0);
|
||||||
check!(ne, 0, 1);
|
check!(ne, 0, 1);
|
||||||
check!(!eq, 0, 1);
|
|
||||||
check!(!ne, 0, 0);
|
|
||||||
check!(ne, FOO as *const _, 0);
|
check!(ne, FOO as *const _, 0);
|
||||||
check!(!eq, FOO as *const _, 0);
|
check!(ne, unsafe { (FOO as *const usize).offset(1) }, 0);
|
||||||
|
check!(ne, unsafe { (FOO as *const usize as *const u8).offset(3) }, 0);
|
||||||
|
|
||||||
// We want pointers to be equal to themselves, but aren't checking this yet because
|
// We want pointers to be equal to themselves, but aren't checking this yet because
|
||||||
// there are some open questions (e.g. whether function pointers to the same function
|
// there are some open questions (e.g. whether function pointers to the same function
|
||||||
// compare equal, they don't necessarily at runtime).
|
// compare equal, they don't necessarily at runtime).
|
||||||
// The case tested here should work eventually, but does not work yet.
|
// The case tested here should work eventually, but does not work yet.
|
||||||
check!(!eq, FOO as *const _, FOO as *const _);
|
check!(!, FOO as *const _, FOO as *const _);
|
||||||
check!(ne, unsafe { (FOO as *const usize).offset(1) }, 0);
|
|
||||||
check!(!eq, unsafe { (FOO as *const usize).offset(1) }, 0);
|
|
||||||
|
|
||||||
check!(ne, unsafe { (FOO as *const usize as *const u8).offset(3) }, 0);
|
|
||||||
check!(!eq, unsafe { (FOO as *const usize as *const u8).offset(3) }, 0);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// If any of the below start compiling, make sure to add a `check` test for it.
|
// If any of the below start compiling, make sure to add a `check` test for it.
|
||||||
|
@ -7,19 +7,19 @@ LL | unsafe { intrinsics::offset(self, count) }
|
|||||||
| out-of-bounds pointer arithmetic: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds
|
| out-of-bounds pointer arithmetic: alloc3 has size $WORD, so pointer to $TWO_WORDS bytes starting at offset 0 is out-of-bounds
|
||||||
| inside `ptr::const_ptr::<impl *const usize>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
| inside `ptr::const_ptr::<impl *const usize>::offset` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $DIR/ptr_comparisons.rs:58:34
|
::: $DIR/ptr_comparisons.rs:50:34
|
||||||
|
|
|
|
||||||
LL | const _: *const usize = unsafe { (FOO as *const usize).offset(2) };
|
LL | const _: *const usize = unsafe { (FOO as *const usize).offset(2) };
|
||||||
| ------------------------------- inside `_` at $DIR/ptr_comparisons.rs:58:34
|
| ------------------------------- inside `_` at $DIR/ptr_comparisons.rs:50:34
|
||||||
|
|
||||||
error[E0080]: evaluation of constant value failed
|
error[E0080]: evaluation of constant value failed
|
||||||
--> $DIR/ptr_comparisons.rs:61:33
|
--> $DIR/ptr_comparisons.rs:53:33
|
||||||
|
|
|
|
||||||
LL | unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) };
|
LL | unsafe { std::ptr::addr_of!((*(FOO as *const usize as *const [u8; 1000]))[999]) };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: alloc3 has size $WORD, so pointer to 1000 bytes starting at offset 0 is out-of-bounds
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: alloc3 has size $WORD, so pointer to 1000 bytes starting at offset 0 is out-of-bounds
|
||||||
|
|
||||||
error: any use of this value will cause an error
|
error: any use of this value will cause an error
|
||||||
--> $DIR/ptr_comparisons.rs:65:27
|
--> $DIR/ptr_comparisons.rs:57:27
|
||||||
|
|
|
|
||||||
LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
|
LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
|
||||||
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
||||||
@ -31,7 +31,7 @@ LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) +
|
|||||||
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
|
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
|
||||||
|
|
||||||
error: any use of this value will cause an error
|
error: any use of this value will cause an error
|
||||||
--> $DIR/ptr_comparisons.rs:70:27
|
--> $DIR/ptr_comparisons.rs:62:27
|
||||||
|
|
|
|
||||||
LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 };
|
LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 };
|
||||||
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
||||||
@ -46,7 +46,7 @@ error: aborting due to 4 previous errors
|
|||||||
For more information about this error, try `rustc --explain E0080`.
|
For more information about this error, try `rustc --explain E0080`.
|
||||||
Future incompatibility report: Future breakage diagnostic:
|
Future incompatibility report: Future breakage diagnostic:
|
||||||
error: any use of this value will cause an error
|
error: any use of this value will cause an error
|
||||||
--> $DIR/ptr_comparisons.rs:65:27
|
--> $DIR/ptr_comparisons.rs:57:27
|
||||||
|
|
|
|
||||||
LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
|
LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) + 4 };
|
||||||
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
||||||
@ -59,7 +59,7 @@ LL | const _: usize = unsafe { std::mem::transmute::<*const usize, usize>(FOO) +
|
|||||||
|
|
||||||
Future breakage diagnostic:
|
Future breakage diagnostic:
|
||||||
error: any use of this value will cause an error
|
error: any use of this value will cause an error
|
||||||
--> $DIR/ptr_comparisons.rs:70:27
|
--> $DIR/ptr_comparisons.rs:62:27
|
||||||
|
|
|
|
||||||
LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 };
|
LL | const _: usize = unsafe { *std::mem::transmute::<&&usize, &usize>(&FOO) + 4 };
|
||||||
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
| -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
|
||||||
|
Loading…
Reference in New Issue
Block a user