Skip to content

Commit 6c77456

Browse files
committed
On object safety error, mention new enum as alternative
When we encounter a `dyn Trait` that isn't object safe, look for its implementors. If there's one, mention using it directly If there are less than 9, mention the possibility of creating a new enum and using that instead. Fix rust-lang#80194.
1 parent 642bfb2 commit 6c77456

26 files changed

+102
-0
lines changed

compiler/rustc_infer/src/traits/error_reporting/mod.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_data_structures::fx::FxIndexSet;
55
use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
66
use rustc_hir as hir;
77
use rustc_hir::def_id::{DefId, LocalDefId};
8+
use rustc_middle::ty::print::with_no_trimmed_paths;
89
use rustc_middle::ty::TyCtxt;
910
use rustc_span::Span;
1011
use std::fmt;
@@ -108,5 +109,41 @@ pub fn report_object_safety_error<'tcx>(
108109
violation.solution(&mut err);
109110
}
110111
}
112+
113+
let impls_of = tcx.trait_impls_of(trait_def_id);
114+
let impls = if impls_of.blanket_impls().is_empty() {
115+
impls_of.non_blanket_impls().values().flatten().collect::<Vec<_>>()
116+
} else {
117+
vec![]
118+
};
119+
match &impls[..] {
120+
[] => {}
121+
_ if impls.len() > 9 => {}
122+
[only] => {
123+
err.help(with_no_trimmed_paths!(format!(
124+
"only type `{}` implements the trait, consider using it directly instead",
125+
tcx.type_of(*only).instantiate_identity(),
126+
)));
127+
}
128+
impls => {
129+
let types = impls
130+
.iter()
131+
.map(|t| {
132+
with_no_trimmed_paths!(format!(
133+
"\n - {}",
134+
tcx.type_of(*t).instantiate_identity(),
135+
))
136+
})
137+
.collect::<Vec<_>>();
138+
err.help(format!(
139+
"the following types implement the trait, consider defining an enum where each \
140+
variant holds one of these types, implementing `{}` for this new enum and using \
141+
it instead:{}",
142+
trait_str,
143+
types.join(""),
144+
));
145+
}
146+
}
147+
111148
err
112149
}

tests/ui/coherence/coherence-impl-trait-for-trait-object-safe.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ LL | trait NotObjectSafe { fn eq(&self, other: Self); }
1212
| |
1313
| this trait cannot be made into an object...
1414
= help: consider moving `eq` to another trait
15+
= help: only type `(dyn NotObjectSafe + 'static)` implements the trait, consider using it directly instead
1516

1617
error: aborting due to previous error
1718

tests/ui/const-generics/generic_const_exprs/object-safety-err-ret.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ LL | trait Foo {
1212
LL | fn test(&self) -> [u8; bar::<Self>()];
1313
| ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type
1414
= help: consider moving `test` to another trait
15+
= help: only type `()` implements the trait, consider using it directly instead
1516

1617
error: aborting due to previous error
1718

tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ LL | trait Trait {
1414
| ----- this trait cannot be made into an object...
1515
LL | fn ptr(self: Ptr<Self>);
1616
| ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on
17+
= help: only type `i32` implements the trait, consider using it directly instead
1718

1819
error[E0038]: the trait `Trait` cannot be made into an object
1920
--> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:5
@@ -31,6 +32,7 @@ LL | trait Trait {
3132
| ----- this trait cannot be made into an object...
3233
LL | fn ptr(self: Ptr<Self>);
3334
| ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on
35+
= help: only type `i32` implements the trait, consider using it directly instead
3436
= note: required for the cast from `Ptr<{integer}>` to `Ptr<dyn Trait>`
3537

3638
error: aborting due to 2 previous errors

tests/ui/generic-associated-types/gat-in-trait-path.base.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ LL | trait Foo {
1212
LL | type A<'a> where Self: 'a;
1313
| ^ ...because it contains the generic associated type `A`
1414
= help: consider moving `A` to another trait
15+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead:
16+
- Fooy
17+
- Fooer<T>
1518

1619
error: aborting due to previous error
1720

tests/ui/generic-associated-types/issue-76535.base.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ LL | pub trait SuperTrait {
2828
LL | type SubType<'a>: SubTrait where Self: 'a;
2929
| ^^^^^^^ ...because it contains the generic associated type `SubType`
3030
= help: consider moving `SubType` to another trait
31+
= help: only type `SuperStruct` implements the trait, consider using it directly instead
3132

3233
error[E0038]: the trait `SuperTrait` cannot be made into an object
3334
--> $DIR/issue-76535.rs:39:57
@@ -43,6 +44,7 @@ LL | pub trait SuperTrait {
4344
LL | type SubType<'a>: SubTrait where Self: 'a;
4445
| ^^^^^^^ ...because it contains the generic associated type `SubType`
4546
= help: consider moving `SubType` to another trait
47+
= help: only type `SuperStruct` implements the trait, consider using it directly instead
4648
= note: required for the cast from `Box<SuperStruct>` to `Box<dyn SuperTrait<SubType = SubStruct<'_>>>`
4749

4850
error: aborting due to 3 previous errors

tests/ui/generic-associated-types/issue-79422.base.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ LL | trait MapLike<K, V> {
2828
LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
2929
| ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
3030
= help: consider moving `VRefCont` to another trait
31+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead:
32+
- std::collections::BTreeMap<K, V>
33+
- Source
3134

3235
error[E0038]: the trait `MapLike` cannot be made into an object
3336
--> $DIR/issue-79422.rs:44:13
@@ -43,6 +46,9 @@ LL | trait MapLike<K, V> {
4346
LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
4447
| ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
4548
= help: consider moving `VRefCont` to another trait
49+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead:
50+
- std::collections::BTreeMap<K, V>
51+
- Source
4652
= note: required for the cast from `Box<BTreeMap<u8, u8>>` to `Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>`
4753

4854
error: aborting due to 3 previous errors

tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
99
|
1010
LL | fn bar(self) -> impl Deref<Target = impl Sized>;
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait cannot be made into an object because method `bar` references an `impl Trait` type in its return type
12+
= help: only type `rpitit::Foreign` implements the trait, consider using it directly instead
1213

1314
error: aborting due to previous error
1415

tests/ui/impl-trait/in-trait/object-safety.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ LL | trait Foo {
1212
LL | fn baz(&self) -> impl Debug;
1313
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
1414
= help: consider moving `baz` to another trait
15+
= help: only type `u32` implements the trait, consider using it directly instead
1516

1617
error[E0038]: the trait `Foo` cannot be made into an object
1718
--> $DIR/object-safety.rs:19:15
@@ -27,6 +28,7 @@ LL | trait Foo {
2728
LL | fn baz(&self) -> impl Debug;
2829
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
2930
= help: consider moving `baz` to another trait
31+
= help: only type `u32` implements the trait, consider using it directly instead
3032

3133
error[E0038]: the trait `Foo` cannot be made into an object
3234
--> $DIR/object-safety.rs:19:13
@@ -42,6 +44,7 @@ LL | trait Foo {
4244
LL | fn baz(&self) -> impl Debug;
4345
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
4446
= help: consider moving `baz` to another trait
47+
= help: only type `u32` implements the trait, consider using it directly instead
4548

4649
error[E0038]: the trait `Foo` cannot be made into an object
4750
--> $DIR/object-safety.rs:16:13
@@ -57,6 +60,7 @@ LL | trait Foo {
5760
LL | fn baz(&self) -> impl Debug;
5861
| ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type
5962
= help: consider moving `baz` to another trait
63+
= help: only type `u32` implements the trait, consider using it directly instead
6064
= note: required for the cast from `Box<u32>` to `Box<dyn Foo>`
6165

6266
error: aborting due to 4 previous errors

tests/ui/impl-trait/object-unsafe-trait-in-return-position-dyn-trait.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ LL | trait NotObjectSafe {
1111
| ------------- this trait cannot be made into an object...
1212
LL | fn foo() -> Self;
1313
| ^^^ ...because associated function `foo` has no `self` parameter
14+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `NotObjectSafe` for this new enum and using it instead:
15+
- A
16+
- B
1417
help: consider turning `foo` into a method by giving it a `&self` argument
1518
|
1619
LL | fn foo(&self) -> Self;
@@ -33,6 +36,9 @@ LL | trait NotObjectSafe {
3336
| ------------- this trait cannot be made into an object...
3437
LL | fn foo() -> Self;
3538
| ^^^ ...because associated function `foo` has no `self` parameter
39+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `NotObjectSafe` for this new enum and using it instead:
40+
- A
41+
- B
3642
help: consider turning `foo` into a method by giving it a `&self` argument
3743
|
3844
LL | fn foo(&self) -> Self;

0 commit comments

Comments
 (0)