about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <jtitor@2k36.org>2023-04-25 02:33:25 +0900
committerGitHub <noreply@github.com>2023-04-25 02:33:25 +0900
commit42467d57cbe48ab2273a9c72b60708e2de2e362f (patch)
tree102a4aa31bc2387a84c95a93c6617cce5b9189ca
parentb72460fe46a873da0c40582636c26e7675e92288 (diff)
parentebe61cefc4fa30f0a719892d544abcaee25da44c (diff)
downloadrust-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.rs15
-rw-r--r--tests/ui/coherence/indirect-impl-for-trait-obj-coherence.rs25
-rw-r--r--tests/ui/consts/non-sync-references-in-const.rs38
-rw-r--r--tests/ui/fn/fn-item-lifetime-bounds.rs37
-rw-r--r--tests/ui/fn/implied-bounds-impl-header-projections.rs31
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs16
-rw-r--r--tests/ui/implied-bounds/implied-bounds-on-trait-hierarchy.rs39
-rw-r--r--tests/ui/typeck/pin-unsound-issue-85099-derefmut.rs68
-rw-r--r--tests/ui/wf/wf-in-fn-type-implicit.rs37
-rw-r--r--tests/ui/wf/wf-in-where-clause-static.rs23
-rw-r--r--tests/ui/wf/wf-normalization-sized.rs19
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() {}