From 6b7e6ea590ad0179e9c61c5e007220ae1e4b9644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 9 Jan 2024 00:22:35 +0000 Subject: [PATCH] Account for traits using self-trait by name without `dyn` Fix #119652. --- .../rustc_hir_analysis/src/astconv/lint.rs | 24 ++-- ...-trait-should-use-self-2021-without-dyn.rs | 19 ++++ ...it-should-use-self-2021-without-dyn.stderr | 105 ++++++++++++++++++ ...bject-unsafe-trait-should-use-self-2021.rs | 17 +++ ...t-unsafe-trait-should-use-self-2021.stderr | 65 +++++++++++ 5 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs create mode 100644 tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr create mode 100644 tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.rs create mode 100644 tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index bf668f311cc..b5f42e98127 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -78,14 +78,17 @@ fn add_generic_param_suggestion( fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> bool { let tcx = self.tcx(); let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; - let (hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - .. - })) = tcx.hir_node_by_def_id(parent_id) - else { - return false; + let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { + (sig, generics, None) + } + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + owner_id, + .. + }) => (sig, generics, Some(tcx.parent(owner_id.to_def_id()))), + _ => return false, }; let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return false; @@ -94,6 +97,11 @@ fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { objects.iter().all(|o| match o.trait_ref.path.res { + Res::Def(DefKind::Trait, id) if Some(id) == owner => { + // When we're dealing with a recursive trait, we don't want to downgrade + // the error, so we consider them to be object safe always. (#119652) + true + } Res::Def(DefKind::Trait, id) => tcx.check_is_object_safe(id), _ => false, }) diff --git a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs new file mode 100644 index 00000000000..6382480b7e3 --- /dev/null +++ b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.rs @@ -0,0 +1,19 @@ +// edition:2021 +#![allow(bare_trait_objects)] +trait A: Sized { + fn f(a: A) -> A; + //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR trait objects must include the `dyn` keyword +} +trait B { + fn f(a: B) -> B; + //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR trait objects must include the `dyn` keyword +} +trait C { + fn f(&self, a: C) -> C; + //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR trait objects must include the `dyn` keyword +} + +fn main() {} diff --git a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr new file mode 100644 index 00000000000..1d8ed548046 --- /dev/null +++ b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021-without-dyn.stderr @@ -0,0 +1,105 @@ +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:4:13 + | +LL | fn f(a: A) -> A; + | ^ + | +help: use a new generic type parameter, constrained by `A` + | +LL | fn f(a: T) -> A; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn f(a: impl A) -> A; + | ++++ +help: alternatively, use a trait object to accept any type that implements `A`, accessing its methods at runtime using dynamic dispatch + | +LL | fn f(a: &dyn A) -> A; + | ++++ + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:4:19 + | +LL | fn f(a: A) -> A; + | ^ + | +help: use `impl A` to return an opaque type, as long as you return a single underlying type + | +LL | fn f(a: A) -> impl A; + | ++++ +help: alternatively, you can return an owned trait object + | +LL | fn f(a: A) -> Box; + | +++++++ + + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:9:13 + | +LL | fn f(a: B) -> B; + | ^ + | +help: use a new generic type parameter, constrained by `B` + | +LL | fn f(a: T) -> B; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn f(a: impl B) -> B; + | ++++ +help: alternatively, use a trait object to accept any type that implements `B`, accessing its methods at runtime using dynamic dispatch + | +LL | fn f(a: &dyn B) -> B; + | ++++ + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:9:19 + | +LL | fn f(a: B) -> B; + | ^ + | +help: use `impl B` to return an opaque type, as long as you return a single underlying type + | +LL | fn f(a: B) -> impl B; + | ++++ +help: alternatively, you can return an owned trait object + | +LL | fn f(a: B) -> Box; + | +++++++ + + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:14:20 + | +LL | fn f(&self, a: C) -> C; + | ^ + | +help: use a new generic type parameter, constrained by `C` + | +LL | fn f(&self, a: T) -> C; + | ++++++ ~ +help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference + | +LL | fn f(&self, a: impl C) -> C; + | ++++ +help: alternatively, use a trait object to accept any type that implements `C`, accessing its methods at runtime using dynamic dispatch + | +LL | fn f(&self, a: &dyn C) -> C; + | ++++ + +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/object-unsafe-trait-should-use-self-2021-without-dyn.rs:14:26 + | +LL | fn f(&self, a: C) -> C; + | ^ + | +help: use `impl C` to return an opaque type, as long as you return a single underlying type + | +LL | fn f(&self, a: C) -> impl C; + | ++++ +help: alternatively, you can return an owned trait object + | +LL | fn f(&self, a: C) -> Box; + | +++++++ + + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.rs b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.rs new file mode 100644 index 00000000000..a598e883f3f --- /dev/null +++ b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.rs @@ -0,0 +1,17 @@ +// edition:2021 +#![allow(bare_trait_objects)] +trait A: Sized { + fn f(a: dyn A) -> dyn A; + //~^ ERROR associated item referring to unboxed trait object for its own trait + //~| ERROR the trait `A` cannot be made into an object +} +trait B { + fn f(a: dyn B) -> dyn B; + //~^ ERROR associated item referring to unboxed trait object for its own trait + //~| ERROR the trait `B` cannot be made into an object +} +trait C { + fn f(&self, a: dyn C) -> dyn C; +} + +fn main() {} diff --git a/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.stderr b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.stderr new file mode 100644 index 00000000000..d6376be9c04 --- /dev/null +++ b/tests/ui/suggestions/object-unsafe-trait-should-use-self-2021.stderr @@ -0,0 +1,65 @@ +error: associated item referring to unboxed trait object for its own trait + --> $DIR/object-unsafe-trait-should-use-self-2021.rs:4:13 + | +LL | trait A: Sized { + | - in this trait +LL | fn f(a: dyn A) -> dyn A; + | ^^^^^ ^^^^^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn f(a: Self) -> Self; + | ~~~~ ~~~~ + +error[E0038]: the trait `A` cannot be made into an object + --> $DIR/object-unsafe-trait-should-use-self-2021.rs:4:13 + | +LL | fn f(a: dyn A) -> dyn A; + | ^^^^^ `A` 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 + --> $DIR/object-unsafe-trait-should-use-self-2021.rs:3:10 + | +LL | trait A: Sized { + | - ^^^^^ ...because it requires `Self: Sized` + | | + | this trait cannot be made into an object... + +error: associated item referring to unboxed trait object for its own trait + --> $DIR/object-unsafe-trait-should-use-self-2021.rs:9:13 + | +LL | trait B { + | - in this trait +LL | fn f(a: dyn B) -> dyn B; + | ^^^^^ ^^^^^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn f(a: Self) -> Self; + | ~~~~ ~~~~ + +error[E0038]: the trait `B` cannot be made into an object + --> $DIR/object-unsafe-trait-should-use-self-2021.rs:9:13 + | +LL | fn f(a: dyn B) -> dyn B; + | ^^^^^ `B` 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 + --> $DIR/object-unsafe-trait-should-use-self-2021.rs:9:8 + | +LL | trait B { + | - this trait cannot be made into an object... +LL | fn f(a: dyn B) -> dyn B; + | ^ ...because associated function `f` has no `self` parameter +help: consider turning `f` into a method by giving it a `&self` argument + | +LL | fn f(&self, a: dyn B) -> dyn B; + | ++++++ +help: alternatively, consider constraining `f` so it does not apply to trait objects + | +LL | fn f(a: dyn B) -> dyn B where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0038`.