Skip to content
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

The <*const T>::guaranteed_* methods now return an option for the unknown case #101483

Merged
merged 1 commit into from
Sep 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,20 +819,13 @@ fn codegen_regular_intrinsic_call<'tcx>(
ret.write_cvalue(fx, val);
}

sym::ptr_guaranteed_eq => {
sym::ptr_guaranteed_cmp => {
intrinsic_args!(fx, args => (a, b); intrinsic);

let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b);
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 => {
intrinsic_args!(fx, args => (); intrinsic);

Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,14 +555,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return;
}

sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
sym::ptr_guaranteed_cmp => {
let a = args[0].immediate();
let b = args[1].immediate();
if name == sym::ptr_guaranteed_eq {
bx.icmp(IntPredicate::IntEQ, a, b)
} else {
bx.icmp(IntPredicate::IntNE, a, b)
}
bx.icmp(IntPredicate::IntEQ, a, b)
}

sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
Expand Down
49 changes: 23 additions & 26 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,34 +191,35 @@ impl interpret::MayLeak for ! {
}

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) {
// Comparisons between integers are always known.
(Scalar::Int { .. }, Scalar::Int { .. }) => a == b,
// Equality with integers can never be known for sure.
(Scalar::Int { .. }, Scalar::Ptr(..)) | (Scalar::Ptr(..), Scalar::Int { .. }) => false,
// FIXME: return `true` 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).
(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,
(Scalar::Int { .. }, Scalar::Int { .. }) => {
if a == b {
1
} else {
0
}
}
// 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.
// Inequality with integers other than null can never be known for sure.
(Scalar::Int(int), ptr @ Scalar::Ptr(..))
| (ptr @ Scalar::Ptr(..), Scalar::Int(int)) => {
int.is_null() && !self.scalar_may_be_null(ptr)?
| (ptr @ Scalar::Ptr(..), Scalar::Int(int))
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.
// Examples include comparison of addresses in different static items.
(Scalar::Ptr(..), Scalar::Ptr(..)) => false,
(Scalar::Ptr(..), Scalar::Ptr(..)) => 2,
})
}
}
Expand Down Expand Up @@ -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");
};
match intrinsic_name {
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
sym::ptr_guaranteed_cmp => {
let a = ecx.read_scalar(&args[0])?;
let b = ecx.read_scalar(&args[1])?;
let cmp = if intrinsic_name == sym::ptr_guaranteed_eq {
ecx.guaranteed_eq(a, b)?
} else {
ecx.guaranteed_ne(a, b)?
};
ecx.write_scalar(Scalar::from_bool(cmp), dest)?;
let cmp = ecx.guaranteed_cmp(a, b)?;
ecx.write_scalar(Scalar::from_u8(cmp), dest)?;
}
sym::const_allocate => {
let size = ecx.read_scalar(&args[0])?.to_machine_usize(ecx)?;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// 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
// agree with runtime equality tests.
ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType),
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,8 +1118,7 @@ symbols! {
profiler_builtins,
profiler_runtime,
ptr,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
ptr_guaranteed_cmp,
ptr_mask,
ptr_null,
ptr_null_mut,
Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_typeck/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_id
| sym::likely
| sym::unlikely
| sym::ptr_guaranteed_eq
| sym::ptr_guaranteed_ne
| sym::ptr_guaranteed_cmp
| sym::minnumf32
| sym::minnumf64
| sym::maxnumf32
Expand Down Expand Up @@ -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]))
}

sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
sym::ptr_guaranteed_cmp => {
(1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
}

sym::const_allocate => {
Expand Down
25 changes: 19 additions & 6 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2013,21 +2013,24 @@ extern "rust-intrinsic" {
pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;

/// 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;
/// 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")]
#[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;

/// 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")]
#[cfg(bootstrap)]
pub fn ptr_guaranteed_ne<T>(ptr: *const T, other: *const T) -> bool;

/// Allocates a block of memory at compile time.
Expand Down Expand Up @@ -2213,6 +2216,16 @@ pub(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, count: usize) -
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
/// and destination must *not* overlap.
///
Expand Down
55 changes: 28 additions & 27 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ impl<T: ?Sized> *const T {
pub const fn is_null(self) -> bool {
// Compare via a cast to a thin pointer, so fat pointers are only
// 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.
Expand Down Expand Up @@ -770,20 +773,16 @@ impl<T: ?Sized> *const T {

/// 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),
/// 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`.
/// spuriously return `None` for pointers that later actually turn out to have its equality known.
/// But when it returns `Some`, the pointers' equality is guaranteed to be known.
///
/// [`guaranteed_ne`]: #method.guaranteed_ne
///
/// The return value may change depending on the compiler version and unsafe code must not
/// The return value may change from `Some` to `None` and vice versa 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
/// 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.
/// 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
Expand All @@ -792,29 +791,28 @@ impl<T: ?Sized> *const T {
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[inline]
pub const fn guaranteed_eq(self, other: *const T) -> bool
pub const fn guaranteed_eq(self, other: *const T) -> Option<bool>
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
where
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),
/// 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 unequal.
/// But when it returns `true`, the pointers are guaranteed to be unequal.
/// it is not always possible to determine inequality of two pointers, so this function may
/// spuriously return `None` for pointers that later actually turn out to have its inequality known.
/// 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
/// 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 must not
/// The return value may change from `Some` to `None` and vice versa 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
/// 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.
/// 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
Expand All @@ -823,11 +821,14 @@ impl<T: ?Sized> *const T {
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[inline]
pub const fn guaranteed_ne(self, other: *const T) -> bool
pub const fn guaranteed_ne(self, other: *const T) -> Option<bool>
where
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)`).
Expand Down
49 changes: 22 additions & 27 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ impl<T: ?Sized> *mut T {
pub const fn is_null(self) -> bool {
// Compare via a cast to a thin pointer, so fat pointers are only
// 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,
}
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
}

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

/// 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),
/// 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
/// spuriously return `None` for pointers that later actually turn out to have its equality known.
/// But when it returns `Some`, the pointers' equality is guaranteed to be known.
///
/// The return value may change depending on the compiler version and unsafe code might not
/// The return value may change from `Some` to `None` and vice versa 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
/// 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.
/// 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
Expand All @@ -719,29 +718,25 @@ impl<T: ?Sized> *mut T {
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[inline]
pub const fn guaranteed_eq(self, other: *mut T) -> bool
pub const fn guaranteed_eq(self, other: *mut T) -> Option<bool>
where
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),
/// 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 unequal.
/// But when it returns `true`, the pointers are guaranteed to be unequal.
///
/// 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
/// it is not always possible to determine inequality of two pointers, so this function may
/// spuriously return `None` for pointers that later actually turn out to have its inequality known.
/// But when it returns `Some`, the pointers' inequality is guaranteed to be known.
///
/// The return value may change depending on the compiler version and unsafe code might not
/// The return value may change from `Some` to `None` and vice versa 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
/// 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.
/// 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
Expand All @@ -750,11 +745,11 @@ impl<T: ?Sized> *mut T {
#[unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
#[inline]
pub const unsafe fn guaranteed_ne(self, other: *mut T) -> bool
pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
where
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
Expand Down
Loading