about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-09-12 15:21:33 +0530
committerGitHub <noreply@github.com>2022-09-12 15:21:33 +0530
commit6bacb6f6e9e1f8519392a08c98201cbd10bc4c5e (patch)
tree02d0df5520f1b385b9c2ff6da4bc1d53bb0b7913
parent5faf033f62512cbc2a0deaac7860950e38a60a45 (diff)
parent89b6488ef0417df6f82671e57c4761a816af3974 (diff)
downloadrust-6bacb6f6e9e1f8519392a08c98201cbd10bc4c5e.tar.gz
rust-6bacb6f6e9e1f8519392a08c98201cbd10bc4c5e.zip
Rollup merge of #101681 - compiler-errors:rpitit-obj-safety, r=lcnr
Deny return-position `impl Trait` in traits for object safety

Fixes #101667
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs26
-rw-r--r--src/test/ui/impl-trait/in-trait/object-safety.rs22
-rw-r--r--src/test/ui/impl-trait/in-trait/object-safety.stderr50
4 files changed, 107 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index c0bf04e9e59..a95e6a61854 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -917,6 +917,12 @@ impl ObjectSafetyViolation {
             }
             ObjectSafetyViolation::Method(
                 name,
+                MethodViolationCode::ReferencesImplTraitInTrait,
+                _,
+            ) => format!("method `{}` references an `impl Trait` type in its return type", name)
+                .into(),
+            ObjectSafetyViolation::Method(
+                name,
                 MethodViolationCode::WhereClauseReferencesSelf,
                 _,
             ) => {
@@ -1021,6 +1027,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 612f5130908..5542f187f93 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 00000000000..dd35b9a2d8a
--- /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 00000000000..9a1554b5e1c
--- /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`.