diff options
| author | Yuki Okushi <jtitor@2k36.org> | 2023-04-25 02:33:25 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-25 02:33:25 +0900 |
| commit | 42467d57cbe48ab2273a9c72b60708e2de2e362f (patch) | |
| tree | 102a4aa31bc2387a84c95a93c6617cce5b9189ca | |
| parent | b72460fe46a873da0c40582636c26e7675e92288 (diff) | |
| parent | ebe61cefc4fa30f0a719892d544abcaee25da44c (diff) | |
| download | rust-42467d57cbe48ab2273a9c72b60708e2de2e362f.tar.gz rust-42467d57cbe48ab2273a9c72b60708e2de2e362f.zip | |
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
| -rw-r--r-- | tests/ui/closures/static-closures-with-nonstatic-return.rs | 15 | ||||
| -rw-r--r-- | tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs | 25 | ||||
| -rw-r--r-- | tests/ui/consts/non-sync-references-in-const.rs | 38 | ||||
| -rw-r--r-- | tests/ui/fn/fn-item-lifetime-bounds.rs | 37 | ||||
| -rw-r--r-- | tests/ui/fn/implied-bounds-impl-header-projections.rs | 31 | ||||
| -rw-r--r-- | tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs | 16 | ||||
| -rw-r--r-- | tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs | 39 | ||||
| -rw-r--r-- | tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs | 68 | ||||
| -rw-r--r-- | tests/ui/wf/wf-in-fn-type-implicit.rs | 37 | ||||
| -rw-r--r-- | tests/ui/wf/wf-in-where-clause-static.rs | 23 | ||||
| -rw-r--r-- | tests/ui/wf/wf-normalization-sized.rs | 19 |
11 files changed, 348 insertions, 0 deletions
diff --git a/tests/ui/closures/static-closures-with-nonstatic-return.rs b/tests/ui/closures/static-closures-with-nonstatic-return.rs new file mode 100644 index 00000000000..b5f0684bae9 --- /dev/null +++ b/tests/ui/closures/static-closures-with-nonstatic-return.rs @@ -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() {} diff --git a/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs new file mode 100644 index 00000000000..bb46498f90e --- /dev/null +++ b/tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs @@ -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() {} diff --git a/tests/ui/consts/non-sync-references-in-const.rs b/tests/ui/consts/non-sync-references-in-const.rs new file mode 100644 index 00000000000..0f668b8d469 --- /dev/null +++ b/tests/ui/consts/non-sync-references-in-const.rs @@ -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(); +} diff --git a/tests/ui/fn/fn-item-lifetime-bounds.rs b/tests/ui/fn/fn-item-lifetime-bounds.rs new file mode 100644 index 00000000000..68a1d0ce9b0 --- /dev/null +++ b/tests/ui/fn/fn-item-lifetime-bounds.rs @@ -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() {} diff --git a/tests/ui/fn/implied-bounds-impl-header-projections.rs b/tests/ui/fn/implied-bounds-impl-header-projections.rs new file mode 100644 index 00000000000..28cec805032 --- /dev/null +++ b/tests/ui/fn/implied-bounds-impl-header-projections.rs @@ -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); +} diff --git a/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs new file mode 100644 index 00000000000..1f5562497c1 --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs @@ -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() {} diff --git a/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs new file mode 100644 index 00000000000..9c26cd59d10 --- /dev/null +++ b/tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs @@ -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); +} diff --git a/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs new file mode 100644 index 00000000000..03602144e50 --- /dev/null +++ b/tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs @@ -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() {} diff --git a/tests/ui/wf/wf-in-fn-type-implicit.rs b/tests/ui/wf/wf-in-fn-type-implicit.rs new file mode 100644 index 00000000000..c5ff92c8875 --- /dev/null +++ b/tests/ui/wf/wf-in-fn-type-implicit.rs @@ -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); +} diff --git a/tests/ui/wf/wf-in-where-clause-static.rs b/tests/ui/wf/wf-in-where-clause-static.rs new file mode 100644 index 00000000000..86722afdf9f --- /dev/null +++ b/tests/ui/wf/wf-in-where-clause-static.rs @@ -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); +} diff --git a/tests/ui/wf/wf-normalization-sized.rs b/tests/ui/wf/wf-normalization-sized.rs new file mode 100644 index 00000000000..473fc79a8a3 --- /dev/null +++ b/tests/ui/wf/wf-normalization-sized.rs @@ -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() {} |
