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:
bors 2022-09-10 09:50:21 +00:00
commit 5197c96c49
12 changed files with 119 additions and 138 deletions

View file

@ -816,20 +816,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);

View file

@ -551,14 +551,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 => {

View file

@ -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,
})
}
}
@ -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)?;

View file

@ -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),

View file

@ -1117,8 +1117,7 @@ symbols! {
profiler_builtins,
profiler_runtime,
ptr,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
ptr_guaranteed_cmp,
ptr_mask,
ptr_null,
ptr_null_mut,

View file

@ -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
@ -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 => {