Skip to content

Commit

Permalink
Rollup merge of rust-lang#110480 - whtahy:105107/known-bug-tests-for-…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
matthiaskrgr authored Apr 24, 2023
2 parents 66ee5e0 + ebe61ce commit 0968aa0
Show file tree
Hide file tree
Showing 11 changed files with 348 additions and 0 deletions.
15 changes: 15 additions & 0 deletions tests/ui/closures/static-closures-with-nonstatic-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// check-pass
// known-bug: #84366

// Should fail. Associated types of 'static types should be `'static`, but
// argument-free closures can be `'static` and return non-`'static` types.

#[allow(dead_code)]
fn foo<'a>() {
let closure = || -> &'a str { "" };
assert_static(closure);
}

fn assert_static<T: 'static>(_: T) {}

fn main() {}
25 changes: 25 additions & 0 deletions tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// check-pass
// known-bug: #57893

// Should fail. Because we see an impl that uses a certain associated type, we
// type-check assuming that impl is used. However, this conflicts with the
// "implicit impl" that we get for trait objects, violating coherence.

trait Object<U> {
type Output;
}

impl<T: ?Sized, U> Object<U> for T {
type Output = U;
}

fn foo<T: ?Sized, U>(x: <T as Object<U>>::Output) -> U {
x
}

#[allow(dead_code)]
fn transmute<T, U>(x: T) -> U {
foo::<dyn Object<U, Output = T>, U>(x)
}

fn main() {}
38 changes: 38 additions & 0 deletions tests/ui/consts/non-sync-references-in-const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// check-pass
// known-bug: #49206

// Should fail. Compiles and prints 2 identical addresses, which shows 2 threads
// with the same `'static` reference to non-`Sync` struct. The problem is that
// promotion to static does not check if the type is `Sync`.

#[allow(dead_code)]
#[derive(Debug)]
struct Foo {
value: u32,
}

// stable negative impl trick from https://crates.io/crates/negative-impl
// see https://github.com/taiki-e/pin-project/issues/102#issuecomment-540472282
// for details.
struct Wrapper<'a, T>(::std::marker::PhantomData<&'a ()>, T);
unsafe impl<T> Sync for Wrapper<'_, T> where T: Sync {}
unsafe impl<'a> std::marker::Sync for Foo where Wrapper<'a, *const ()>: Sync {}
fn _assert_sync<T: Sync>() {}

fn inspect() {
let foo: &'static Foo = &Foo { value: 1 };
println!(
"I am in thread {:?}, address: {:p}",
std::thread::current().id(),
foo as *const Foo,
);
}

fn main() {
// _assert_sync::<Foo>(); // uncomment this line causes compile error
// "`*const ()` cannot be shared between threads safely"

let handle = std::thread::spawn(inspect);
inspect();
handle.join().unwrap();
}
37 changes: 37 additions & 0 deletions tests/ui/fn/fn-item-lifetime-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// check-pass
// known-bug: #84533

// Should fail. Lifetimes are checked correctly when `foo` is called, but NOT
// when only the lifetime parameters are instantiated.

use std::marker::PhantomData;

#[allow(dead_code)]
fn foo<'b, 'a>() -> PhantomData<&'b &'a ()> {
PhantomData
}

#[allow(dead_code)]
#[allow(path_statements)]
fn caller<'b, 'a>() {
foo::<'b, 'a>;
}

// In contrast to above, below code correctly does NOT compile.
// fn caller<'b, 'a>() {
// foo::<'b, 'a>();
// }

// error: lifetime may not live long enough
// --> src/main.rs:22:5
// |
// 21 | fn caller<'b, 'a>() {
// | -- -- lifetime `'a` defined here
// | |
// | lifetime `'b` defined here
// 22 | foo::<'b, 'a>();
// | ^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
// |
// = help: consider adding the following bound: `'a: 'b`

fn main() {}
31 changes: 31 additions & 0 deletions tests/ui/fn/implied-bounds-impl-header-projections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// check-pass
// known-bug: #100051

// Should fail. Implied bounds from projections in impl headers can create
// improper lifetimes. Variant of issue #98543 which was fixed by #99217.

trait Trait {
type Type;
}

impl<T> Trait for T {
type Type = ();
}

trait Extend<'a, 'b> {
fn extend(self, s: &'a str) -> &'b str;
}

impl<'a, 'b> Extend<'a, 'b> for <&'b &'a () as Trait>::Type
where
for<'what, 'ever> &'what &'ever (): Trait,
{
fn extend(self, s: &'a str) -> &'b str {
s
}
}

fn main() {
let y = <() as Extend<'_, '_>>::extend((), &String::from("Hello World"));
println!("{}", y);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// check-pass
// known-bug: #25860

// Should fail. The combination of variance and implied bounds for nested
// references allows us to infer a longer lifetime than we can prove.

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
let f: fn(_, &'a T) -> &'static T = foo;
f(UNIT, x)
}

fn main() {}
39 changes: 39 additions & 0 deletions tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// check-pass
// known-bug: #84591

// Should fail. Subtrait can incorrectly extend supertrait lifetimes even when
// supertrait has weaker implied bounds than subtrait. Strongly related to
// issue #25860.

trait Subtrait<T>: Supertrait {}
trait Supertrait {
fn action(self);
}

fn subs_to_soup<T, U>(x: T)
where
T: Subtrait<U>,
{
soup(x)
}

fn soup<T: Supertrait>(x: T) {
x.action();
}

impl<'a, 'b: 'a> Supertrait for (&'b str, &mut &'a str) {
fn action(self) {
*self.1 = self.0;
}
}

impl<'a, 'b> Subtrait<&'a &'b str> for (&'b str, &mut &'a str) {}

fn main() {
let mut d = "hi";
{
let x = "Hello World".to_string();
subs_to_soup((x.as_str(), &mut d));
}
println!("{}", d);
}
68 changes: 68 additions & 0 deletions tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// check-pass
// known-bug: #85099

// Should fail. Can coerce `Pin<T>` into `Pin<U>` where
// `T: Deref<Target: Unpin>` and `U: Deref<Target: !Unpin>`, using the
// `CoerceUnsized` impl on `Pin` and an unorthodox `DerefMut` impl for
// `Pin<&_>`.

// This should not be allowed, since one can unpin `T::Target` (since it is
// `Unpin`) to gain unpinned access to the previously pinned `U::Target` (which
// is `!Unpin`) and then move it.

use std::{
cell::{RefCell, RefMut},
future::Future,
ops::DerefMut,
pin::Pin,
};

struct SomeLocalStruct<'a, Fut>(&'a RefCell<Fut>);

trait SomeTrait<'a, Fut> {
#[allow(clippy::mut_from_ref)]
fn deref_helper(&self) -> &mut (dyn SomeTrait<'a, Fut> + 'a) {
unimplemented!()
}
fn downcast(self: Pin<&mut Self>) -> Pin<&mut Fut> {
unimplemented!()
}
}

impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for SomeLocalStruct<'a, Fut> {
fn deref_helper(&self) -> &mut (dyn SomeTrait<'a, Fut> + 'a) {
let x = Box::new(self.0.borrow_mut());
let x: &'a mut RefMut<'a, Fut> = Box::leak(x);
&mut **x
}
}
impl<'a, Fut: Future<Output = ()>> SomeTrait<'a, Fut> for Fut {
fn downcast(self: Pin<&mut Self>) -> Pin<&mut Fut> {
self
}
}

impl<'b, 'a, Fut> DerefMut for Pin<&'b dyn SomeTrait<'a, Fut>> {
fn deref_mut<'c>(
self: &'c mut Pin<&'b dyn SomeTrait<'a, Fut>>,
) -> &'c mut (dyn SomeTrait<'a, Fut> + 'b) {
self.deref_helper()
}
}

// obviously a "working" function with this signature is problematic
pub fn unsound_pin<Fut: Future<Output = ()>>(
fut: Fut,
callback: impl FnOnce(Pin<&mut Fut>),
) -> Fut {
let cell = RefCell::new(fut);
let s: &SomeLocalStruct<'_, Fut> = &SomeLocalStruct(&cell);
let p: Pin<Pin<&SomeLocalStruct<'_, Fut>>> = Pin::new(Pin::new(s));
let mut p: Pin<Pin<&dyn SomeTrait<'_, Fut>>> = p;
let r: Pin<&mut dyn SomeTrait<'_, Fut>> = p.as_mut();
let f: Pin<&mut Fut> = r.downcast();
callback(f);
cell.into_inner()
}

fn main() {}
37 changes: 37 additions & 0 deletions tests/ui/wf/wf-in-fn-type-implicit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// check-pass
// known-bug: #104005

// Should fail. Function type parameters with implicit type annotations are not
// checked for well-formedness, which allows incorrect borrowing.

// In contrast, user annotations are always checked for well-formedness, and the
// commented code below is correctly rejected by the borrow checker.

use std::fmt::Display;

trait Displayable {
fn display(self) -> Box<dyn Display>;
}

impl<T: Display> Displayable for (T, Option<&'static T>) {
fn display(self) -> Box<dyn Display> {
Box::new(self.0)
}
}

fn extend_lt<T, U>(val: T) -> Box<dyn Display>
where
(T, Option<U>): Displayable,
{
Displayable::display((val, None))
}

fn main() {
// *incorrectly* compiles
let val = extend_lt(&String::from("blah blah blah"));
println!("{}", val);

// *correctly* fails to compile
// let val = extend_lt::<_, &_>(&String::from("blah blah blah"));
// println!("{}", val);
}
23 changes: 23 additions & 0 deletions tests/ui/wf/wf-in-where-clause-static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// check-pass
// known-bug: #98117

// Should fail. Functions are responsible for checking the well-formedness of
// their own where clauses, so this should fail and require an explicit bound
// `T: 'static`.

use std::fmt::Display;

trait Static: 'static {}
impl<T> Static for &'static T {}

fn foo<S: Display>(x: S) -> Box<dyn Display>
where
&'static S: Static,
{
Box::new(x)
}

fn main() {
let s = foo(&String::from("blah blah blah"));
println!("{}", s);
}
19 changes: 19 additions & 0 deletions tests/ui/wf/wf-normalization-sized.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// check-pass
// known-bug: #100041

// Should fail. Normalization can bypass well-formedness checking.
// `[[[[[[u8]]]]]]` is not a well-formed type since size of type `[u8]` cannot
// be known at compile time (since `Sized` is not implemented for `[u8]`).

trait WellUnformed {
type RequestNormalize;
}

impl<T: ?Sized> WellUnformed for T {
type RequestNormalize = ();
}

const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
const _: <Vec<str> as WellUnformed>::RequestNormalize = ();

fn main() {}

0 comments on commit 0968aa0

Please sign in to comment.