Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deduplicate object safety errors on impl dyn Trait { .. } #121200

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions compiler/rustc_hir_analysis/src/outlives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::query::Providers;
Expand Down Expand Up @@ -46,6 +47,24 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
&[]
}
}
DefKind::Impl { of_trait: false }
Copy link
Member

@compiler-errors compiler-errors Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a solution that doesn't involve changing the rules for where we add implicit outlives obligations. This code is really delicate, and I'm worried this could turn out to be unsound without us really knowing why.

Is it possible to instead modify the object safety violation error reporting code to instead prefer pointing at Self type rather than arguments? We can get away with corner cases in diagnostics reporting code, but can't really get away with it in good-path code 😅

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, could you explain why adding this outlives obligation actually solves the problem? It's not necessarily clear why this is fixing what it does, and I'm not yet convinced its worth the changes to the inferred_outlives_of function for such a special case.

if let hir::ItemKind::Impl(item) = tcx.hir().expect_item(item_def_id).kind
&& let hir::Impl { self_ty, .. } = &item
&& let hir::TyKind::TraitObject(
_,
hir::Lifetime { res: hir::LifetimeName::ImplicitObjectLifetimeDefault, .. },
_,
) = self_ty.kind =>
{
// `impl dyn Trait {}` has an implicit `dyn Trait: 'static` bound. We add it here for
// more context in errors. With this we point at `impl dyn Trait` and not to it's
// associated functions, deduplicating errors to a single one per `impl dyn Trait` instead
// of one for it plus one for each associated function.
let ty = tcx.type_of(item_def_id).instantiate_identity();
Copy link
Member

@compiler-errors compiler-errors Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this should at least be asserting that the implicit object outlives is actually static:

let ty::Dynamic(_, outlives, _) = *ty.kind() else {
    bug!("expected dyn self type");
};
assert_eq!(outlives, tcx.regions.re_static);

Then at least if the object outlives bound computation changes down the road, this will ICE.

let clause_kind =
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, tcx.lifetimes.re_static));
&*tcx.arena.alloc_from_iter([(clause_kind.to_predicate(tcx), self_ty.span)])
}
_ => &[],
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/ui/associated-consts/associated-const-in-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ trait Trait {

impl dyn Trait {
//~^ ERROR the trait `Trait` cannot be made into an object [E0038]
//~| ERROR the trait `Trait` cannot be made into an object [E0038]
const fn n() -> usize { Self::N }
//~^ ERROR the trait `Trait` cannot be made into an object [E0038]
}
Expand Down
20 changes: 18 additions & 2 deletions tests/ui/associated-consts/associated-const-in-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,23 @@ LL | const N: usize;
= help: consider moving `N` to another trait

error[E0038]: the trait `Trait` cannot be made into an object
--> $DIR/associated-const-in-trait.rs:9:29
--> $DIR/associated-const-in-trait.rs:7:6
|
LL | impl dyn Trait {
| ^^^^^^^^^ `Trait` 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/associated-const-in-trait.rs:4:11
|
LL | trait Trait {
| ----- this trait cannot be made into an object...
LL | const N: usize;
| ^ ...because it contains this associated `const`
= help: consider moving `N` to another trait
Copy link
Member

@fmease fmease Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally unrelated but if tcx.features().generic_const_items we could suggest alternatively, consider constraining `N` so it does not apply to trait objects // const N: usize where Self: Sized;

= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0038]: the trait `Trait` cannot be made into an object
--> $DIR/associated-const-in-trait.rs:10:29
|
LL | const fn n() -> usize { Self::N }
| ^^^^ `Trait` cannot be made into an object
Expand All @@ -28,6 +44,6 @@ LL | const N: usize;
| ^ ...because it contains this associated `const`
= help: consider moving `N` to another trait

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0038`.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ impl MyTrait for Outer {

impl dyn MyTrait {
//~^ ERROR the trait `MyTrait` cannot be made into an object
//~| ERROR the trait `MyTrait` cannot be made into an object
fn other(&self) -> impl Marker {
//~^ ERROR the trait `MyTrait` cannot be made into an object
MyTrait::foo(&self)
//~^ ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied
//~| ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ LL | fn foo(&self) -> impl Marker;
= help: only type `Outer` implements the trait, consider using it directly instead

error[E0038]: the trait `MyTrait` cannot be made into an object
--> $DIR/cycle-effective-visibilities-during-object-safety.rs:18:15
--> $DIR/cycle-effective-visibilities-during-object-safety.rs:16:6
|
LL | fn other(&self) -> impl Marker {
| ^^^^ `MyTrait` cannot be made into an object
LL | impl dyn MyTrait {
| ^^^^^^^^^^^ `MyTrait` 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/cycle-effective-visibilities-during-object-safety.rs:5:22
Expand All @@ -63,6 +63,7 @@ LL | fn foo(&self) -> impl Marker;
| ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type
= help: consider moving `foo` to another trait
= help: only type `Outer` implements the trait, consider using it directly instead
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 5 previous errors

Expand Down
Loading