Skip to content

Commit 42467d5

Browse files
authored
Rollup merge of rust-lang#110480 - whtahy:105107/known-bug-tests-for-unsound-issues, r=jackh726
Add `known-bug` tests for 11 unsound issues r? ``@jackh726`` Should tests for other issues be in separate PRs? Thanks. Edit: Partially addresses rust-lang#105107. This PR adds `known-bug` tests for 11 unsound issues: - rust-lang#25860 - rust-lang#49206 - rust-lang#57893 - rust-lang#84366 - rust-lang#84533 - rust-lang#84591 - rust-lang#85099 - rust-lang#98117 - rust-lang#100041 - rust-lang#100051 - rust-lang#104005
2 parents b72460f + ebe61ce commit 42467d5

11 files changed

+348
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// check-pass
2+
// known-bug: #84366
3+
4+
// Should fail. Associated types of 'static types should be `'static`, but
5+
// argument-free closures can be `'static` and return non-`'static` types.
6+
7+
#[allow(dead_code)]
8+
fn foo<'a>() {
9+
let closure = || -> &'a str { "" };
10+
assert_static(closure);
11+
}
12+
13+
fn assert_static<T: 'static>(_: T) {}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// check-pass
2+
// known-bug: #57893
3+
4+
// Should fail. Because we see an impl that uses a certain associated type, we
5+
// type-check assuming that impl is used. However, this conflicts with the
6+
// "implicit impl" that we get for trait objects, violating coherence.
7+
8+
trait Object<U> {
9+
type Output;
10+
}
11+
12+
impl<T: ?Sized, U> Object<U> for T {
13+
type Output = U;
14+
}
15+
16+
fn foo<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U {
17+
x
18+
}
19+
20+
#[allow(dead_code)]
21+
fn transmute<T, U>(x: T) -> U {
22+
foo::<dyn Object<U, Output = T>, U>(x)
23+
}
24+
25+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// check-pass
2+
// known-bug: #49206
3+
4+
// Should fail. Compiles and prints 2 identical addresses, which shows 2 threads
5+
// with the same `'static` reference to non-`Sync` struct. The problem is that
6+
// promotion to static does not check if the type is `Sync`.
7+
8+
#[allow(dead_code)]
9+
#[derive(Debug)]
10+
struct Foo {
11+
value: u32,
12+
}
13+
14+
// stable negative impl trick from https://crates.io/crates/negative-impl
15+
// see https://github.com/taiki-e/pin-project/issues/102#issuecomment-540472282
16+
// for details.
17+
struct Wrapper<'a, T>(::std::marker::PhantomData<&'a ()>, T);
18+
unsafe impl<T> Sync for Wrapper<'_, T> where T: Sync {}
19+
unsafe impl<'a> std::marker::Sync for Foo where Wrapper<'a, *const ()>: Sync {}
20+
fn _assert_sync<T: Sync>() {}
21+
22+
fn inspect() {
23+
let foo: &'static Foo = &Foo { value: 1 };
24+
println!(
25+
"I am in thread {:?}, address: {:p}",
26+
std::thread::current().id(),
27+
foo as *const Foo,
28+
);
29+
}
30+
31+
fn main() {
32+
// _assert_sync::<Foo>(); // uncomment this line causes compile error
33+
// "`*const ()` cannot be shared between threads safely"
34+
35+
let handle = std::thread::spawn(inspect);
36+
inspect();
37+
handle.join().unwrap();
38+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// check-pass
2+
// known-bug: #84533
3+
4+
// Should fail. Lifetimes are checked correctly when `foo` is called, but NOT
5+
// when only the lifetime parameters are instantiated.
6+
7+
use std::marker::PhantomData;
8+
9+
#[allow(dead_code)]
10+
fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> {
11+
PhantomData
12+
}
13+
14+
#[allow(dead_code)]
15+
#[allow(path_statements)]
16+
fn caller<'b, 'a>() {
17+
foo::<'b, 'a>;
18+
}
19+
20+
// In contrast to above, below code correctly does NOT compile.
21+
// fn caller<'b, 'a>() {
22+
// foo::<'b, 'a>();
23+
// }
24+
25+
// error: lifetime may not live long enough
26+
// --> src/main.rs:22:5
27+
// |
28+
// 21 | fn caller<'b, 'a>() {
29+
// | -- -- lifetime `'a` defined here
30+
// | |
31+
// | lifetime `'b` defined here
32+
// 22 | foo::<'b, 'a>();
33+
// | ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
34+
// |
35+
// = help: consider adding the following bound: `'a: 'b`
36+
37+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// check-pass
2+
// known-bug: #100051
3+
4+
// Should fail. Implied bounds from projections in impl headers can create
5+
// improper lifetimes. Variant of issue #98543 which was fixed by #99217.
6+
7+
trait Trait {
8+
type Type;
9+
}
10+
11+
impl<T> Trait for T {
12+
type Type = ();
13+
}
14+
15+
trait Extend<'a, 'b> {
16+
fn extend(self, s: &'a str) -> &'b str;
17+
}
18+
19+
impl<'a, 'b> Extend<'a, 'b> for <&'b &'a () as Trait>::Type
20+
where
21+
for<'what, 'ever> &'what &'ever (): Trait,
22+
{
23+
fn extend(self, s: &'a str) -> &'b str {
24+
s
25+
}
26+
}
27+
28+
fn main() {
29+
let y = <() as Extend<'_, '_>>::extend((), &String::from("Hello World"));
30+
println!("{}", y);
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// check-pass
2+
// known-bug: #25860
3+
4+
// Should fail. The combination of variance and implied bounds for nested
5+
// references allows us to infer a longer lifetime than we can prove.
6+
7+
static UNIT: &'static &'static () = &&();
8+
9+
fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }
10+
11+
fn bad<'a, T>(x: &'a T) -> &'static T {
12+
let f: fn(_, &'a T) -> &'static T = foo;
13+
f(UNIT, x)
14+
}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// check-pass
2+
// known-bug: #84591
3+
4+
// Should fail. Subtrait can incorrectly extend supertrait lifetimes even when
5+
// supertrait has weaker implied bounds than subtrait. Strongly related to
6+
// issue #25860.
7+
8+
trait Subtrait<T>: Supertrait {}
9+
trait Supertrait {
10+
fn action(self);
11+
}
12+
13+
fn subs_to_soup<T, U>(x: T)
14+
where
15+
T: Subtrait<U>,
16+
{
17+
soup(x)
18+
}
19+
20+
fn soup<T: Supertrait>(x: T) {
21+
x.action();
22+
}
23+
24+
impl<'a, 'b: 'a> Supertrait for (&'b str, &mut &'a str) {
25+
fn action(self) {
26+
*self.1 = self.0;
27+
}
28+
}
29+
30+
impl<'a, 'b> Subtrait<&'a &'b str> for (&'b str, &mut &'a str) {}
31+
32+
fn main() {
33+
let mut d = "hi";
34+
{
35+
let x = "Hello World".to_string();
36+
subs_to_soup((x.as_str(), &mut d));
37+
}
38+
println!("{}", d);
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// check-pass
2+
// known-bug: #85099
3+
4+
// Should fail. Can coerce `Pin<T>` into `Pin<U>` where
5+
// `T: Deref<Target: Unpin>` and `U: Deref<Target: !Unpin>`, using the
6+
// `CoerceUnsized` impl on `Pin` and an unorthodox `DerefMut` impl for
7+
// `Pin<&_>`.
8+
9+
// This should not be allowed, since one can unpin `T::Target` (since it is
10+
// `Unpin`) to gain unpinned access to the previously pinned `U::Target` (which
11+
// is `!Unpin`) and then move it.
12+
13+
use std::{
14+
cell::{RefCell, RefMut},
15+
future::Future,
16+
ops::DerefMut,
17+
pin::Pin,
18+
};
19+
20+
struct SomeLocalStruct<'a, Fut>(&'a RefCell<Fut>);
21+
22+
trait SomeTrait<'a, Fut> {
23+
#[allow(clippy::mut_from_ref)]
24+
fn deref_helper(&self) -> &mut (dyn SomeTrait<'a, Fut> + 'a) {
25+
unimplemented!()
26+
}
27+
fn downcast(self: Pin<&mut Self>) -> Pin<&mut Fut> {
28+
unimplemented!()
29+
}
30+
}
31+
32+
impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for SomeLocalStruct<'a, Fut> {
33+
fn deref_helper(&self) -> &mut (dyn SomeTrait<'a, Fut> + 'a) {
34+
let x = Box::new(self.0.borrow_mut());
35+
let x: &'a mut RefMut<'a, Fut> = Box::leak(x);
36+
&mut **x
37+
}
38+
}
39+
impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for Fut {
40+
fn downcast(self: Pin<&mut Self>) -> Pin<&mut Fut> {
41+
self
42+
}
43+
}
44+
45+
impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> {
46+
fn deref_mut<'c>(
47+
self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>,
48+
) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) {
49+
self.deref_helper()
50+
}
51+
}
52+
53+
// obviously a "working" function with this signature is problematic
54+
pub fn unsound_pin<Fut: Future<Output = ()>>(
55+
fut: Fut,
56+
callback: impl FnOnce(Pin<&mut Fut>),
57+
) -> Fut {
58+
let cell = RefCell::new(fut);
59+
let s: &SomeLocalStruct<'_, Fut> = &SomeLocalStruct(&cell);
60+
let p: Pin<Pin<&SomeLocalStruct<'_, Fut>>> = Pin::new(Pin::new(s));
61+
let mut p: Pin<Pin<&dyn SomeTrait<'_, Fut>>> = p;
62+
let r: Pin<&mut dyn SomeTrait<'_, Fut>> = p.as_mut();
63+
let f: Pin<&mut Fut> = r.downcast();
64+
callback(f);
65+
cell.into_inner()
66+
}
67+
68+
fn main() {}

tests/ui/wf/wf-in-fn-type-implicit.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// check-pass
2+
// known-bug: #104005
3+
4+
// Should fail. Function type parameters with implicit type annotations are not
5+
// checked for well-formedness, which allows incorrect borrowing.
6+
7+
// In contrast, user annotations are always checked for well-formedness, and the
8+
// commented code below is correctly rejected by the borrow checker.
9+
10+
use std::fmt::Display;
11+
12+
trait Displayable {
13+
fn display(self) -> Box<dyn Display>;
14+
}
15+
16+
impl<T: Display> Displayable for (T, Option<&'static T>) {
17+
fn display(self) -> Box<dyn Display> {
18+
Box::new(self.0)
19+
}
20+
}
21+
22+
fn extend_lt<T, U>(val: T) -> Box<dyn Display>
23+
where
24+
(T, Option<U>): Displayable,
25+
{
26+
Displayable::display((val, None))
27+
}
28+
29+
fn main() {
30+
// *incorrectly* compiles
31+
let val = extend_lt(&String::from("blah blah blah"));
32+
println!("{}", val);
33+
34+
// *correctly* fails to compile
35+
// let val = extend_lt::<_, &_>(&String::from("blah blah blah"));
36+
// println!("{}", val);
37+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// check-pass
2+
// known-bug: #98117
3+
4+
// Should fail. Functions are responsible for checking the well-formedness of
5+
// their own where clauses, so this should fail and require an explicit bound
6+
// `T: 'static`.
7+
8+
use std::fmt::Display;
9+
10+
trait Static: 'static {}
11+
impl<T> Static for &'static T {}
12+
13+
fn foo<S: Display>(x: S) -> Box<dyn Display>
14+
where
15+
&'static S: Static,
16+
{
17+
Box::new(x)
18+
}
19+
20+
fn main() {
21+
let s = foo(&String::from("blah blah blah"));
22+
println!("{}", s);
23+
}

tests/ui/wf/wf-normalization-sized.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// check-pass
2+
// known-bug: #100041
3+
4+
// Should fail. Normalization can bypass well-formedness checking.
5+
// `[[[[[[u8]]]]]]` is not a well-formed type since size of type `[u8]` cannot
6+
// be known at compile time (since `Sized` is not implemented for `[u8]`).
7+
8+
trait WellUnformed {
9+
type RequestNormalize;
10+
}
11+
12+
impl<T: ?Sized> WellUnformed for T {
13+
type RequestNormalize = ();
14+
}
15+
16+
const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
17+
const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
18+
19+
fn main() {}

0 commit comments

Comments
 (0)