From 89b6488ef0417df6f82671e57c4761a816af3974 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Sun, 11 Sep 2022 09:13:55 +0000
Subject: [PATCH] Deny RPITIT for object safety

---
 compiler/rustc_middle/src/traits/mod.rs       |  9 ++++
 .../src/traits/object_safety.rs               | 26 ++++++++++
 .../ui/impl-trait/in-trait/object-safety.rs   | 22 ++++++++
 .../impl-trait/in-trait/object-safety.stderr  | 50 +++++++++++++++++++
 4 files changed, 107 insertions(+)
 create mode 100644 src/test/ui/impl-trait/in-trait/object-safety.rs
 create mode 100644 src/test/ui/impl-trait/in-trait/object-safety.stderr

diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 755d9f8f69667..981d44a26079b 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -908,6 +908,12 @@ impl ObjectSafetyViolation {
             ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => {
                 format!("method `{}` references the `Self` type in its return type", name).into()
             }
+            ObjectSafetyViolation::Method(
+                name,
+                MethodViolationCode::ReferencesImplTraitInTrait,
+                _,
+            ) => format!("method `{}` references an `impl Trait` type in its return type", name)
+                .into(),
             ObjectSafetyViolation::Method(
                 name,
                 MethodViolationCode::WhereClauseReferencesSelf,
@@ -1014,6 +1020,9 @@ pub enum MethodViolationCode {
     /// e.g., `fn foo(&self) -> Self`
     ReferencesSelfOutput,
 
+    /// e.g., `fn foo(&self) -> impl Sized`
+    ReferencesImplTraitInTrait,
+
     /// e.g., `fn foo(&self) where Self: Clone`
     WhereClauseReferencesSelf,
 
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 612f513090888..5542f187f93f7 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -13,6 +13,7 @@ use super::elaborate_predicates;
 use crate::infer::TyCtxtInferExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::{self, Obligation, ObligationCause};
+use hir::def::DefKind;
 use rustc_errors::{FatalError, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -431,6 +432,9 @@ fn virtual_call_violation_for_method<'tcx>(
     if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
         return Some(MethodViolationCode::ReferencesSelfOutput);
     }
+    if contains_illegal_impl_trait_in_trait(tcx, sig.output()) {
+        return Some(MethodViolationCode::ReferencesImplTraitInTrait);
+    }
 
     // We can't monomorphize things like `fn foo<A>(...)`.
     let own_counts = tcx.generics_of(method.def_id).own_counts();
@@ -793,6 +797,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
                         ControlFlow::CONTINUE
                     }
                 }
+                ty::Projection(ref data)
+                    if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder =>
+                {
+                    // We'll deny these later in their own pass
+                    ControlFlow::CONTINUE
+                }
                 ty::Projection(ref data) => {
                     // This is a projected type `<Foo as SomeTrait>::X`.
 
@@ -861,6 +871,22 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
         .is_break()
 }
 
+pub fn contains_illegal_impl_trait_in_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: ty::Binder<'tcx, Ty<'tcx>>,
+) -> bool {
+    // FIXME(RPITIT): Perhaps we should use a visitor here?
+    ty.skip_binder().walk().any(|arg| {
+        if let ty::GenericArgKind::Type(ty) = arg.unpack()
+            && let ty::Projection(proj) = ty.kind()
+        {
+            tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
+        } else {
+            false
+        }
+    })
+}
+
 pub fn provide(providers: &mut ty::query::Providers) {
     *providers = ty::query::Providers { object_safety_violations, ..*providers };
 }
diff --git a/src/test/ui/impl-trait/in-trait/object-safety.rs b/src/test/ui/impl-trait/in-trait/object-safety.rs
new file mode 100644
index 0000000000000..dd35b9a2d8a75
--- /dev/null
+++ b/src/test/ui/impl-trait/in-trait/object-safety.rs
@@ -0,0 +1,22 @@
+#![feature(return_position_impl_trait_in_trait)]
+#![allow(incomplete_features)]
+
+use std::fmt::Debug;
+
+trait Foo {
+    fn baz(&self) -> impl Debug;
+}
+
+impl Foo for u32 {
+    fn baz(&self) -> u32 {
+        32
+    }
+}
+
+fn main() {
+    let i = Box::new(42_u32) as Box<dyn Foo>;
+    //~^ ERROR the trait `Foo` cannot be made into an object
+    //~| ERROR the trait `Foo` cannot be made into an object
+    let s = i.baz();
+    //~^ ERROR the trait `Foo` cannot be made into an object
+}
diff --git a/src/test/ui/impl-trait/in-trait/object-safety.stderr b/src/test/ui/impl-trait/in-trait/object-safety.stderr
new file mode 100644
index 0000000000000..9a1554b5e1cbd
--- /dev/null
+++ b/src/test/ui/impl-trait/in-trait/object-safety.stderr
@@ -0,0 +1,50 @@
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/object-safety.rs:17:33
+   |
+LL |     let i = Box::new(42_u32) as Box<dyn Foo>;
+   |                                 ^^^^^^^^^^^^ `Foo` 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/object-safety.rs:7:8
+   |
+LL | trait Foo {
+   |       --- this trait cannot be made into an object...
+LL |     fn baz(&self) -> impl Debug;
+   |        ^^^ ...because method `baz` references an `impl Trait` type in its return type
+   = help: consider moving `baz` to another trait
+
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/object-safety.rs:20:13
+   |
+LL |     let s = i.baz();
+   |             ^^^^^^^ `Foo` 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/object-safety.rs:7:8
+   |
+LL | trait Foo {
+   |       --- this trait cannot be made into an object...
+LL |     fn baz(&self) -> impl Debug;
+   |        ^^^ ...because method `baz` references an `impl Trait` type in its return type
+   = help: consider moving `baz` to another trait
+
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/object-safety.rs:17:13
+   |
+LL |     let i = Box::new(42_u32) as Box<dyn Foo>;
+   |             ^^^^^^^^^^^^^^^^ `Foo` 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/object-safety.rs:7:8
+   |
+LL | trait Foo {
+   |       --- this trait cannot be made into an object...
+LL |     fn baz(&self) -> impl Debug;
+   |        ^^^ ...because method `baz` references an `impl Trait` type in its return type
+   = help: consider moving `baz` to another trait
+   = note: required for `Box<u32>` to implement `CoerceUnsized<Box<dyn Foo>>`
+   = note: required by cast to type `Box<dyn Foo>`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0038`.