Skip to content

Commit

Permalink
Auto merge of #112319 - oli-obk:assoc_ty_sized_bound_for_object_safet…
Browse files Browse the repository at this point in the history
…y2, r=compiler-errors

Don't require associated types with Self: Sized bounds in `dyn Trait` objects

Trait objects require *all* associated types to be specified, even if the associated type has an explicit `where Self: Sized` bound. The following snippet does not compile on master, but does with this PR.

```rust
fn _assert_is_object_safe(_: &dyn Foo) {}

pub trait Foo {
    type Bar where Self: Sized;
}
```

In contrast, if a `Self: Sized` bound is added to a method, the methodjust isn't callable on trait objects, but the trait can be made object safe just fine.

```rust
fn _assert_is_object_safe(_: &dyn Foo) {}

pub trait Foo {
    fn foo() where Self: Sized;
}
```

This PR closes this inconsistency (though it still exists for associated constants).

Additionally this PR adds a new lint that informs users they can remove associated type bounds from their trait objects if those associated type bounds have a `where Self: Sized` bound, and are thus useless.

r? `@compiler-errors`
  • Loading branch information
bors committed Jul 5, 2023
2 parents 6dab6dc + f80aec7 commit 99f7d36
Show file tree
Hide file tree
Showing 15 changed files with 652 additions and 404 deletions.
5 changes: 5 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ hir_analysis_unrecognized_intrinsic_function =
unrecognized intrinsic function: `{$name}`
.label = unrecognized intrinsic
hir_analysis_unused_associated_type_bounds =
unnecessary associated type bound for not object safe associated type
.note = this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`.
.suggestion = remove this bound
hir_analysis_value_of_associated_struct_already_specified =
the value of the associated type `{$item_name}` (from trait `{$def_path}`) is already specified
.label = re-bound here
Expand Down
397 changes: 11 additions & 386 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs

Large diffs are not rendered by default.

408 changes: 408 additions & 0 deletions compiler/rustc_hir_analysis/src/astconv/object_safety.rs

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_errors::{
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
MultiSpan,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
use rustc_span::{symbol::Ident, Span, Symbol};

Expand Down Expand Up @@ -900,3 +900,11 @@ pub(crate) enum LateBoundInApit {
param_span: Span,
},
}

#[derive(LintDiagnostic)]
#[diag(hir_analysis_unused_associated_type_bounds)]
#[note]
pub struct UnusedAssociatedTypeBounds {
#[suggestion(code = "")]
pub span: Span,
}
27 changes: 27 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3409,6 +3409,7 @@ declare_lint_pass! {
UNSTABLE_SYNTAX_PRE_EXPANSION,
UNSUPPORTED_CALLING_CONVENTIONS,
UNUSED_ASSIGNMENTS,
UNUSED_ASSOCIATED_TYPE_BOUNDS,
UNUSED_ATTRIBUTES,
UNUSED_CRATE_DEPENDENCIES,
UNUSED_EXTERN_CRATES,
Expand Down Expand Up @@ -3468,6 +3469,32 @@ declare_lint! {
report_in_external_macro
}

declare_lint! {
/// The `unused_associated_type_bounds` lint is emitted when an
/// associated type bound is added to a trait object, but the associated
/// type has a `where Self: Sized` bound, and is thus unavailable on the
/// trait object anyway.
///
/// ### Example
///
/// ```rust
/// trait Foo {
/// type Bar where Self: Sized;
/// }
/// type Mop = dyn Foo<Bar = ()>;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Just like methods with `Self: Sized` bounds are unavailable on trait
/// objects, associated types can be removed from the trait object.
pub UNUSED_ASSOCIATED_TYPE_BOUNDS,
Warn,
"detects unused `Foo = Bar` bounds in `dyn Trait<Foo = Bar>`"
}

declare_lint! {
/// The `unused_doc_comments` lint detects doc comments that aren't used
/// by `rustdoc`.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,10 @@ rustc_queries! {
desc { "getting cfg-ed out item names" }
separate_provide_extern
}

query generics_require_sized_self(def_id: DefId) -> bool {
desc { "check whether the item has a `where Self: Sized` bound" }
}
}

rustc_query_append! { define_callbacks! }
Expand Down
13 changes: 9 additions & 4 deletions compiler/rustc_trait_selection/src/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A
debug_assert!(tcx.generics_of(trait_def_id).has_self);
debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
// Any method that has a `Self: Sized` bound cannot be called.
if generics_require_sized_self(tcx, method.def_id) {
if tcx.generics_require_sized_self(method.def_id) {
return false;
}

Expand Down Expand Up @@ -331,7 +331,7 @@ fn super_predicates_have_non_lifetime_binders(
}

fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
generics_require_sized_self(tcx, trait_def_id)
tcx.generics_require_sized_self(trait_def_id)
}

fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
Expand Down Expand Up @@ -364,7 +364,7 @@ fn object_safety_violation_for_assoc_item(
) -> Option<ObjectSafetyViolation> {
// Any item that has a `Self : Sized` requisite is otherwise
// exempt from the regulations.
if generics_require_sized_self(tcx, item.def_id) {
if tcx.generics_require_sized_self(item.def_id) {
return None;
}

Expand Down Expand Up @@ -922,5 +922,10 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
}

pub fn provide(providers: &mut Providers) {
*providers = Providers { object_safety_violations, check_is_object_safe, ..*providers };
*providers = Providers {
object_safety_violations,
check_is_object_safe,
generics_require_sized_self,
..*providers
};
}
17 changes: 16 additions & 1 deletion tests/ui/object-safety/assoc_type_bounds_sized.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
//! This test checks that associated types only need to be
//! mentioned in trait objects, if they don't require `Self: Sized`.

// check-pass

trait Foo {
type Bar
where
Self: Sized;
}

fn foo(_: &dyn Foo) {} //~ ERROR: the value of the associated type `Bar` (from trait `Foo`) must be specified
fn foo(_: &dyn Foo) {}

trait Other: Sized {}

trait Boo {
type Assoc
where
Self: Other;
}

fn boo(_: &dyn Boo) {}

fn main() {}
12 changes: 0 additions & 12 deletions tests/ui/object-safety/assoc_type_bounds_sized.stderr

This file was deleted.

25 changes: 25 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized_others.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! This test checks that even if some associated types have
//! `where Self: Sized` bounds, those without still need to be
//! mentioned in trait objects.

trait Foo {
type Bar
where
Self: Sized;
type Bop;
}

fn foo(_: &dyn Foo) {}
//~^ ERROR the value of the associated type `Bop` (from trait `Foo`) must be specified

trait Bar {
type Bop;
type Bar
where
Self: Sized;
}

fn bar(_: &dyn Bar) {}
//~^ ERROR the value of the associated type `Bop` (from trait `Bar`) must be specified

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized_others.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0191]: the value of the associated type `Bop` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds_sized_others.rs:12:16
|
LL | type Bop;
| -------- `Bop` defined here
...
LL | fn foo(_: &dyn Foo) {}
| ^^^ help: specify the associated type: `Foo<Bop = Type>`

error[E0191]: the value of the associated type `Bop` (from trait `Bar`) must be specified
--> $DIR/assoc_type_bounds_sized_others.rs:22:16
|
LL | type Bop;
| -------- `Bop` defined here
...
LL | fn bar(_: &dyn Bar) {}
| ^^^ help: specify the associated type: `Bar<Bop = Type>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0191`.
17 changes: 17 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// check-pass

trait Foo {
type Bar
where
Self: Sized;
}

fn foo(_: &dyn Foo<Bar = ()>) {}
//~^ WARN: unnecessary associated type bound for not object safe associated type
//~| WARN: unnecessary associated type bound for not object safe associated type
//~| WARN: unnecessary associated type bound for not object safe associated type

#[allow(unused_associated_type_bounds)]
fn bar(_: &dyn Foo<Bar = ()>) {}

fn main() {}
27 changes: 27 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
warning: unnecessary associated type bound for not object safe associated type
--> $DIR/assoc_type_bounds_sized_unnecessary.rs:9:20
|
LL | fn foo(_: &dyn Foo<Bar = ()>) {}
| ^^^^^^^^ help: remove this bound
|
= note: this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`.
= note: `#[warn(unused_associated_type_bounds)]` on by default

warning: unnecessary associated type bound for not object safe associated type
--> $DIR/assoc_type_bounds_sized_unnecessary.rs:9:20
|
LL | fn foo(_: &dyn Foo<Bar = ()>) {}
| ^^^^^^^^ help: remove this bound
|
= note: this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`.

warning: unnecessary associated type bound for not object safe associated type
--> $DIR/assoc_type_bounds_sized_unnecessary.rs:9:20
|
LL | fn foo(_: &dyn Foo<Bar = ()>) {}
| ^^^^^^^^ help: remove this bound
|
= note: this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`.

warning: 3 warnings emitted

20 changes: 20 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized_used.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! This test checks that even if some associated types have
//! `where Self: Sized` bounds, those without still need to be
//! mentioned in trait objects.

trait Bop {
type Bar: Default
where
Self: Sized;
}

fn bop<T: Bop + ?Sized>() {
let _ = <T as Bop>::Bar::default();
//~^ ERROR: trait bounds were not satisfied
//~| ERROR: the size for values of type `T` cannot be known at compilation time
}

fn main() {
bop::<dyn Bop>();
//~^ ERROR: the size for values of type `dyn Bop` cannot be known at compilation time
}
53 changes: 53 additions & 0 deletions tests/ui/object-safety/assoc_type_bounds_sized_used.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
error[E0599]: the function or associated item `default` exists for associated type `<T as Bop>::Bar`, but its trait bounds were not satisfied
--> $DIR/assoc_type_bounds_sized_used.rs:12:30
|
LL | let _ = <T as Bop>::Bar::default();
| ^^^^^^^ function or associated item cannot be called on `<T as Bop>::Bar` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`T: Sized`
which is required by `<T as Bop>::Bar: Default`
help: consider restricting the type parameter to satisfy the trait bound
|
LL | fn bop<T: Bop + ?Sized>() where T: Sized {
| ++++++++++++++

error[E0277]: the size for values of type `T` cannot be known at compilation time
--> $DIR/assoc_type_bounds_sized_used.rs:12:13
|
LL | fn bop<T: Bop + ?Sized>() {
| - this type parameter needs to be `Sized`
LL | let _ = <T as Bop>::Bar::default();
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
note: required by a bound in `Bop::Bar`
--> $DIR/assoc_type_bounds_sized_used.rs:8:15
|
LL | type Bar: Default
| --- required by a bound in this associated type
LL | where
LL | Self: Sized;
| ^^^^^ required by this bound in `Bop::Bar`
help: consider removing the `?Sized` bound to make the type parameter `Sized`
|
LL - fn bop<T: Bop + ?Sized>() {
LL + fn bop<T: Bop>() {
|

error[E0277]: the size for values of type `dyn Bop` cannot be known at compilation time
--> $DIR/assoc_type_bounds_sized_used.rs:18:11
|
LL | bop::<dyn Bop>();
| ^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn Bop`
note: required by a bound in `bop`
--> $DIR/assoc_type_bounds_sized_used.rs:11:11
|
LL | fn bop<T: Bop + ?Sized>() {
| ^^^ required by this bound in `bop`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.

0 comments on commit 99f7d36

Please sign in to comment.