Skip to content

Commit d61c58a

Browse files
committed
Combine tests/ui/const-ptr/guaranteed_cmp.rs into tests/ui/consts/ptr_comparisons.rs
1 parent 252f0e0 commit d61c58a

File tree

2 files changed

+171
-214
lines changed

2 files changed

+171
-214
lines changed

tests/ui/const-ptr/guaranteed_cmp.rs

Lines changed: 0 additions & 184 deletions
This file was deleted.

tests/ui/consts/ptr_comparisons.rs

Lines changed: 171 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,184 @@
11
//@ compile-flags: --crate-type=lib
22
//@ check-pass
3+
//@ edition: 2024
4+
#![feature(const_raw_ptr_comparison)]
5+
#![feature(fn_align)]
6+
// Generally:
7+
// For any `Some` return, `None` would also be valid, unless otherwise noted.
8+
// For any `None` return, only `None` is valid, unless otherwise noted.
39

4-
#![feature(
5-
core_intrinsics,
6-
const_raw_ptr_comparison,
7-
)]
10+
macro_rules! do_test {
11+
($a:expr, $b:expr, $expected:pat) => {
12+
const _: () = {
13+
let a: *const _ = $a;
14+
let b: *const _ = $b;
15+
assert!(matches!(<*const u8>::guaranteed_eq(a.cast(), b.cast()), $expected));
16+
};
17+
};
18+
}
819

9-
const FOO: &usize = &42;
20+
#[repr(align(2))]
21+
struct T(#[allow(unused)] u16);
1022

11-
macro_rules! check {
12-
(eq, $a:expr, $b:expr) => {
13-
pub const _: () =
14-
assert!(std::intrinsics::ptr_guaranteed_cmp($a as *const u8, $b as *const u8) == 1);
15-
};
16-
(ne, $a:expr, $b:expr) => {
17-
pub const _: () =
18-
assert!(std::intrinsics::ptr_guaranteed_cmp($a as *const u8, $b as *const u8) == 0);
23+
#[repr(align(2))]
24+
struct AlignedZst;
25+
26+
static A: T = T(42);
27+
static B: T = T(42);
28+
static mut MUT_STATIC: T = T(42);
29+
static ZST: () = ();
30+
static ALIGNED_ZST: AlignedZst = AlignedZst;
31+
static LARGE_WORD_ALIGNED: [usize; 2] = [0, 1];
32+
static mut MUT_LARGE_WORD_ALIGNED: [usize; 2] = [0, 1];
33+
34+
const FN_PTR: *const () = {
35+
fn foo() {}
36+
unsafe { std::mem::transmute(foo as fn()) }
37+
};
38+
39+
const ALIGNED_FN_PTR: *const () = {
40+
#[rustc_align(2)]
41+
fn aligned_foo() {}
42+
unsafe { std::mem::transmute(aligned_foo as fn()) }
43+
};
44+
45+
// Only on armv5te-* and armv4t-*
46+
#[cfg(all(
47+
target_arch = "arm",
48+
not(target_feature = "v6"),
49+
))]
50+
const ALIGNED_THUMB_FN_PTR: *const () = {
51+
#[rustc_align(2)]
52+
#[instruction_set(arm::t32)]
53+
fn aligned_thumb_foo() {}
54+
unsafe { std::mem::transmute(aligned_thumb_foo as fn()) }
55+
};
56+
57+
trait Trait {
58+
#[allow(unused)]
59+
fn method(&self) -> u8;
60+
}
61+
impl Trait for u32 {
62+
fn method(&self) -> u8 { 1 }
63+
}
64+
impl Trait for i32 {
65+
fn method(&self) -> u8 { 2 }
66+
}
67+
68+
const VTABLE_PTR_1: *const () = {
69+
let [_data, vtable] = unsafe {
70+
std::mem::transmute::<&dyn Trait, [*const (); 2]>(&42_u32 as &dyn Trait)
1971
};
20-
(!, $a:expr, $b:expr) => {
21-
pub const _: () =
22-
assert!(std::intrinsics::ptr_guaranteed_cmp($a as *const u8, $b as *const u8) == 2);
72+
vtable
73+
};
74+
const VTABLE_PTR_2: *const () = {
75+
let [_data, vtable] = unsafe {
76+
std::mem::transmute::<&dyn Trait, [*const (); 2]>(&42_i32 as &dyn Trait)
2377
};
24-
}
78+
vtable
79+
};
2580

26-
check!(eq, 0, 0);
27-
check!(ne, 0, 1);
28-
check!(ne, FOO as *const _, 0);
29-
check!(ne, unsafe { (FOO as *const usize).offset(1) }, 0);
30-
check!(ne, unsafe { (FOO as *const usize as *const u8).offset(3) }, 0);
81+
// Cannot be `None`: `is_null` is stable with strong guarantees about integer-valued pointers.
82+
do_test!(0 as *const u8, 0 as *const u8, Some(true));
83+
do_test!(0 as *const u8, 1 as *const u8, Some(false));
3184

32-
// We want pointers to be equal to themselves, but aren't checking this yet because
33-
// there are some open questions (e.g. whether function pointers to the same function
34-
// compare equal: they don't necessarily do at runtime).
35-
check!(!, FOO as *const _, FOO as *const _);
85+
// Cannot be `None`: `static`s' addresses, references, (and within and one-past-the-end of those),
86+
// and `fn` pointers cannot be null, and `is_null` is stable with strong guarantees, and
87+
// `is_null` is implemented using `guaranteed_cmp`.
88+
do_test!(&A, 0 as *const u8, Some(false));
89+
do_test!((&raw const A).cast::<u8>().wrapping_add(1), 0 as *const u8, Some(false));
90+
do_test!((&raw const A).wrapping_add(1), 0 as *const u8, Some(false));
91+
do_test!(&ZST, 0 as *const u8, Some(false));
92+
do_test!(&(), 0 as *const u8, Some(false));
93+
do_test!(const { &() }, 0 as *const u8, Some(false));
94+
do_test!(FN_PTR, 0 as *const u8, Some(false));
3695

3796
// aside from 0, these pointers might end up pretty much anywhere.
38-
check!(!, FOO as *const _, 1); // this one could be `ne` by taking into account alignment
39-
check!(!, FOO as *const _, 1024);
97+
do_test!(&A, align_of::<T>() as *const u8, None);
98+
do_test!(&A, 1 as *const u8, Some(false)); // this one takes into account alignment, so we know that
4099

41100
// When pointers go out-of-bounds, they *might* become null, so these comparions cannot work.
42-
check!(!, unsafe { (FOO as *const usize).wrapping_add(2) }, 0);
43-
check!(!, unsafe { (FOO as *const usize).wrapping_sub(1) }, 0);
101+
do_test!((&raw const A).wrapping_add(2), 0 as *const u8, None);
102+
do_test!((&raw const A).wrapping_sub(1), 0 as *const u8, None);
103+
104+
// Statics cannot be duplicated
105+
do_test!(&A, &A, Some(true));
106+
107+
// Two non-ZST statics cannot have the same address
108+
do_test!(&A, &B, Some(false));
109+
do_test!(&A, &raw const MUT_STATIC, Some(false));
110+
111+
// One-past-the-end of one static can be equal to the address of another static.
112+
do_test!(&A, (&raw const B).wrapping_add(1), None);
113+
114+
// Cannot know if ZST static is at the same address with anything non-null (if alignment allows).
115+
do_test!(&A, &ZST, None);
116+
do_test!(&A, &ALIGNED_ZST, None);
117+
118+
// Unclear if ZST statics can be placed "in the middle of" non-ZST statics.
119+
// For now, we conservatively say they could, and return None here.
120+
do_test!(&ZST, (&raw const A).wrapping_byte_add(1), None);
121+
122+
// As per https://doc.rust-lang.org/nightly/reference/items/static-items.html#r-items.static.storage-disjointness
123+
// immutable statics are allowed to overlap with const items and promoteds.
124+
do_test!(&A, &T(42), None);
125+
do_test!(&A, const { &T(42) }, None);
126+
do_test!(&A, { const X: T = T(42); &X }, None);
127+
128+
// These could return Some(false), since only immutable statics can overlap with const items
129+
// and promoteds.
130+
do_test!(&raw const MUT_STATIC, &T(42), None);
131+
do_test!(&raw const MUT_STATIC, const { &T(42) }, None);
132+
do_test!(&raw const MUT_STATIC, { const X: T = T(42); &X }, None);
133+
134+
// An odd offset from a 2-aligned allocation can never be equal to an even offset from a
135+
// 2-aligned allocation, even if the offsets are out-of-bounds.
136+
do_test!(&A, (&raw const B).wrapping_byte_add(1), Some(false));
137+
do_test!(&A, (&raw const B).wrapping_byte_add(5), Some(false));
138+
do_test!(&A, (&raw const ALIGNED_ZST).wrapping_byte_add(1), Some(false));
139+
do_test!(&ALIGNED_ZST, (&raw const A).wrapping_byte_add(1), Some(false));
140+
do_test!(&A, (&T(42) as *const T).wrapping_byte_add(1), Some(false));
141+
do_test!(&A, (const { &T(42) } as *const T).wrapping_byte_add(1), Some(false));
142+
do_test!(&A, ({ const X: T = T(42); &X } as *const T).wrapping_byte_add(1), Some(false));
143+
144+
// Pointers into the same static are equal if and only if their offset is the same,
145+
// even if either is out-of-bounds.
146+
do_test!(&A, &A, Some(true));
147+
do_test!(&A, &A.0, Some(true));
148+
do_test!(&A, (&raw const A).wrapping_byte_add(1), Some(false));
149+
do_test!(&A, (&raw const A).wrapping_byte_add(2), Some(false));
150+
do_test!(&A, (&raw const A).wrapping_byte_add(51), Some(false));
151+
do_test!((&raw const A).wrapping_byte_add(51), (&raw const A).wrapping_byte_add(51), Some(true));
152+
153+
// Pointers to the same fn may be unequal, since `fn`s can be duplicated.
154+
do_test!(FN_PTR, FN_PTR, None);
155+
do_test!(ALIGNED_FN_PTR, ALIGNED_FN_PTR, None);
156+
157+
// Pointers to different fns may be equal, since `fn`s can be deduplicated.
158+
do_test!(FN_PTR, ALIGNED_FN_PTR, None);
159+
160+
// Pointers to the same vtable may be unequal, since vtables can be duplicated.
161+
do_test!(VTABLE_PTR_1, VTABLE_PTR_1, None);
162+
163+
// Pointers to different vtables may be equal, since vtables can be deduplicated.
164+
do_test!(VTABLE_PTR_1, VTABLE_PTR_2, None);
165+
166+
// Function pointers to aligned function allocations are not necessarily actually aligned,
167+
// due to platform-specific semantics.
168+
// See https://github.com/rust-lang/rust/issues/144661
169+
// FIXME: This could return `Some` on platforms where function pointers' addresses actually
170+
// correspond to function addresses including alignment, or on ARM if t32 function pointers
171+
// have their low bit set for consteval.
172+
do_test!(ALIGNED_FN_PTR, ALIGNED_FN_PTR.wrapping_byte_offset(1), None);
173+
#[cfg(all(
174+
target_arch = "arm",
175+
not(target_feature = "v6"),
176+
))]
177+
do_test!(ALIGNED_THUMB_FN_PTR, ALIGNED_THUMB_FN_PTR.wrapping_byte_offset(1), None);
178+
179+
// Conservatively say we don't know.
180+
do_test!(FN_PTR, VTABLE_PTR_1, None);
181+
do_test!((&raw const LARGE_WORD_ALIGNED).cast::<usize>().wrapping_add(1), VTABLE_PTR_1, None);
182+
do_test!((&raw const MUT_LARGE_WORD_ALIGNED).cast::<usize>().wrapping_add(1), VTABLE_PTR_1, None);
183+
do_test!((&raw const LARGE_WORD_ALIGNED).cast::<usize>().wrapping_add(1), FN_PTR, None);
184+
do_test!((&raw const MUT_LARGE_WORD_ALIGNED).cast::<usize>().wrapping_add(1), FN_PTR, None);

0 commit comments

Comments
 (0)