Rollup merge of #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 #105107. This PR adds `known-bug` tests for 11 unsound issues: - #25860 - #49206 - #57893 - #84366 - #84533 - #84591 - #85099 - #98117 - #100041 - #100051 - #104005
This commit is contained in:
commit
42467d57cb
15
tests/ui/closures/static-closures-with-nonstatic-return.rs
Normal file
15
tests/ui/closures/static-closures-with-nonstatic-return.rs
Normal file
@ -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
tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs
Normal file
25
tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs
Normal file
@ -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
tests/ui/consts/non-sync-references-in-const.rs
Normal file
38
tests/ui/consts/non-sync-references-in-const.rs
Normal file
@ -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
tests/ui/fn/fn-item-lifetime-bounds.rs
Normal file
37
tests/ui/fn/fn-item-lifetime-bounds.rs
Normal file
@ -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
tests/ui/fn/implied-bounds-impl-header-projections.rs
Normal file
31
tests/ui/fn/implied-bounds-impl-header-projections.rs
Normal file
@ -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);
|
||||
}
|
@ -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
tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs
Normal file
39
tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs
Normal file
@ -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
tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs
Normal file
68
tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs
Normal file
@ -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
tests/ui/wf/wf-in-fn-type-implicit.rs
Normal file
37
tests/ui/wf/wf-in-fn-type-implicit.rs
Normal file
@ -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
tests/ui/wf/wf-in-where-clause-static.rs
Normal file
23
tests/ui/wf/wf-in-where-clause-static.rs
Normal file
@ -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
tests/ui/wf/wf-normalization-sized.rs
Normal file
19
tests/ui/wf/wf-normalization-sized.rs
Normal file
@ -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() {}
|
Loading…
x
Reference in New Issue
Block a user