about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-31 07:46:14 +0000
committerbors <bors@rust-lang.org>2021-07-31 07:46:14 +0000
commit7069a8c2b78c5d23205de1cabb4c2a65229dbd8f (patch)
treee2afdc516470e471a88c40ce97494138dfa1c740
parent6b0b07d41f07e1ba5808693d900903499ccf7a32 (diff)
parenta28ee25483d92b7924752651d9c751ae2c4f2c34 (diff)
Auto merge of #86264 - crlf0710:trait_upcasting_part1, r=nikomatsakis
Trait upcasting coercion (part1)

This revives the first part of earlier PR #60900 .

It's not very clear to me which parts of that pr was design decisions, so i decide to cut it into pieces and land them incrementally. This allows more eyes on the details.

This is the first part, it adds feature gates, adds feature gates tests, and implemented the unsize conversion part.
(I hope i have dealt with the `ExistentialTraitRef` values correctly...)

The next part will be implementing the pointer casting.
-rw-r--r--compiler/rustc_feature/src/active.rs4
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs22
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs54
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs20
-rw-r--r--src/test/ui/feature-gates/feature-gate-trait_upcasting.rs13
-rw-r--r--src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr12
-rw-r--r--src/test/ui/issues/issue-11515.rs4
-rw-r--r--src/test/ui/issues/issue-11515.stderr10
-rw-r--r--src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs13
-rw-r--r--src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs22
-rw-r--r--src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr14
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-1.rs33
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr80
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-2.rs34
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr61
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-3.rs22
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-3.stderr33
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-4.rs32
-rw-r--r--src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr47
20 files changed, 504 insertions, 27 deletions
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 1ec9a0518b8..638330c904d 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -683,6 +683,10 @@ declare_features! (
     /// Allows the `?` operator in const contexts.
     (active, const_try, "1.56.0", Some(74935), None),
 
+    /// Allows upcasting trait objects via supertraits.
+    /// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`.
+    (incomplete, trait_upcasting, "1.56.0", Some(65991), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 295e53aba35..1ac489f600a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1270,6 +1270,7 @@ symbols! {
         trace_macros,
         track_caller,
         trait_alias,
+        trait_upcasting,
         transmute,
         transparent,
         transparent_enums,
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 752f6a8debc..c7bf1f2a943 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -693,22 +693,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let may_apply = match (source.kind(), target.kind()) {
             // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
             (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
-                // Upcasts permit two things:
-                //
-                // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
-                // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
-                //
-                // Note that neither of these changes requires any
-                // change at runtime. Eventually this will be
-                // generalized.
-                //
-                // We always upcast when we can because of reason
-                // #2 (region bounds).
-                data_a.principal_def_id() == data_b.principal_def_id()
-                    && data_b
-                        .auto_traits()
-                        // All of a's auto traits need to be in b's auto traits.
-                        .all(|b| data_a.auto_traits().any(|a| a == b))
+                // See `confirm_builtin_unsize_candidate` for more info.
+                let auto_traits_compatible = data_b
+                    .auto_traits()
+                    // All of a's auto traits need to be in b's auto traits.
+                    .all(|b| data_a.auto_traits().any(|a| a == b));
+                auto_traits_compatible
             }
 
             // `T` -> `Trait`
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index f8297ee3a07..0c2099593a2 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -703,10 +703,56 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         match (source.kind(), target.kind()) {
             // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
             (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => {
-                // See `assemble_candidates_for_unsizing` for more info.
-                let iter = data_a
-                    .principal()
-                    .map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
+                // Upcast coercions permit several things:
+                //
+                // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
+                // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`
+                // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar`
+                //
+                // Note that neither of the first two of these changes requires any
+                // change at runtime. The third needs to change pointer metadata at runtime.
+                //
+                // We always perform upcasting coercions when we can because of reason
+                // #2 (region bounds).
+
+                // We already checked the compatiblity of auto traits within `assemble_candidates_for_unsizing`.
+
+                let principal_a = data_a.principal();
+                let principal_def_id_b = data_b.principal_def_id();
+
+                let existential_predicate = if let Some(principal_a) = principal_a {
+                    let source_trait_ref = principal_a.with_self_ty(tcx, source);
+                    let target_trait_did = principal_def_id_b.ok_or_else(|| Unimplemented)?;
+                    let upcast_idx = util::supertraits(tcx, source_trait_ref)
+                        .position(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did)
+                        .ok_or_else(|| Unimplemented)?;
+                    // FIXME(crlf0710): This is less than ideal, for example,
+                    // if the trait is defined as `trait Foo: Bar<u32> + Bar<i32>`,
+                    // the coercion from Box<Foo> to Box<dyn Bar<_>> is actually ambiguous.
+                    // We currently make this coercion fail for now.
+                    //
+                    // see #65991 for more information.
+                    if util::supertraits(tcx, source_trait_ref)
+                        .skip(upcast_idx + 1)
+                        .any(|upcast_trait_ref| upcast_trait_ref.def_id() == target_trait_did)
+                    {
+                        return Err(Unimplemented);
+                    }
+                    let target_trait_ref =
+                        util::supertraits(tcx, source_trait_ref).nth(upcast_idx).unwrap();
+                    let existential_predicate = target_trait_ref.map_bound(|trait_ref| {
+                        ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
+                            tcx, trait_ref,
+                        ))
+                    });
+                    Some(existential_predicate)
+                } else if principal_def_id_b.is_none() {
+                    None
+                } else {
+                    return Err(Unimplemented);
+                };
+
+                let iter = existential_predicate
                     .into_iter()
                     .chain(
                         data_a
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index ba76b9c8dd5..a83b39a1108 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -576,6 +576,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         )];
 
         let mut has_unsized_tuple_coercion = false;
+        let mut has_trait_upcasting_coercion = false;
 
         // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
         // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
@@ -590,7 +591,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     if traits.contains(&trait_pred.def_id()) =>
                 {
                     if unsize_did == trait_pred.def_id() {
+                        let self_ty = trait_pred.self_ty();
                         let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
+                        if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) =
+                            (self_ty.kind(), unsize_ty.kind())
+                        {
+                            if data_a.principal_def_id() != data_b.principal_def_id() {
+                                debug!("coerce_unsized: found trait upcasting coercion");
+                                has_trait_upcasting_coercion = true;
+                            }
+                        }
                         if let ty::Tuple(..) = unsize_ty.kind() {
                             debug!("coerce_unsized: found unsized tuple coercion");
                             has_unsized_tuple_coercion = true;
@@ -666,6 +676,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             .emit();
         }
 
+        if has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting {
+            feature_err(
+                &self.tcx.sess.parse_sess,
+                sym::trait_upcasting,
+                self.cause.span,
+                "trait upcasting coercion is experimental",
+            )
+            .emit();
+        }
+
         Ok(coercion)
     }
 
diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs
new file mode 100644
index 00000000000..e4102f1cfa7
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs
@@ -0,0 +1,13 @@
+trait Foo {}
+
+trait Bar: Foo {}
+
+impl Foo for () {}
+
+impl Bar for () {}
+
+fn main() {
+    let bar: &dyn Bar = &();
+    let foo: &dyn Foo = bar;
+    //~^ ERROR trait upcasting coercion is experimental [E0658]
+}
diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr
new file mode 100644
index 00000000000..bc13a5d7d7b
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr
@@ -0,0 +1,12 @@
+error[E0658]: trait upcasting coercion is experimental
+  --> $DIR/feature-gate-trait_upcasting.rs:11:25
+   |
+LL |     let foo: &dyn Foo = bar;
+   |                         ^^^
+   |
+   = note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
+   = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/issues/issue-11515.rs b/src/test/ui/issues/issue-11515.rs
index a7671b9282a..2072f9c47e2 100644
--- a/src/test/ui/issues/issue-11515.rs
+++ b/src/test/ui/issues/issue-11515.rs
@@ -1,10 +1,10 @@
 #![feature(box_syntax)]
 
 struct Test {
-    func: Box<dyn FnMut() + 'static>
+    func: Box<dyn FnMut() + 'static>,
 }
 
 fn main() {
     let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
-    let test = box Test { func: closure }; //~ ERROR mismatched types
+    let test = box Test { func: closure }; //~ ERROR trait upcasting coercion is experimental [E0658]
 }
diff --git a/src/test/ui/issues/issue-11515.stderr b/src/test/ui/issues/issue-11515.stderr
index 7935615ad7e..a70e7c416bc 100644
--- a/src/test/ui/issues/issue-11515.stderr
+++ b/src/test/ui/issues/issue-11515.stderr
@@ -1,12 +1,12 @@
-error[E0308]: mismatched types
+error[E0658]: trait upcasting coercion is experimental
   --> $DIR/issue-11515.rs:9:33
    |
 LL |     let test = box Test { func: closure };
-   |                                 ^^^^^^^ expected trait `FnMut`, found trait `Fn`
+   |                                 ^^^^^^^
    |
-   = note: expected struct `Box<(dyn FnMut() + 'static)>`
-              found struct `Box<(dyn Fn() + 'static)>`
+   = note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
+   = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs
new file mode 100644
index 00000000000..277d9eabe4f
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs
@@ -0,0 +1,13 @@
+// run-pass
+#![feature(box_syntax, trait_upcasting)]
+#![allow(incomplete_features)]
+
+struct Test {
+    func: Box<dyn FnMut() + 'static>,
+}
+
+fn main() {
+    let closure: Box<dyn Fn() + 'static> = Box::new(|| ());
+    let mut test = box Test { func: closure };
+    (test.func)();
+}
diff --git a/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs
new file mode 100644
index 00000000000..6986ad62172
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.rs
@@ -0,0 +1,22 @@
+// check-fail
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Bar<T> {
+    fn bar(&self, _: T) {}
+}
+
+trait Foo : Bar<i32> + Bar<u32> {
+    fn foo(&self, _: ()) {}
+}
+
+struct S;
+
+impl Bar<i32> for S {}
+impl Bar<u32> for S {}
+impl Foo for S {}
+
+fn main() {
+    let s: &dyn Foo = &S;
+    let t: &dyn Bar<_> = s; //~ ERROR mismatched types
+}
diff --git a/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr
new file mode 100644
index 00000000000..e9670ad7def
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/multiple-occurence-ambiguousity.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/multiple-occurence-ambiguousity.rs:21:26
+   |
+LL |     let t: &dyn Bar<_> = s;
+   |            -----------   ^ expected trait `Bar`, found trait `Foo`
+   |            |
+   |            expected due to this
+   |
+   = note: expected reference `&dyn Bar<_>`
+              found reference `&dyn Foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs
new file mode 100644
index 00000000000..1a0e5072843
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.rs
@@ -0,0 +1,33 @@
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo: Bar<i32> + Bar<u32> {}
+trait Bar<T> {
+    fn bar(&self) -> Option<T> {
+        None
+    }
+}
+
+fn test_specific(x: &dyn Foo) {
+    let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
+                                //~^ ERROR non-primitive cast
+                                //~^^ ERROR the trait bound `&dyn Foo: Bar<i32>` is not satisfied
+    let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually
+                                //~^ ERROR non-primitive cast
+                                //~^^ ERROR the trait bound `&dyn Foo: Bar<u32>` is not satisfied
+}
+
+fn test_unknown_version(x: &dyn Foo) {
+    let _ = x as &dyn Bar<_>; // Ambiguous
+                              //~^ ERROR non-primitive cast
+                              //~^^ ERROR the trait bound `&dyn Foo: Bar<_>` is not satisfied
+}
+
+fn test_infer_version(x: &dyn Foo) {
+    let a = x as &dyn Bar<_>; // FIXME: OK, eventually
+                              //~^ ERROR non-primitive cast
+                              //~^^ ERROR the trait bound `&dyn Foo: Bar<u32>` is not satisfied
+    let _: Option<u32> = a.bar();
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr
new file mode 100644
index 00000000000..6aaa8a4a904
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-1.stderr
@@ -0,0 +1,80 @@
+error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<i32>`
+  --> $DIR/type-checking-test-1.rs:12:13
+   |
+LL |     let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
+   |             ^^^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let _ = &x as &dyn Bar<i32>; // FIXME: OK, eventually
+   |             ^
+
+error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<u32>`
+  --> $DIR/type-checking-test-1.rs:15:13
+   |
+LL |     let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually
+   |             ^^^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let _ = &x as &dyn Bar<u32>; // FIXME: OK, eventually
+   |             ^
+
+error[E0277]: the trait bound `&dyn Foo: Bar<i32>` is not satisfied
+  --> $DIR/type-checking-test-1.rs:12:13
+   |
+LL |     let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
+   |             ^ the trait `Bar<i32>` is not implemented for `&dyn Foo`
+   |
+   = note: required for the cast to the object type `dyn Bar<i32>`
+
+error[E0277]: the trait bound `&dyn Foo: Bar<u32>` is not satisfied
+  --> $DIR/type-checking-test-1.rs:15:13
+   |
+LL |     let _ = x as &dyn Bar<u32>; // FIXME: OK, eventually
+   |             ^ the trait `Bar<u32>` is not implemented for `&dyn Foo`
+   |
+   = note: required for the cast to the object type `dyn Bar<u32>`
+
+error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>`
+  --> $DIR/type-checking-test-1.rs:21:13
+   |
+LL |     let _ = x as &dyn Bar<_>; // Ambiguous
+   |             ^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let _ = &x as &dyn Bar<_>; // Ambiguous
+   |             ^
+
+error[E0277]: the trait bound `&dyn Foo: Bar<_>` is not satisfied
+  --> $DIR/type-checking-test-1.rs:21:13
+   |
+LL |     let _ = x as &dyn Bar<_>; // Ambiguous
+   |             ^ the trait `Bar<_>` is not implemented for `&dyn Foo`
+   |
+   = note: required for the cast to the object type `dyn Bar<_>`
+
+error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<u32>`
+  --> $DIR/type-checking-test-1.rs:27:13
+   |
+LL |     let a = x as &dyn Bar<_>; // FIXME: OK, eventually
+   |             ^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let a = &x as &dyn Bar<_>; // FIXME: OK, eventually
+   |             ^
+
+error[E0277]: the trait bound `&dyn Foo: Bar<u32>` is not satisfied
+  --> $DIR/type-checking-test-1.rs:27:13
+   |
+LL |     let a = x as &dyn Bar<_>; // FIXME: OK, eventually
+   |             ^ the trait `Bar<u32>` is not implemented for `&dyn Foo`
+   |
+   = note: required for the cast to the object type `dyn Bar<u32>`
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0277, E0605.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs
new file mode 100644
index 00000000000..326df74211e
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.rs
@@ -0,0 +1,34 @@
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo<T>: Bar<i32> + Bar<T> {}
+trait Bar<T> {
+    fn bar(&self) -> Option<T> {
+        None
+    }
+}
+
+fn test_specific(x: &dyn Foo<i32>) {
+    let _ = x as &dyn Bar<i32>; // OK
+}
+
+fn test_specific2(x: &dyn Foo<u32>) {
+    let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
+                                //~^ ERROR non-primitive cast
+                                //~^^ ERROR the trait bound `&dyn Foo<u32>: Bar<i32>` is not satisfied
+}
+
+fn test_specific3(x: &dyn Foo<i32>) {
+    let _ = x as &dyn Bar<u32>; // Error
+                                //~^ ERROR non-primitive cast
+                                //~^^ ERROR the trait bound `&dyn Foo<i32>: Bar<u32>` is not satisfied
+}
+
+fn test_infer_arg(x: &dyn Foo<u32>) {
+    let a = x as &dyn Bar<_>; // Ambiguous
+                              //~^ ERROR non-primitive cast
+                              //~^^ ERROR the trait bound `&dyn Foo<u32>: Bar<_>` is not satisfied
+    let _ = a.bar();
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr
new file mode 100644
index 00000000000..a38f8a14604
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-2.stderr
@@ -0,0 +1,61 @@
+error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<i32>`
+  --> $DIR/type-checking-test-2.rs:16:13
+   |
+LL |     let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
+   |             ^^^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let _ = &x as &dyn Bar<i32>; // FIXME: OK, eventually
+   |             ^
+
+error[E0277]: the trait bound `&dyn Foo<u32>: Bar<i32>` is not satisfied
+  --> $DIR/type-checking-test-2.rs:16:13
+   |
+LL |     let _ = x as &dyn Bar<i32>; // FIXME: OK, eventually
+   |             ^ the trait `Bar<i32>` is not implemented for `&dyn Foo<u32>`
+   |
+   = note: required for the cast to the object type `dyn Bar<i32>`
+
+error[E0605]: non-primitive cast: `&dyn Foo<i32>` as `&dyn Bar<u32>`
+  --> $DIR/type-checking-test-2.rs:22:13
+   |
+LL |     let _ = x as &dyn Bar<u32>; // Error
+   |             ^^^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let _ = &x as &dyn Bar<u32>; // Error
+   |             ^
+
+error[E0277]: the trait bound `&dyn Foo<i32>: Bar<u32>` is not satisfied
+  --> $DIR/type-checking-test-2.rs:22:13
+   |
+LL |     let _ = x as &dyn Bar<u32>; // Error
+   |             ^ the trait `Bar<u32>` is not implemented for `&dyn Foo<i32>`
+   |
+   = note: required for the cast to the object type `dyn Bar<u32>`
+
+error[E0605]: non-primitive cast: `&dyn Foo<u32>` as `&dyn Bar<_>`
+  --> $DIR/type-checking-test-2.rs:28:13
+   |
+LL |     let a = x as &dyn Bar<_>; // Ambiguous
+   |             ^^^^^^^^^^^^^^^^ invalid cast
+   |
+help: consider borrowing the value
+   |
+LL |     let a = &x as &dyn Bar<_>; // Ambiguous
+   |             ^
+
+error[E0277]: the trait bound `&dyn Foo<u32>: Bar<_>` is not satisfied
+  --> $DIR/type-checking-test-2.rs:28:13
+   |
+LL |     let a = x as &dyn Bar<_>; // Ambiguous
+   |             ^ the trait `Bar<_>` is not implemented for `&dyn Foo<u32>`
+   |
+   = note: required for the cast to the object type `dyn Bar<_>`
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0277, E0605.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-3.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-3.rs
new file mode 100644
index 00000000000..49c24e404dc
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-3.rs
@@ -0,0 +1,22 @@
+// ignore-compare-mode-nll
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo<'a>: Bar<'a> {}
+trait Bar<'a> {}
+
+fn test_correct(x: &dyn Foo<'static>) {
+    let _ = x as &dyn Bar<'static>;
+}
+
+fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) {
+    let _ = x as &dyn Bar<'a>; // Error
+                               //~^ ERROR mismatched types
+}
+
+fn test_wrong2<'a>(x: &dyn Foo<'a>) {
+    let _ = x as &dyn Bar<'static>; // Error
+                                    //~^ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-3.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-3.stderr
new file mode 100644
index 00000000000..593ee0a3430
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-3.stderr
@@ -0,0 +1,33 @@
+error[E0308]: mismatched types
+  --> $DIR/type-checking-test-3.rs:13:13
+   |
+LL |     let _ = x as &dyn Bar<'a>; // Error
+   |             ^ lifetime mismatch
+   |
+   = note: expected trait object `dyn Bar<'a>`
+              found trait object `dyn Bar<'static>`
+note: the lifetime `'a` as defined on the function body at 12:16...
+  --> $DIR/type-checking-test-3.rs:12:16
+   |
+LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) {
+   |                ^^
+   = note: ...does not necessarily outlive the static lifetime
+
+error[E0308]: mismatched types
+  --> $DIR/type-checking-test-3.rs:18:13
+   |
+LL |     let _ = x as &dyn Bar<'static>; // Error
+   |             ^ lifetime mismatch
+   |
+   = note: expected trait object `dyn Bar<'static>`
+              found trait object `dyn Bar<'a>`
+note: the lifetime `'a` as defined on the function body at 17:16...
+  --> $DIR/type-checking-test-3.rs:17:16
+   |
+LL | fn test_wrong2<'a>(x: &dyn Foo<'a>) {
+   |                ^^
+   = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs b/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs
new file mode 100644
index 00000000000..9b27fd46f7a
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-4.rs
@@ -0,0 +1,32 @@
+// ignore-compare-mode-nll
+#![feature(trait_upcasting)]
+#![allow(incomplete_features)]
+
+trait Foo<'a>: Bar<'a, 'a> {}
+trait Bar<'a, 'b> {
+    fn get_b(&self) -> Option<&'a u32> {
+        None
+    }
+}
+
+fn test_correct(x: &dyn Foo<'static>) {
+    let _ = x as &dyn Bar<'static, 'static>;
+}
+
+fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) {
+    let _ = x as &dyn Bar<'static, 'a>; // Error
+                                        //~^ ERROR mismatched types
+}
+
+fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) {
+    let _ = x as &dyn Bar<'a, 'static>; // Error
+                                        //~^ ERROR mismatched types
+}
+
+fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> {
+    let y = x as &dyn Bar<'_, '_>;
+    //~^ ERROR `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
+    y.get_b() // ERROR
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr b/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr
new file mode 100644
index 00000000000..811e524eda7
--- /dev/null
+++ b/src/test/ui/traits/trait-upcasting/type-checking-test-4.stderr
@@ -0,0 +1,47 @@
+error[E0308]: mismatched types
+  --> $DIR/type-checking-test-4.rs:17:13
+   |
+LL |     let _ = x as &dyn Bar<'static, 'a>; // Error
+   |             ^ lifetime mismatch
+   |
+   = note: expected trait object `dyn Bar<'static, 'a>`
+              found trait object `dyn Bar<'static, 'static>`
+note: the lifetime `'a` as defined on the function body at 16:16...
+  --> $DIR/type-checking-test-4.rs:16:16
+   |
+LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) {
+   |                ^^
+   = note: ...does not necessarily outlive the static lifetime
+
+error[E0308]: mismatched types
+  --> $DIR/type-checking-test-4.rs:22:13
+   |
+LL |     let _ = x as &dyn Bar<'a, 'static>; // Error
+   |             ^ lifetime mismatch
+   |
+   = note: expected trait object `dyn Bar<'a, 'static>`
+              found trait object `dyn Bar<'static, 'static>`
+note: the lifetime `'a` as defined on the function body at 21:16...
+  --> $DIR/type-checking-test-4.rs:21:16
+   |
+LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) {
+   |                ^^
+   = note: ...does not necessarily outlive the static lifetime
+
+error[E0759]: `x` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
+  --> $DIR/type-checking-test-4.rs:27:27
+   |
+LL | fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> {
+   |                       ------------ this data with lifetime `'a`...
+LL |     let y = x as &dyn Bar<'_, '_>;
+   |             -             ^^
+   |             |
+   |             ...is captured here...
+LL |
+LL |     y.get_b() // ERROR
+   |     --------- ...and is required to live as long as `'static` here
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0759.
+For more information about an error, try `rustc --explain E0308`.