Skip to content

Commit 95de91b

Browse files
Rollup merge of #117132 - estebank:issue-80194, r=petrochenkov
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 #80194.
2 parents 824e367 + 8c04999 commit 95de91b

25 files changed

+130
-1
lines changed

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

+63-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ 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::TyCtxt;
8+
use rustc_middle::ty::print::with_no_trimmed_paths;
9+
use rustc_middle::ty::{self, TyCtxt};
910
use rustc_span::Span;
1011
use std::fmt;
1112
use std::iter;
@@ -108,5 +109,66 @@ 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
116+
.non_blanket_impls()
117+
.values()
118+
.flatten()
119+
.filter(|def_id| {
120+
!matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..))
121+
})
122+
.collect::<Vec<_>>()
123+
} else {
124+
vec![]
125+
};
126+
let externally_visible = if !impls.is_empty()
127+
&& let Some(def_id) = trait_def_id.as_local()
128+
&& tcx.effective_visibilities(()).is_exported(def_id)
129+
{
130+
true
131+
} else {
132+
false
133+
};
134+
match &impls[..] {
135+
[] => {}
136+
_ if impls.len() > 9 => {}
137+
[only] if externally_visible => {
138+
err.help(with_no_trimmed_paths!(format!(
139+
"only type `{}` is seen to implement the trait in this crate, consider using it \
140+
directly instead",
141+
tcx.type_of(*only).instantiate_identity(),
142+
)));
143+
}
144+
[only] => {
145+
err.help(with_no_trimmed_paths!(format!(
146+
"only type `{}` implements the trait, consider using it directly instead",
147+
tcx.type_of(*only).instantiate_identity(),
148+
)));
149+
}
150+
impls => {
151+
let types = impls
152+
.iter()
153+
.map(|t| {
154+
with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),))
155+
})
156+
.collect::<Vec<_>>();
157+
err.help(format!(
158+
"the following types implement the trait, consider defining an enum where each \
159+
variant holds one of these types, implementing `{}` for this new enum and using \
160+
it instead:\n{}",
161+
trait_str,
162+
types.join("\n"),
163+
));
164+
}
165+
}
166+
if externally_visible {
167+
err.note(format!(
168+
"`{trait_str}` can be implemented in other crates; if you want to support your users \
169+
passing their own types here, you can't refer to a specific type",
170+
));
171+
}
172+
111173
err
112174
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ LL | fn test(&self) -> [u8; bar::<Self>()];
1515
| ...because method `test` references the `Self` type in its `where` clause
1616
= help: consider moving `test` to another trait
1717
= help: consider moving `test` to another trait
18+
= help: only type `()` implements the trait, consider using it directly instead
1819

1920
error: aborting due to previous error
2021

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

+2
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

+3
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

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ 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` is seen to implement the trait in this crate, consider using it directly instead
32+
= note: `SuperTrait` can be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type
3133

3234
error[E0038]: the trait `SuperTrait` cannot be made into an object
3335
--> $DIR/issue-76535.rs:39:57
@@ -43,6 +45,8 @@ LL | pub trait SuperTrait {
4345
LL | type SubType<'a>: SubTrait where Self: 'a;
4446
| ^^^^^^^ ...because it contains the generic associated type `SubType`
4547
= help: consider moving `SubType` to another trait
48+
= help: only type `SuperStruct` is seen to implement the trait in this crate, consider using it directly instead
49+
= note: `SuperTrait` can be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type
4650
= note: required for the cast from `Box<SuperStruct>` to `Box<dyn SuperTrait<SubType = SubStruct<'_>>>`
4751

4852
error: aborting due to 3 previous errors

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

+6
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

+1
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

+4
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:17: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:17: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:14: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

+6
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;

tests/ui/issues/issue-19380.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LL | trait Qiz {
1111
| --- this trait cannot be made into an object...
1212
LL | fn qiz();
1313
| ^^^ ...because associated function `qiz` has no `self` parameter
14+
= help: only type `Foo` implements the trait, consider using it directly instead
1415
help: consider turning `qiz` into a method by giving it a `&self` argument
1516
|
1617
LL | fn qiz(&self);
@@ -33,6 +34,7 @@ LL | trait Qiz {
3334
| --- this trait cannot be made into an object...
3435
LL | fn qiz();
3536
| ^^^ ...because associated function `qiz` has no `self` parameter
37+
= help: only type `Foo` implements the trait, consider using it directly instead
3638
= note: required for the cast from `&Foo` to `&'static (dyn Qiz + 'static)`
3739
help: consider turning `qiz` into a method by giving it a `&self` argument
3840
|

tests/ui/object-safety/issue-19538.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ LL | fn foo<T>(&self, val: T);
1313
LL | trait Bar: Foo { }
1414
| --- this trait cannot be made into an object...
1515
= help: consider moving `foo` to another trait
16+
= help: only type `Thing` implements the trait, consider using it directly instead
1617

1718
error[E0038]: the trait `Bar` cannot be made into an object
1819
--> $DIR/issue-19538.rs:17:30
@@ -29,6 +30,7 @@ LL | fn foo<T>(&self, val: T);
2930
LL | trait Bar: Foo { }
3031
| --- this trait cannot be made into an object...
3132
= help: consider moving `foo` to another trait
33+
= help: only type `Thing` implements the trait, consider using it directly instead
3234
= note: required for the cast from `&mut Thing` to `&mut dyn Bar`
3335

3436
error: aborting due to 2 previous errors

tests/ui/object-safety/object-safety-issue-22040.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LL | trait Expr: Debug + PartialEq {
1111
| ---- ^^^^^^^^^ ...because it uses `Self` as a type parameter
1212
| |
1313
| this trait cannot be made into an object...
14+
= help: only type `SExpr<'x>` implements the trait, consider using it directly instead
1415

1516
error: aborting due to previous error
1617

tests/ui/object-safety/object-safety-no-static.curr.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LL | trait Foo {
1111
| --- this trait cannot be made into an object...
1212
LL | fn foo() {}
1313
| ^^^ ...because associated function `foo` has no `self` parameter
14+
= help: only type `Bar` implements the trait, consider using it directly instead
1415
help: consider turning `foo` into a method by giving it a `&self` argument
1516
|
1617
LL | fn foo(&self) {}

tests/ui/object-safety/object-safety-no-static.object_safe_for_dispatch.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LL | trait Foo {
1111
| --- this trait cannot be made into an object...
1212
LL | fn foo() {}
1313
| ^^^ ...because associated function `foo` has no `self` parameter
14+
= help: only type `Bar` implements the trait, consider using it directly instead
1415
= note: required for the cast from `Box<Bar>` to `Box<dyn Foo>`
1516
help: consider turning `foo` into a method by giving it a `&self` argument
1617
|

tests/ui/self/arbitrary-self-types-not-object-safe.curr.stderr

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

1819
error[E0038]: the trait `Foo` cannot be made into an object
1920
--> $DIR/arbitrary-self-types-not-object-safe.rs:33:13
@@ -31,6 +32,7 @@ LL | trait Foo {
3132
| --- this trait cannot be made into an object...
3233
LL | fn foo(self: &Rc<Self>) -> usize;
3334
| ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on
35+
= help: only type `usize` implements the trait, consider using it directly instead
3436
= note: required for the cast from `Rc<usize>` to `Rc<dyn Foo>`
3537

3638
error: aborting due to 2 previous errors

tests/ui/self/arbitrary-self-types-not-object-safe.object_safe_for_dispatch.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ LL | trait Foo {
1414
| --- this trait cannot be made into an object...
1515
LL | fn foo(self: &Rc<Self>) -> usize;
1616
| ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on
17+
= help: only type `usize` implements the trait, consider using it directly instead
1718
= note: required for the cast from `Rc<usize>` to `Rc<dyn Foo>`
1819

1920
error: aborting due to previous error

tests/ui/traits/issue-38604.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LL | trait Foo where u32: Q<Self> {
1111
| --- ^^^^^^^ ...because it uses `Self` as a type parameter
1212
| |
1313
| this trait cannot be made into an object...
14+
= help: only type `()` implements the trait, consider using it directly instead
1415

1516
error[E0038]: the trait `Foo` cannot be made into an object
1617
--> $DIR/issue-38604.rs:15:9
@@ -25,6 +26,7 @@ LL | trait Foo where u32: Q<Self> {
2526
| --- ^^^^^^^ ...because it uses `Self` as a type parameter
2627
| |
2728
| this trait cannot be made into an object...
29+
= help: only type `()` implements the trait, consider using it directly instead
2830
= note: required for the cast from `Box<()>` to `Box<dyn Foo>`
2931

3032
error: aborting due to 2 previous errors

tests/ui/traits/item-privacy.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ LL | const C: u8 = 0;
143143
= help: consider moving `C` to another trait
144144
= help: consider moving `A` to another trait
145145
= help: consider moving `B` to another trait
146+
= help: only type `S` implements the trait, consider using it directly instead
146147

147148
error[E0223]: ambiguous associated type
148149
--> $DIR/item-privacy.rs:115:12

tests/ui/traits/non_lifetime_binders/supertrait-object-safety.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ LL | trait Foo: for<T> Bar<T> {}
2020
| --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables
2121
| |
2222
| this trait cannot be made into an object...
23+
= help: only type `()` implements the trait, consider using it directly instead
2324
= note: required for the cast from `&()` to `&dyn Foo`
2425

2526
error[E0038]: the trait `Foo` cannot be made into an object
@@ -35,6 +36,7 @@ LL | trait Foo: for<T> Bar<T> {}
3536
| --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables
3637
| |
3738
| this trait cannot be made into an object...
39+
= help: only type `()` implements the trait, consider using it directly instead
3840

3941
error[E0038]: the trait `Foo` cannot be made into an object
4042
--> $DIR/supertrait-object-safety.rs:22:5
@@ -49,6 +51,7 @@ LL | trait Foo: for<T> Bar<T> {}
4951
| --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables
5052
| |
5153
| this trait cannot be made into an object...
54+
= help: only type `()` implements the trait, consider using it directly instead
5255

5356
error: aborting due to 3 previous errors; 1 warning emitted
5457

tests/ui/traits/object/safety.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LL | trait Tr {
1111
| -- this trait cannot be made into an object...
1212
LL | fn foo();
1313
| ^^^ ...because associated function `foo` has no `self` parameter
14+
= help: only type `St` implements the trait, consider using it directly instead
1415
= note: required for the cast from `&St` to `&dyn Tr`
1516
help: consider turning `foo` into a method by giving it a `&self` argument
1617
|
@@ -34,6 +35,7 @@ LL | trait Tr {
3435
| -- this trait cannot be made into an object...
3536
LL | fn foo();
3637
| ^^^ ...because associated function `foo` has no `self` parameter
38+
= help: only type `St` implements the trait, consider using it directly instead
3739
help: consider turning `foo` into a method by giving it a `&self` argument
3840
|
3941
LL | fn foo(&self);

tests/ui/traits/test-2.stderr

+9
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ LL | trait bar { fn dup(&self) -> Self; fn blah<X>(&self); }
4242
| this trait cannot be made into an object...
4343
= help: consider moving `dup` to another trait
4444
= help: consider moving `blah` to another trait
45+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead:
46+
i32
47+
u32
4548

4649
error[E0038]: the trait `bar` cannot be made into an object
4750
--> $DIR/test-2.rs:13:5
@@ -59,6 +62,9 @@ LL | trait bar { fn dup(&self) -> Self; fn blah<X>(&self); }
5962
| this trait cannot be made into an object...
6063
= help: consider moving `dup` to another trait
6164
= help: consider moving `blah` to another trait
65+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead:
66+
i32
67+
u32
6268

6369
error[E0038]: the trait `bar` cannot be made into an object
6470
--> $DIR/test-2.rs:13:6
@@ -76,6 +82,9 @@ LL | trait bar { fn dup(&self) -> Self; fn blah<X>(&self); }
7682
| this trait cannot be made into an object...
7783
= help: consider moving `dup` to another trait
7884
= help: consider moving `blah` to another trait
85+
= help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead:
86+
i32
87+
u32
7988
= note: required for the cast from `Box<{integer}>` to `Box<dyn bar>`
8089

8190
error: aborting due to 5 previous errors

tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ LL | trait MyAdd<Rhs=Self> { fn add(&self, other: &Rhs) -> Self; }
2424
| |
2525
| this trait cannot be made into an object...
2626
= help: consider moving `add` to another trait
27+
= help: only type `i32` implements the trait, consider using it directly instead
2728

2829
error: aborting due to 2 previous errors
2930

0 commit comments

Comments
 (0)