Skip to content

Commit 41ff460

Browse files
committed
Auto merge of #127003 - GrigorenkoPV:107975, r=SparrowLii
Add a test for #107975 The int is zero. But also not zero. This is so much fun. This is a part of #105107. Initially I was going to just rebase #108445, but quite a few things changed since then: * The [mcve](#105787 (comment)) used for #105787 got fixed.[^upd2] * You can't just `a ?= b` for #107975 anymore. Now you have to `a-b ?= 0`. This is what this PR does. As an additional flex, it show that three ways of converting a pointer to its address have this issue: 1. `as usize` 2. `.expose_provenance()` 3. `.addr()` * #108425 simply got fixed. Yay. As an aside, the naming for `addr_of!` is quite unfortunate in context of provenance APIs. Because `addr_of!` gives you a pointer, but what provenance APIs refer to as "address" is the `usize` value. Oh well. UPD1: GitHub is incapable of parsing #107975 in the PR name, so let's add it here. [^upd2]: UPD2: [The other mcve](#105787 (comment)) does not work anymore either, saying "this behavior recently changed as a result of a bug fix; see #56105 for details."
2 parents 9057c3f + 2b08914 commit 41ff460

25 files changed

+799
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
See https://github.com/rust-lang/rust/issues/107975
2+
3+
Basically, if you have two pointers with the same address but from two different allocations,
4+
the compiler gets confused whether their addresses are equal or not,
5+
resulting in some self-contradictory behavior of the compiled code.
6+
7+
This folder contains some examples.
8+
They all boil down to allocating a variable on the stack, taking its address,
9+
getting rid of the variable, and then doing it all again.
10+
This way we end up with two addresses stored in two `usize`s (`a` and `b`).
11+
The addresses are (probably) equal but (definitely) come from two different allocations.
12+
Logically, we would expect that exactly one of the following options holds true:
13+
1. `a == b`
14+
2. `a != b`
15+
Sadly, the compiler does not always agree.
16+
17+
Due to Rust having at least three meaningfully different ways
18+
to get a variable's address as an `usize`,
19+
each example is provided in three versions, each in the corresponding subfolder:
20+
1. `./as-cast/` for `&v as *const _ as usize`,
21+
2. `./strict-provenance/` for `addr_of!(v).addr()`,
22+
2. `./exposed-provenance/` for `addr_of!(v).expose_provenance()`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
fn main() {
6+
let a: usize = {
7+
let v = 0u8;
8+
&v as *const _ as usize
9+
};
10+
let b: usize = {
11+
let v = 0u8;
12+
&v as *const _ as usize
13+
};
14+
15+
// `a` and `b` are not equal.
16+
assert_ne!(a, b);
17+
// But they are the same number.
18+
assert_eq!(format!("{a}"), format!("{b}"));
19+
// And they are equal.
20+
assert_eq!(a, b);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908
6+
7+
fn f() -> usize {
8+
let v = 0;
9+
&v as *const _ as usize
10+
}
11+
12+
fn main() {
13+
let a = f();
14+
let b = f();
15+
16+
// `a` and `b` are not equal.
17+
assert_ne!(a, b);
18+
// But they are the same number.
19+
assert_eq!(format!("{a}"), format!("{b}"));
20+
// And they are equal.
21+
assert_eq!(a, b);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
6+
7+
#[inline(never)]
8+
fn cmp(a: usize, b: usize) -> bool {
9+
a == b
10+
}
11+
12+
#[inline(always)]
13+
fn cmp_in(a: usize, b: usize) -> bool {
14+
a == b
15+
}
16+
17+
fn main() {
18+
let a = {
19+
let v = 0;
20+
&v as *const _ as usize
21+
};
22+
let b = {
23+
let v = 0;
24+
&v as *const _ as usize
25+
};
26+
assert_eq!(format!("{}", a == b), "false");
27+
assert_eq!(format!("{}", cmp_in(a, b)), "false");
28+
assert_eq!(format!("{}", cmp(a, b)), "true");
29+
assert_eq!(a.to_string(), b.to_string());
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
6+
7+
#[inline(never)]
8+
fn cmp(a: usize, b: usize) -> bool {
9+
a == b
10+
}
11+
12+
#[inline(always)]
13+
fn cmp_in(a: usize, b: usize) -> bool {
14+
a == b
15+
}
16+
17+
fn main() {
18+
let a = {
19+
let v = 0;
20+
&v as *const _ as usize
21+
};
22+
let b = {
23+
let v = 0;
24+
&v as *const _ as usize
25+
};
26+
assert_eq!(a.to_string(), b.to_string());
27+
assert_eq!(format!("{}", a == b), "true");
28+
assert_eq!(format!("{}", cmp_in(a, b)), "true");
29+
assert_eq!(format!("{}", cmp(a, b)), "true");
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
6+
7+
fn main() {
8+
let a = {
9+
let v = 0;
10+
&v as *const _ as usize
11+
};
12+
let b = {
13+
let v = 0;
14+
&v as *const _ as usize
15+
};
16+
17+
assert_ne!(a, b);
18+
println!("{a}"); // or b
19+
assert_eq!(a, b);
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1430704499
6+
7+
fn main() {
8+
let a = {
9+
let v = 0;
10+
&v as *const _ as usize
11+
};
12+
let b = {
13+
let v = 0;
14+
&v as *const _ as usize
15+
};
16+
17+
assert_ne!(a, b);
18+
assert_ne!(a, b);
19+
let c = a;
20+
assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "false true false");
21+
println!("{a} {b}");
22+
assert_eq!(format!("{} {} {}", a == b, a == c, b == c), "true true true");
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
6+
7+
use std::cell::{Ref, RefCell};
8+
9+
fn main() {
10+
let a: usize = {
11+
let v = 0u8;
12+
&v as *const _ as usize
13+
};
14+
let b: usize = {
15+
let v = 0u8;
16+
&v as *const _ as usize
17+
};
18+
let i: usize = b - a;
19+
20+
// A surprise tool that will help us later.
21+
let arr = [
22+
RefCell::new(Some(Box::new(1u8))),
23+
RefCell::new(None),
24+
RefCell::new(None),
25+
RefCell::new(None),
26+
];
27+
28+
// `i` is not 0
29+
assert_ne!(i, 0);
30+
31+
// Let's borrow the `i`-th element.
32+
// If `i` is out of bounds, indexing will panic.
33+
let r: Ref<Option<Box<u8>>> = arr[i].borrow();
34+
35+
// If we got here, it means `i` was in bounds.
36+
// Now, two options are possible:
37+
// EITHER `i` is not 0 (as we have asserted above),
38+
// so the unwrap will panic, because only the 0-th element is `Some`
39+
// OR the assert lied, `i` *is* 0, and the `unwrap` will not panic.
40+
let r: &Box<u8> = r.as_ref().unwrap();
41+
42+
// If we got here, it means `i` *was* actually 0.
43+
// Let's ignore the fact that the assert has lied
44+
// and try to take a mutable reference to the 0-th element.
45+
// `borrow_mut` should panic, because we are sill holding on
46+
// to a shared `Ref` for the same `RefCell`.
47+
*arr[0].borrow_mut() = None;
48+
49+
// But it doesn't panic!
50+
// We have successfully replaced `Some(Box)` with `None`,
51+
// while holding a shared reference to it.
52+
// No unsafe involved.
53+
54+
// The `Box` has been deallocated by now, so this is a dangling reference!
55+
let r: &u8 = &*r;
56+
println!("{:p}", r);
57+
58+
// The following might segfault. Or it might not.
59+
// Depends on the platform semantics
60+
// and whatever happened to the pointed-to memory after deallocation.
61+
// let u: u8 = *r;
62+
// println!("{u}");
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Derived from https://github.com/rust-lang/rust/issues/107975#issuecomment-1431758601
6+
7+
fn main() {
8+
let a: usize = {
9+
let v = 0u8;
10+
&v as *const _ as usize
11+
};
12+
let b: usize = {
13+
let v = 0u8;
14+
&v as *const _ as usize
15+
};
16+
17+
// So, are `a` and `b` equal?
18+
19+
// Let's check their difference.
20+
let i: usize = a - b;
21+
// It's not zero, which means `a` and `b` are not equal.
22+
assert_ne!(i, 0);
23+
// But it looks like zero...
24+
assert_eq!(i.to_string(), "0");
25+
// ...and now it *is* zero?
26+
assert_eq!(i, 0);
27+
// So `a` and `b` are equal after all?
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
#![feature(exposed_provenance)]
6+
7+
use std::ptr;
8+
9+
fn main() {
10+
let a: usize = {
11+
let v = 0u8;
12+
ptr::from_ref(&v).expose_provenance()
13+
};
14+
let b: usize = {
15+
let v = 0u8;
16+
ptr::from_ref(&v).expose_provenance()
17+
};
18+
19+
// `a` and `b` are not equal.
20+
assert_ne!(a, b);
21+
// But they are the same number.
22+
assert_eq!(format!("{a}"), format!("{b}"));
23+
// And they are equal.
24+
assert_eq!(a, b);
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1434203908
6+
7+
#![feature(exposed_provenance)]
8+
9+
use std::ptr;
10+
11+
fn f() -> usize {
12+
let v = 0;
13+
ptr::from_ref(&v).expose_provenance()
14+
}
15+
16+
fn main() {
17+
let a = f();
18+
let b = f();
19+
20+
// `a` and `b` are not equal.
21+
assert_ne!(a, b);
22+
// But they are the same number.
23+
assert_eq!(format!("{a}"), format!("{b}"));
24+
// And they are equal.
25+
assert_eq!(a, b);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
6+
7+
#![feature(exposed_provenance)]
8+
9+
use std::ptr;
10+
11+
#[inline(never)]
12+
fn cmp(a: usize, b: usize) -> bool {
13+
a == b
14+
}
15+
16+
#[inline(always)]
17+
fn cmp_in(a: usize, b: usize) -> bool {
18+
a == b
19+
}
20+
21+
fn main() {
22+
let a: usize = {
23+
let v = 0;
24+
ptr::from_ref(&v).expose_provenance()
25+
};
26+
let b: usize = {
27+
let v = 0;
28+
ptr::from_ref(&v).expose_provenance()
29+
};
30+
assert_eq!(format!("{}", a == b), "false");
31+
assert_eq!(format!("{}", cmp_in(a, b)), "false");
32+
assert_eq!(format!("{}", cmp(a, b)), "true");
33+
assert_eq!(a.to_string(), b.to_string());
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@ known-bug: #107975
2+
//@ compile-flags: -Copt-level=2
3+
//@ run-pass
4+
5+
// Based on https://github.com/rust-lang/rust/issues/107975#issuecomment-1432161340
6+
7+
#![feature(exposed_provenance)]
8+
9+
use std::ptr;
10+
11+
#[inline(never)]
12+
fn cmp(a: usize, b: usize) -> bool {
13+
a == b
14+
}
15+
16+
#[inline(always)]
17+
fn cmp_in(a: usize, b: usize) -> bool {
18+
a == b
19+
}
20+
21+
fn main() {
22+
let a: usize = {
23+
let v = 0;
24+
ptr::from_ref(&v).expose_provenance()
25+
};
26+
let b: usize = {
27+
let v = 0;
28+
ptr::from_ref(&v).expose_provenance()
29+
};
30+
assert_eq!(a.to_string(), b.to_string());
31+
assert_eq!(format!("{}", a == b), "true");
32+
assert_eq!(format!("{}", cmp_in(a, b)), "true");
33+
assert_eq!(format!("{}", cmp(a, b)), "true");
34+
}

0 commit comments

Comments
 (0)