about summary refs log tree commit diff
path: root/src/test/ui/traits/object
diff options
context:
space:
mode:
authorBram van den Heuvel <bram@vandenheuvel.online>2021-02-08 23:15:45 +0100
committerBram van den Heuvel <bram@vandenheuvel.online>2021-02-09 14:19:35 +0100
commit0d96a79fb875a38df6d8f7216f094959c4de3761 (patch)
tree738a5dada0e1322599f272abf31a371ae414a126 /src/test/ui/traits/object
parent921ec4b3fca17cc777766c240038d7d50ba98e0d (diff)
Organize trait test files
Diffstat (limited to 'src/test/ui/traits/object')
-rw-r--r--src/test/ui/traits/object/auto-dedup-in-impl.rs19
-rw-r--r--src/test/ui/traits/object/auto-dedup-in-impl.stderr12
-rw-r--r--src/test/ui/traits/object/auto-dedup.rs46
-rw-r--r--src/test/ui/traits/object/bounds-cycle-1.rs24
-rw-r--r--src/test/ui/traits/object/bounds-cycle-2.rs28
-rw-r--r--src/test/ui/traits/object/bounds-cycle-3.rs25
-rw-r--r--src/test/ui/traits/object/bounds-cycle-4.rs25
-rw-r--r--src/test/ui/traits/object/exclusion.rs19
-rw-r--r--src/test/ui/traits/object/generics.rs43
-rw-r--r--src/test/ui/traits/object/lifetime-first.rs13
-rw-r--r--src/test/ui/traits/object/macro-matcher.rs12
-rw-r--r--src/test/ui/traits/object/macro-matcher.stderr19
-rw-r--r--src/test/ui/traits/object/safety.rs17
-rw-r--r--src/test/ui/traits/object/safety.stderr49
-rw-r--r--src/test/ui/traits/object/supertrait-lifetime-bound.rs16
-rw-r--r--src/test/ui/traits/object/vs-lifetime-2.rs11
-rw-r--r--src/test/ui/traits/object/vs-lifetime-2.stderr9
-rw-r--r--src/test/ui/traits/object/vs-lifetime.rs17
-rw-r--r--src/test/ui/traits/object/vs-lifetime.stderr52
-rw-r--r--src/test/ui/traits/object/with-lifetime-bound.rs34
-rw-r--r--src/test/ui/traits/object/with-self-in-projection-output-bad.rs50
-rw-r--r--src/test/ui/traits/object/with-self-in-projection-output-bad.stderr21
-rw-r--r--src/test/ui/traits/object/with-self-in-projection-output-good.rs28
-rw-r--r--src/test/ui/traits/object/with-self-in-projection-output-repeated-supertrait.rs51
24 files changed, 640 insertions, 0 deletions
diff --git a/src/test/ui/traits/object/auto-dedup-in-impl.rs b/src/test/ui/traits/object/auto-dedup-in-impl.rs
new file mode 100644
index 00000000000..85698f19489
--- /dev/null
+++ b/src/test/ui/traits/object/auto-dedup-in-impl.rs
@@ -0,0 +1,19 @@
+// Checks to make sure that `dyn Trait + Send` and `dyn Trait + Send + Send` are the same type.
+// Issue: #47010
+
+struct Struct;
+impl Trait for Struct {}
+trait Trait {}
+
+type Send1 = dyn Trait + Send;
+type Send2 = dyn Trait + Send + Send;
+
+fn main () {}
+
+impl dyn Trait + Send {
+    fn test(&self) { println!("one"); } //~ ERROR duplicate definitions with name `test`
+}
+
+impl dyn Trait + Send + Send {
+    fn test(&self) { println!("two"); }
+}
diff --git a/src/test/ui/traits/object/auto-dedup-in-impl.stderr b/src/test/ui/traits/object/auto-dedup-in-impl.stderr
new file mode 100644
index 00000000000..5f13c781341
--- /dev/null
+++ b/src/test/ui/traits/object/auto-dedup-in-impl.stderr
@@ -0,0 +1,12 @@
+error[E0592]: duplicate definitions with name `test`
+  --> $DIR/auto-dedup-in-impl.rs:14:5
+   |
+LL |     fn test(&self) { println!("one"); }
+   |     ^^^^^^^^^^^^^^ duplicate definitions for `test`
+...
+LL |     fn test(&self) { println!("two"); }
+   |     -------------- other definition for `test`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0592`.
diff --git a/src/test/ui/traits/object/auto-dedup.rs b/src/test/ui/traits/object/auto-dedup.rs
new file mode 100644
index 00000000000..39d25eb7fe0
--- /dev/null
+++ b/src/test/ui/traits/object/auto-dedup.rs
@@ -0,0 +1,46 @@
+// run-pass
+
+#![allow(unused_assignments)]
+
+// Test that duplicate auto trait bounds in trait objects don't create new types.
+#[allow(unused_assignments)]
+use std::marker::Send as SendAlias;
+
+// A dummy trait for the non-auto trait.
+trait Trait {}
+
+// A dummy struct to implement `Trait` and `Send`.
+struct Struct;
+
+impl Trait for Struct {}
+
+// These three functions should be equivalent.
+fn takes_dyn_trait_send(_: Box<dyn Trait + Send>) {}
+fn takes_dyn_trait_send_send(_: Box<dyn Trait + Send + Send>) {}
+fn takes_dyn_trait_send_sendalias(_: Box<dyn Trait + Send + SendAlias>) {}
+
+impl dyn Trait + Send + Send {
+    fn do_nothing(&self) {}
+}
+
+fn main() {
+    // 1. Moving into a variable with more `Send`s and back.
+    let mut dyn_trait_send = Box::new(Struct) as Box<dyn Trait + Send>;
+    let dyn_trait_send_send: Box<dyn Trait + Send + Send> = dyn_trait_send;
+    dyn_trait_send = dyn_trait_send_send;
+
+    // 2. Calling methods with different number of `Send`s.
+    let dyn_trait_send = Box::new(Struct) as Box<dyn Trait + Send>;
+    takes_dyn_trait_send_send(dyn_trait_send);
+
+    let dyn_trait_send_send = Box::new(Struct) as Box<dyn Trait + Send + Send>;
+    takes_dyn_trait_send(dyn_trait_send_send);
+
+    // 3. Aliases to the trait are transparent.
+    let dyn_trait_send = Box::new(Struct) as Box<dyn Trait + Send>;
+    takes_dyn_trait_send_sendalias(dyn_trait_send);
+
+    // 4. Calling an impl that duplicates an auto trait.
+    let dyn_trait_send = Box::new(Struct) as Box<dyn Trait + Send>;
+    dyn_trait_send.do_nothing();
+}
diff --git a/src/test/ui/traits/object/bounds-cycle-1.rs b/src/test/ui/traits/object/bounds-cycle-1.rs
new file mode 100644
index 00000000000..3146764927c
--- /dev/null
+++ b/src/test/ui/traits/object/bounds-cycle-1.rs
@@ -0,0 +1,24 @@
+// Check that we don't have a cycle when we try to normalize `Self::U` in the
+// bound below.
+
+// check-pass
+
+trait Is {
+    type T;
+}
+
+impl<U> Is for U {
+    type T = U;
+}
+
+trait Obj {
+    type U: Is<T = Self::U>;
+}
+
+fn is_obj<T: ?Sized + Obj>(_: &T) {}
+
+fn f(x: &dyn Obj<U = i32>) {
+    is_obj(x)
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/object/bounds-cycle-2.rs b/src/test/ui/traits/object/bounds-cycle-2.rs
new file mode 100644
index 00000000000..4c1df38058d
--- /dev/null
+++ b/src/test/ui/traits/object/bounds-cycle-2.rs
@@ -0,0 +1,28 @@
+// Check that we don't have a cycle when we try to normalize `Self::V` in the
+// bound below.
+
+// check-pass
+
+trait Is {
+    type T;
+}
+
+impl<U> Is for U {
+    type T = U;
+}
+
+trait Super {
+    type V;
+}
+
+trait Obj: Super {
+    type U: Is<T = Self::V>;
+}
+
+fn is_obj<T: ?Sized + Obj>(_: &T) {}
+
+fn f(x: &dyn Obj<U = i32, V = i32>) {
+    is_obj(x)
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/object/bounds-cycle-3.rs b/src/test/ui/traits/object/bounds-cycle-3.rs
new file mode 100644
index 00000000000..55726a5ae45
--- /dev/null
+++ b/src/test/ui/traits/object/bounds-cycle-3.rs
@@ -0,0 +1,25 @@
+// Check that we don't have a cycle when we try to normalize `Self::V` in the
+// bound below.
+
+// check-pass
+
+trait Is {
+    type T;
+}
+
+impl<U> Is for U {
+    type T = U;
+}
+
+trait Obj {
+    type U: Is<T = Self::V>;
+    type V;
+}
+
+fn is_obj<T: ?Sized + Obj>(_: &T) {}
+
+fn f(x: &dyn Obj<U = i32, V = i32>) {
+    is_obj(x)
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/object/bounds-cycle-4.rs b/src/test/ui/traits/object/bounds-cycle-4.rs
new file mode 100644
index 00000000000..f83cb75c7f2
--- /dev/null
+++ b/src/test/ui/traits/object/bounds-cycle-4.rs
@@ -0,0 +1,25 @@
+// Check that we don't have a cycle when we try to normalize `Self::U` in the
+// bound below. Make sure that having a lifetime on the trait object doesn't break things
+
+// check-pass
+
+trait Is {
+    type T;
+}
+
+impl<U> Is for U {
+    type T = U;
+}
+
+trait Obj<'a> {
+    type U: Is<T = Self::V>;
+    type V;
+}
+
+fn is_obj<'a, T: ?Sized + Obj<'a>>(_: &T) {}
+
+fn f<'a>(x: &dyn Obj<'a, U = i32, V = i32>) {
+    is_obj(x)
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/object/exclusion.rs b/src/test/ui/traits/object/exclusion.rs
new file mode 100644
index 00000000000..0b8b0e2f5ef
--- /dev/null
+++ b/src/test/ui/traits/object/exclusion.rs
@@ -0,0 +1,19 @@
+// run-pass
+trait Future: 'static {
+    // The requirement for Self: Sized must prevent instantiation of
+    // Future::forget in vtables, otherwise there's an infinite type
+    // recursion through <Map<...> as Future>::forget.
+    fn forget(self) where Self: Sized {
+        Box::new(Map(self)) as Box<dyn Future>;
+    }
+}
+
+struct Map<A>(A);
+impl<A: Future> Future for Map<A> {}
+
+pub struct Promise;
+impl Future for Promise {}
+
+fn main() {
+    Promise.forget();
+}
diff --git a/src/test/ui/traits/object/generics.rs b/src/test/ui/traits/object/generics.rs
new file mode 100644
index 00000000000..c18754302b7
--- /dev/null
+++ b/src/test/ui/traits/object/generics.rs
@@ -0,0 +1,43 @@
+// run-pass
+// test for #8664
+
+#![feature(box_syntax)]
+
+use std::marker;
+
+pub trait Trait2<A> {
+    fn doit(&self) -> A;
+}
+
+pub struct Impl<A1, A2, A3> {
+    m1: marker::PhantomData<(A1,A2,A3)>,
+    /*
+     * With A2 we get the ICE:
+     * task <unnamed> failed at 'index out of bounds: the len is 1 but the index is 1',
+     * src/librustc/middle/subst.rs:58
+     */
+    t: Box<dyn Trait2<A2>+'static>
+}
+
+impl<A1, A2, A3> Impl<A1, A2, A3> {
+    pub fn step(&self) {
+        self.t.doit();
+    }
+}
+
+// test for #8601
+
+enum Type<T> { Constant(T) }
+
+trait Trait<K,V> {
+    fn method(&self, _: Type<(K,V)>) -> isize;
+}
+
+impl<V> Trait<u8,V> for () {
+    fn method(&self, _x: Type<(u8,V)>) -> isize { 0 }
+}
+
+pub fn main() {
+    let a = box () as Box<dyn Trait<u8, u8>>;
+    assert_eq!(a.method(Type::Constant((1, 2))), 0);
+}
diff --git a/src/test/ui/traits/object/lifetime-first.rs b/src/test/ui/traits/object/lifetime-first.rs
new file mode 100644
index 00000000000..33757cb7c0a
--- /dev/null
+++ b/src/test/ui/traits/object/lifetime-first.rs
@@ -0,0 +1,13 @@
+// run-pass
+use std::fmt::Display;
+
+static BYTE: u8 = 33;
+
+fn main() {
+    let x: &(dyn 'static + Display) = &BYTE;
+    let y: Box<dyn 'static + Display> = Box::new(BYTE);
+    let xstr = format!("{}", x);
+    let ystr = format!("{}", y);
+    assert_eq!(xstr, "33");
+    assert_eq!(ystr, "33");
+}
diff --git a/src/test/ui/traits/object/macro-matcher.rs b/src/test/ui/traits/object/macro-matcher.rs
new file mode 100644
index 00000000000..91097874997
--- /dev/null
+++ b/src/test/ui/traits/object/macro-matcher.rs
@@ -0,0 +1,12 @@
+// `ty` matcher accepts trait object types
+
+macro_rules! m {
+    ($t: ty) => ( let _: $t; )
+}
+
+fn main() {
+    m!(dyn Copy + Send + 'static);
+    //~^ ERROR the trait `Copy` cannot be made into an object
+    m!(dyn 'static + Send);
+    m!(dyn 'static +); //~ ERROR at least one trait is required for an object type
+}
diff --git a/src/test/ui/traits/object/macro-matcher.stderr b/src/test/ui/traits/object/macro-matcher.stderr
new file mode 100644
index 00000000000..6d1e236c048
--- /dev/null
+++ b/src/test/ui/traits/object/macro-matcher.stderr
@@ -0,0 +1,19 @@
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/macro-matcher.rs:11:8
+   |
+LL |     m!(dyn 'static +);
+   |        ^^^^^^^^^^^^^
+
+error[E0038]: the trait `Copy` cannot be made into an object
+  --> $DIR/macro-matcher.rs:8:8
+   |
+LL |     m!(dyn Copy + Send + 'static);
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^ `Copy` cannot be made into an object
+   |
+   = note: the trait cannot be made into an object because it requires `Self: Sized`
+   = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0038, E0224.
+For more information about an error, try `rustc --explain E0038`.
diff --git a/src/test/ui/traits/object/safety.rs b/src/test/ui/traits/object/safety.rs
new file mode 100644
index 00000000000..f43d332d696
--- /dev/null
+++ b/src/test/ui/traits/object/safety.rs
@@ -0,0 +1,17 @@
+// Check that static methods are not object-safe.
+
+trait Tr {
+    fn foo();
+    fn bar(&self) { }
+}
+
+struct St;
+
+impl Tr for St {
+    fn foo() {}
+}
+
+fn main() {
+    let _: &dyn Tr = &St; //~ ERROR E0038
+    //~^ ERROR E0038
+}
diff --git a/src/test/ui/traits/object/safety.stderr b/src/test/ui/traits/object/safety.stderr
new file mode 100644
index 00000000000..6784689072e
--- /dev/null
+++ b/src/test/ui/traits/object/safety.stderr
@@ -0,0 +1,49 @@
+error[E0038]: the trait `Tr` cannot be made into an object
+  --> $DIR/safety.rs:15:22
+   |
+LL |     let _: &dyn Tr = &St;
+   |                      ^^^ `Tr` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/safety.rs:4:8
+   |
+LL | trait Tr {
+   |       -- this trait cannot be made into an object...
+LL |     fn foo();
+   |        ^^^ ...because associated function `foo` has no `self` parameter
+   = note: required because of the requirements on the impl of `CoerceUnsized<&dyn Tr>` for `&St`
+   = note: required by cast to type `&dyn Tr`
+help: consider turning `foo` into a method by giving it a `&self` argument
+   |
+LL |     fn foo(&self);
+   |            ^^^^^
+help: alternatively, consider constraining `foo` so it does not apply to trait objects
+   |
+LL |     fn foo() where Self: Sized;
+   |              ^^^^^^^^^^^^^^^^^
+
+error[E0038]: the trait `Tr` cannot be made into an object
+  --> $DIR/safety.rs:15:12
+   |
+LL |     let _: &dyn Tr = &St;
+   |            ^^^^^^^ `Tr` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/safety.rs:4:8
+   |
+LL | trait Tr {
+   |       -- this trait cannot be made into an object...
+LL |     fn foo();
+   |        ^^^ ...because associated function `foo` has no `self` parameter
+help: consider turning `foo` into a method by giving it a `&self` argument
+   |
+LL |     fn foo(&self);
+   |            ^^^^^
+help: alternatively, consider constraining `foo` so it does not apply to trait objects
+   |
+LL |     fn foo() where Self: Sized;
+   |              ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0038`.
diff --git a/src/test/ui/traits/object/supertrait-lifetime-bound.rs b/src/test/ui/traits/object/supertrait-lifetime-bound.rs
new file mode 100644
index 00000000000..9d834727a4a
--- /dev/null
+++ b/src/test/ui/traits/object/supertrait-lifetime-bound.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+use std::any::Any;
+
+trait A<T>: Any {
+    fn m(&self) {}
+}
+
+impl<S, T: 'static> A<S> for T {}
+
+fn call_obj<'a>() {
+    let obj: &dyn A<&'a ()> = &();
+    obj.m();
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/object/vs-lifetime-2.rs b/src/test/ui/traits/object/vs-lifetime-2.rs
new file mode 100644
index 00000000000..0b33dc7f69a
--- /dev/null
+++ b/src/test/ui/traits/object/vs-lifetime-2.rs
@@ -0,0 +1,11 @@
+// A few contrived examples where lifetime should (or should not) be parsed as an object type.
+// Lifetimes parsed as types are still rejected later by semantic checks.
+
+// `'static` is a lifetime, `'static +` is a type, `'a` is a type
+fn g() where
+    'static: 'static,
+    dyn 'static +: 'static + Copy,
+    //~^ ERROR at least one trait is required for an object type
+{}
+
+fn main() {}
diff --git a/src/test/ui/traits/object/vs-lifetime-2.stderr b/src/test/ui/traits/object/vs-lifetime-2.stderr
new file mode 100644
index 00000000000..9b8e793dfd2
--- /dev/null
+++ b/src/test/ui/traits/object/vs-lifetime-2.stderr
@@ -0,0 +1,9 @@
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/vs-lifetime-2.rs:7:5
+   |
+LL |     dyn 'static +: 'static + Copy,
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0224`.
diff --git a/src/test/ui/traits/object/vs-lifetime.rs b/src/test/ui/traits/object/vs-lifetime.rs
new file mode 100644
index 00000000000..e49d276a55a
--- /dev/null
+++ b/src/test/ui/traits/object/vs-lifetime.rs
@@ -0,0 +1,17 @@
+// A few contrived examples where lifetime should (or should not) be parsed as an object type.
+// Lifetimes parsed as types are still rejected later by semantic checks.
+
+struct S<'a, T>(&'a u8, T);
+
+fn main() {
+    // `'static` is a lifetime argument, `'static +` is a type argument
+    let _: S<'static, u8>;
+    let _: S<'static, dyn 'static +>;
+    //~^ at least one trait is required for an object type
+    let _: S<'static, 'static>;
+    //~^ ERROR this struct takes 1 lifetime argument but 2 lifetime arguments were supplied
+    //~| ERROR this struct takes 1 type argument but 0 type arguments were supplied
+    let _: S<dyn 'static +, 'static>;
+    //~^ ERROR type provided when a lifetime was expected
+    //~| ERROR at least one trait is required for an object type
+}
diff --git a/src/test/ui/traits/object/vs-lifetime.stderr b/src/test/ui/traits/object/vs-lifetime.stderr
new file mode 100644
index 00000000000..6673472e4a9
--- /dev/null
+++ b/src/test/ui/traits/object/vs-lifetime.stderr
@@ -0,0 +1,52 @@
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/vs-lifetime.rs:9:23
+   |
+LL |     let _: S<'static, dyn 'static +>;
+   |                       ^^^^^^^^^^^^^
+
+error[E0107]: this struct takes 1 lifetime argument but 2 lifetime arguments were supplied
+  --> $DIR/vs-lifetime.rs:11:12
+   |
+LL |     let _: S<'static, 'static>;
+   |            ^        --------- help: remove this lifetime argument
+   |            |
+   |            expected 1 lifetime argument
+   |
+note: struct defined here, with 1 lifetime parameter: `'a`
+  --> $DIR/vs-lifetime.rs:4:8
+   |
+LL | struct S<'a, T>(&'a u8, T);
+   |        ^ --
+
+error[E0107]: this struct takes 1 type argument but 0 type arguments were supplied
+  --> $DIR/vs-lifetime.rs:11:12
+   |
+LL |     let _: S<'static, 'static>;
+   |            ^ expected 1 type argument
+   |
+note: struct defined here, with 1 type parameter: `T`
+  --> $DIR/vs-lifetime.rs:4:8
+   |
+LL | struct S<'a, T>(&'a u8, T);
+   |        ^     -
+help: add missing type argument
+   |
+LL |     let _: S<'static, 'static, T>;
+   |                              ^^^
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/vs-lifetime.rs:14:14
+   |
+LL |     let _: S<dyn 'static +, 'static>;
+   |              ^^^^^^^^^^^^^
+
+error[E0747]: type provided when a lifetime was expected
+  --> $DIR/vs-lifetime.rs:14:14
+   |
+LL |     let _: S<dyn 'static +, 'static>;
+   |              ^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0107, E0224, E0747.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/traits/object/with-lifetime-bound.rs b/src/test/ui/traits/object/with-lifetime-bound.rs
new file mode 100644
index 00000000000..05aab5e3b08
--- /dev/null
+++ b/src/test/ui/traits/object/with-lifetime-bound.rs
@@ -0,0 +1,34 @@
+// run-pass
+// Uncovered during work on new scoping rules for safe destructors
+// as an important use case to support properly.
+
+
+pub struct E<'a> {
+    pub f: &'a u8,
+}
+impl<'b> E<'b> {
+    pub fn m(&self) -> &'b u8 { self.f }
+}
+
+pub struct P<'c> {
+    pub g: &'c u8,
+}
+pub trait M {
+    fn n(&self) -> u8;
+}
+impl<'d> M for P<'d> {
+    fn n(&self) -> u8 { *self.g }
+}
+
+fn extension<'e>(x: &'e E<'e>) -> Box<dyn M+'e> {
+    loop {
+        let p = P { g: x.m() };
+        return Box::new(p) as Box<dyn M+'e>;
+    }
+}
+
+fn main() {
+    let w = E { f: &10 };
+    let o = extension(&w);
+    assert_eq!(o.n(), 10);
+}
diff --git a/src/test/ui/traits/object/with-self-in-projection-output-bad.rs b/src/test/ui/traits/object/with-self-in-projection-output-bad.rs
new file mode 100644
index 00000000000..f34fa80a0ce
--- /dev/null
+++ b/src/test/ui/traits/object/with-self-in-projection-output-bad.rs
@@ -0,0 +1,50 @@
+// Regression test for #56288. Checks that if a supertrait defines an associated type
+// projection that references `Self`, then that associated type must still be explicitly
+// specified in the `dyn Trait` variant, since we don't know what `Self` is anymore.
+
+trait Base {
+    type Output;
+}
+
+trait Helper: Base<Output=<Self as Helper>::Target> {
+    type Target;
+}
+
+impl Base for u32
+{
+    type Output = i32;
+}
+
+impl Helper for u32
+{
+    type Target = i32;
+}
+
+trait ConstI32 {
+    type Out;
+}
+
+impl<T: ?Sized> ConstI32 for T {
+    type Out = i32;
+}
+
+// Test that you still need to manually give a projection type if the Output type
+// is normalizable.
+trait NormalizableHelper:
+    Base<Output=<Self as ConstI32>::Out>
+{
+    type Target;
+}
+
+impl NormalizableHelper for u32
+{
+    type Target = i32;
+}
+
+fn main() {
+    let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
+    //~^ ERROR the value of the associated type `Output` (from trait `Base`) must be specified
+
+    let _y: Box<dyn NormalizableHelper<Target=i32>> = Box::new(2u32);
+    //~^ ERROR the value of the associated type `Output` (from trait `Base`) must be specified
+}
diff --git a/src/test/ui/traits/object/with-self-in-projection-output-bad.stderr b/src/test/ui/traits/object/with-self-in-projection-output-bad.stderr
new file mode 100644
index 00000000000..45978a84068
--- /dev/null
+++ b/src/test/ui/traits/object/with-self-in-projection-output-bad.stderr
@@ -0,0 +1,21 @@
+error[E0191]: the value of the associated type `Output` (from trait `Base`) must be specified
+  --> $DIR/with-self-in-projection-output-bad.rs:45:21
+   |
+LL |     type Output;
+   |     ------------ `Output` defined here
+...
+LL |     let _x: Box<dyn Helper<Target=i32>> = Box::new(2u32);
+   |                     ^^^^^^^^^^^^^^^^^^ help: specify the associated type: `Helper<Target=i32, Output = Type>`
+
+error[E0191]: the value of the associated type `Output` (from trait `Base`) must be specified
+  --> $DIR/with-self-in-projection-output-bad.rs:48:21
+   |
+LL |     type Output;
+   |     ------------ `Output` defined here
+...
+LL |     let _y: Box<dyn NormalizableHelper<Target=i32>> = Box::new(2u32);
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `NormalizableHelper<Target=i32, Output = Type>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0191`.
diff --git a/src/test/ui/traits/object/with-self-in-projection-output-good.rs b/src/test/ui/traits/object/with-self-in-projection-output-good.rs
new file mode 100644
index 00000000000..d1b7bf6c2d7
--- /dev/null
+++ b/src/test/ui/traits/object/with-self-in-projection-output-good.rs
@@ -0,0 +1,28 @@
+// build-pass (FIXME(62277): could be check-pass?)
+
+// Regression test related to #56288. Check that a supertrait projection (of
+// `Output`) that references `Self` can be ok if it is referencing a projection (of
+// `Self::Target`, in this case). Note that we still require the user to manually
+// specify both `Target` and `Output` for now.
+
+trait Base {
+    type Output;
+}
+
+trait Helper: Base<Output=<Self as Helper>::Target> {
+    type Target;
+}
+
+impl Base for u32
+{
+    type Output = i32;
+}
+
+impl Helper for u32
+{
+    type Target = i32;
+}
+
+fn main() {
+    let _x: Box<dyn Helper<Target=i32, Output=i32>> = Box::new(2u32);
+}
diff --git a/src/test/ui/traits/object/with-self-in-projection-output-repeated-supertrait.rs b/src/test/ui/traits/object/with-self-in-projection-output-repeated-supertrait.rs
new file mode 100644
index 00000000000..39e817168f6
--- /dev/null
+++ b/src/test/ui/traits/object/with-self-in-projection-output-repeated-supertrait.rs
@@ -0,0 +1,51 @@
+// build-pass (FIXME(62277): could be check-pass?)
+
+// FIXME(eddyb) shorten the name so windows doesn't choke on it.
+#![crate_name = "trait_test"]
+
+// Regression test related to #56288. Check that a supertrait projection (of
+// `Output`) that references `Self` is ok if there is another occurrence of
+// the same supertrait that specifies the projection explicitly, even if
+// the projection's associated type is not explicitly specified in the object type.
+//
+// Note that in order for this to compile, we need the `Self`-referencing projection
+// to normalize fairly directly to a concrete type, otherwise the trait resolver
+// will hate us.
+//
+// There is a test in `trait-object-with-self-in-projection-output-bad.rs` that
+// having a normalizing, but `Self`-containing projection does not *by itself*
+// allow you to avoid writing the projected type (`Output`, in this example)
+// explicitly.
+
+trait ConstI32 {
+    type Out;
+}
+
+impl<T: ?Sized> ConstI32 for T {
+    type Out = i32;
+}
+
+trait Base {
+    type Output;
+}
+
+trait NormalizingHelper: Base<Output=<Self as ConstI32>::Out> + Base<Output=i32> {
+    type Target;
+}
+
+impl Base for u32
+{
+    type Output = i32;
+}
+
+impl NormalizingHelper for u32
+{
+    type Target = i32;
+}
+
+fn main() {
+    // Make sure this works both with and without the associated type
+    // being specified.
+    let _x: Box<dyn NormalizingHelper<Target=i32>> = Box::new(2u32);
+    let _y: Box<dyn NormalizingHelper<Target=i32, Output=i32>> = Box::new(2u32);
+}