Auto merge of #112319 - oli-obk:assoc_ty_sized_bound_for_object_safety2, 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`
This commit is contained in:
bors 2023-07-05 08:48:04 +00:00
commit 99f7d368c0
15 changed files with 652 additions and 404 deletions

View file

@ -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

View file

@ -6,14 +6,13 @@ mod bounds;
mod errors;
pub mod generics;
mod lint;
mod object_safety;
use crate::astconv::errors::prohibit_assoc_ty_binding;
use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args};
use crate::bounds::Bounds;
use crate::collect::HirPlaceholderCollector;
use crate::errors::{
AmbiguousLifetimeBound, TraitObjectDeclaredWithNoTraits, TypeofReservedKeywordUsed,
};
use crate::errors::{AmbiguousLifetimeBound, TypeofReservedKeywordUsed};
use crate::middle::resolve_bound_vars as rbv;
use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax;
@ -31,24 +30,17 @@ use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::DynKind;
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::spec::abi;
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{
self, astconv_object_safety_violations, NormalizeExt, ObligationCtxt,
};
use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCtxt};
use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use smallvec::{smallvec, SmallVec};
use std::collections::BTreeSet;
use std::fmt::Display;
use std::slice;
@ -926,380 +918,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
fn conv_object_ty_poly_trait_ref(
&self,
span: Span,
hir_trait_bounds: &[hir::PolyTraitRef<'_>],
lifetime: &hir::Lifetime,
borrowed: bool,
representation: DynKind,
) -> Ty<'tcx> {
let tcx = self.tcx();
let mut bounds = Bounds::default();
let mut potential_assoc_types = Vec::new();
let dummy_self = self.tcx().types.trait_object_dummy_self;
for trait_bound in hir_trait_bounds.iter().rev() {
if let GenericArgCountResult {
correct:
Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
..
} = self.instantiate_poly_trait_ref(
&trait_bound.trait_ref,
trait_bound.span,
ty::BoundConstness::NotConst,
ty::ImplPolarity::Positive,
dummy_self,
&mut bounds,
false,
// FIXME: This should be `true`, but we don't really handle
// associated type bounds or type aliases in objects in a way
// that makes this meaningful, I think.
OnlySelfBounds(false),
) {
potential_assoc_types.extend(cur_potential_assoc_types);
}
}
let mut trait_bounds = vec![];
let mut projection_bounds = vec![];
for (pred, span) in bounds.clauses() {
let bound_pred = pred.kind();
match bound_pred.skip_binder() {
ty::ClauseKind::Trait(trait_pred) => {
assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
trait_bounds.push((
bound_pred.rebind(trait_pred.trait_ref),
span,
trait_pred.constness,
));
}
ty::ClauseKind::Projection(proj) => {
projection_bounds.push((bound_pred.rebind(proj), span));
}
ty::ClauseKind::TypeOutlives(_) => {
// Do nothing, we deal with regions separately
}
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
bug!()
}
}
}
// Expand trait aliases recursively and check that only one regular (non-auto) trait
// is used and no 'maybe' bounds are used.
let expanded_traits =
traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
.filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
if regular_traits.len() > 1 {
let first_trait = &regular_traits[0];
let additional_trait = &regular_traits[1];
let mut err = struct_span_err!(
tcx.sess,
additional_trait.bottom().1,
E0225,
"only auto traits can be used as additional traits in a trait object"
);
additional_trait.label_with_exp_info(
&mut err,
"additional non-auto trait",
"additional use",
);
first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
err.help(format!(
"consider creating a new trait with all of these as supertraits and using that \
trait here instead: `trait NewTrait: {} {{}}`",
regular_traits
.iter()
.map(|t| t.trait_ref().print_only_trait_path().to_string())
.collect::<Vec<_>>()
.join(" + "),
));
err.note(
"auto-traits like `Send` and `Sync` are traits that have special properties; \
for more information on them, visit \
<https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
);
err.emit();
}
if regular_traits.is_empty() && auto_traits.is_empty() {
let trait_alias_span = trait_bounds
.iter()
.map(|&(trait_ref, _, _)| trait_ref.def_id())
.find(|&trait_ref| tcx.is_trait_alias(trait_ref))
.map(|trait_ref| tcx.def_span(trait_ref));
let reported =
tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
return tcx.ty_error(reported);
}
// Check that there are no gross object safety violations;
// most importantly, that the supertraits don't contain `Self`,
// to avoid ICEs.
for item in &regular_traits {
let object_safety_violations =
astconv_object_safety_violations(tcx, item.trait_ref().def_id());
if !object_safety_violations.is_empty() {
let reported = report_object_safety_error(
tcx,
span,
item.trait_ref().def_id(),
&object_safety_violations,
)
.emit();
return tcx.ty_error(reported);
}
}
// Use a `BTreeSet` to keep output in a more consistent order.
let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
let regular_traits_refs_spans = trait_bounds
.into_iter()
.filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
for (base_trait_ref, span, constness) in regular_traits_refs_spans {
assert_eq!(constness, ty::BoundConstness::NotConst);
let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
for pred in traits::elaborate(tcx, [base_pred]) {
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
let pred = bound_predicate.rebind(pred);
associated_types.entry(span).or_default().extend(
tcx.associated_items(pred.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.filter(|item| item.opt_rpitit_info.is_none())
.map(|item| item.def_id),
);
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
let pred = bound_predicate.rebind(pred);
// A `Self` within the original bound will be substituted with a
// `trait_object_dummy_self`, so check for that.
let references_self = match pred.skip_binder().term.unpack() {
ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
ty::TermKind::Const(c) => {
c.ty().walk().any(|arg| arg == dummy_self.into())
}
};
// If the projection output contains `Self`, force the user to
// elaborate it explicitly to avoid a lot of complexity.
//
// The "classically useful" case is the following:
// ```
// trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
// type MyOutput;
// }
// ```
//
// Here, the user could theoretically write `dyn MyTrait<Output = X>`,
// but actually supporting that would "expand" to an infinitely-long type
// `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
//
// Instead, we force the user to write
// `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
// the discussion in #56288 for alternatives.
if !references_self {
// Include projections defined on supertraits.
projection_bounds.push((pred, span));
}
}
_ => (),
}
}
}
// `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where <Self as Trait>::Assoc = Foo`.
// So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
// types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
// corresponding `Projection` clause
for (projection_bound, _) in &projection_bounds {
for def_ids in associated_types.values_mut() {
def_ids.remove(&projection_bound.projection_def_id());
}
}
self.complain_about_missing_associated_types(
associated_types,
potential_assoc_types,
hir_trait_bounds,
);
// De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
// `dyn Trait + Send`.
// We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
// the bounds
let mut duplicates = FxHashSet::default();
auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
debug!("regular_traits: {:?}", regular_traits);
debug!("auto_traits: {:?}", auto_traits);
// Erase the `dummy_self` (`trait_object_dummy_self`) used above.
let existential_trait_refs = regular_traits.iter().map(|i| {
i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
assert_eq!(trait_ref.self_ty(), dummy_self);
// Verify that `dummy_self` did not leak inside default type parameters. This
// could not be done at path creation, since we need to see through trait aliases.
let mut missing_type_params = vec![];
let mut references_self = false;
let generics = tcx.generics_of(trait_ref.def_id);
let substs: Vec<_> = trait_ref
.substs
.iter()
.enumerate()
.skip(1) // Remove `Self` for `ExistentialPredicate`.
.map(|(index, arg)| {
if arg == dummy_self.into() {
let param = &generics.params[index];
missing_type_params.push(param.name);
return tcx.ty_error_misc().into();
} else if arg.walk().any(|arg| arg == dummy_self.into()) {
references_self = true;
return tcx.ty_error_misc().into();
}
arg
})
.collect();
let substs = tcx.mk_substs(&substs);
let span = i.bottom().1;
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
&& hir_bound.span.contains(span)
});
self.complain_about_missing_type_params(
missing_type_params,
trait_ref.def_id,
span,
empty_generic_args,
);
if references_self {
let def_id = i.bottom().0.def_id();
let mut err = struct_span_err!(
tcx.sess,
i.bottom().1,
E0038,
"the {} `{}` cannot be made into an object",
tcx.def_descr(def_id),
tcx.item_name(def_id),
);
err.note(
rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
.error_msg(),
);
err.emit();
}
ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
})
});
let existential_projections = projection_bounds
.iter()
// We filter out traits that don't have `Self` as their self type above,
// we need to do the same for projections.
.filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
.map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_ty.self_ty(), dummy_self);
// Like for trait refs, verify that `dummy_self` did not leak inside default type
// parameters.
let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return true;
}
false
});
if references_self {
let guar = tcx.sess.delay_span_bug(
span,
"trait object projection bounds reference `Self`",
);
let substs: Vec<_> = b
.projection_ty
.substs
.iter()
.map(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return tcx.ty_error(guar).into();
}
arg
})
.collect();
b.projection_ty.substs = tcx.mk_substs(&substs);
}
ty::ExistentialProjection::erase_self_ty(tcx, b)
})
});
let regular_trait_predicates = existential_trait_refs
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
});
// N.b. principal, projections, auto traits
// FIXME: This is actually wrong with multiple principals in regards to symbol mangling
let mut v = regular_trait_predicates
.chain(
existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
)
.chain(auto_trait_predicates)
.collect::<SmallVec<[_; 8]>>();
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
v.dedup();
let existential_predicates = tcx.mk_poly_existential_predicates(&v);
// Use explicitly-specified region bound.
let region_bound = if !lifetime.is_elided() {
self.ast_region_to_region(lifetime, None)
} else {
self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
if tcx.named_bound_var(lifetime.hir_id).is_some() {
self.ast_region_to_region(lifetime, None)
} else {
self.re_infer(None, span).unwrap_or_else(|| {
let mut err = struct_span_err!(
tcx.sess,
span,
E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound"
);
let e = if borrowed {
// We will have already emitted an error E0106 complaining about a
// missing named lifetime in `&dyn Trait`, so we elide this one.
err.delay_as_bug()
} else {
err.emit()
};
ty::Region::new_error(tcx, e)
})
}
})
};
debug!("region_bound: {:?}", region_bound);
let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation);
debug!("trait_object_type: {:?}", ty);
ty
}
fn report_ambiguous_associated_type(
&self,
span: Span,
@ -2802,7 +2420,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
TraitObjectSyntax::DynStar => ty::DynStar,
};
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
self.conv_object_ty_poly_trait_ref(
ast_ty.span,
ast_ty.hir_id,
bounds,
lifetime,
borrowed,
repr,
)
}
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);

View file

@ -0,0 +1,408 @@
use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds};
use crate::bounds::Bounds;
use crate::errors::TraitObjectDeclaredWithNoTraits;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{DynKind, ToPredicate};
use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
use rustc_trait_selection::traits::{self, astconv_object_safety_violations};
use smallvec::{smallvec, SmallVec};
use std::collections::BTreeSet;
use super::AstConv;
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
pub(super) fn conv_object_ty_poly_trait_ref(
&self,
span: Span,
hir_id: hir::HirId,
hir_trait_bounds: &[hir::PolyTraitRef<'_>],
lifetime: &hir::Lifetime,
borrowed: bool,
representation: DynKind,
) -> Ty<'tcx> {
let tcx = self.tcx();
let mut bounds = Bounds::default();
let mut potential_assoc_types = Vec::new();
let dummy_self = self.tcx().types.trait_object_dummy_self;
for trait_bound in hir_trait_bounds.iter().rev() {
if let GenericArgCountResult {
correct:
Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
..
} = self.instantiate_poly_trait_ref(
&trait_bound.trait_ref,
trait_bound.span,
ty::BoundConstness::NotConst,
ty::ImplPolarity::Positive,
dummy_self,
&mut bounds,
false,
// FIXME: This should be `true`, but we don't really handle
// associated type bounds or type aliases in objects in a way
// that makes this meaningful, I think.
OnlySelfBounds(false),
) {
potential_assoc_types.extend(cur_potential_assoc_types);
}
}
let mut trait_bounds = vec![];
let mut projection_bounds = vec![];
for (pred, span) in bounds.clauses() {
let bound_pred = pred.kind();
match bound_pred.skip_binder() {
ty::ClauseKind::Trait(trait_pred) => {
assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
trait_bounds.push((
bound_pred.rebind(trait_pred.trait_ref),
span,
trait_pred.constness,
));
}
ty::ClauseKind::Projection(proj) => {
projection_bounds.push((bound_pred.rebind(proj), span));
}
ty::ClauseKind::TypeOutlives(_) => {
// Do nothing, we deal with regions separately
}
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
bug!()
}
}
}
// Expand trait aliases recursively and check that only one regular (non-auto) trait
// is used and no 'maybe' bounds are used.
let expanded_traits =
traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
.filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
if regular_traits.len() > 1 {
let first_trait = &regular_traits[0];
let additional_trait = &regular_traits[1];
let mut err = struct_span_err!(
tcx.sess,
additional_trait.bottom().1,
E0225,
"only auto traits can be used as additional traits in a trait object"
);
additional_trait.label_with_exp_info(
&mut err,
"additional non-auto trait",
"additional use",
);
first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
err.help(format!(
"consider creating a new trait with all of these as supertraits and using that \
trait here instead: `trait NewTrait: {} {{}}`",
regular_traits
.iter()
.map(|t| t.trait_ref().print_only_trait_path().to_string())
.collect::<Vec<_>>()
.join(" + "),
));
err.note(
"auto-traits like `Send` and `Sync` are traits that have special properties; \
for more information on them, visit \
<https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
);
err.emit();
}
if regular_traits.is_empty() && auto_traits.is_empty() {
let trait_alias_span = trait_bounds
.iter()
.map(|&(trait_ref, _, _)| trait_ref.def_id())
.find(|&trait_ref| tcx.is_trait_alias(trait_ref))
.map(|trait_ref| tcx.def_span(trait_ref));
let reported =
tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
return tcx.ty_error(reported);
}
// Check that there are no gross object safety violations;
// most importantly, that the supertraits don't contain `Self`,
// to avoid ICEs.
for item in &regular_traits {
let object_safety_violations =
astconv_object_safety_violations(tcx, item.trait_ref().def_id());
if !object_safety_violations.is_empty() {
let reported = report_object_safety_error(
tcx,
span,
item.trait_ref().def_id(),
&object_safety_violations,
)
.emit();
return tcx.ty_error(reported);
}
}
// Use a `BTreeSet` to keep output in a more consistent order.
let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
let regular_traits_refs_spans = trait_bounds
.into_iter()
.filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
for (base_trait_ref, span, constness) in regular_traits_refs_spans {
assert_eq!(constness, ty::BoundConstness::NotConst);
let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
for pred in traits::elaborate(tcx, [base_pred]) {
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
let pred = bound_predicate.rebind(pred);
associated_types.entry(span).or_default().extend(
tcx.associated_items(pred.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.filter(|item| item.opt_rpitit_info.is_none())
.map(|item| item.def_id),
);
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
let pred = bound_predicate.rebind(pred);
// A `Self` within the original bound will be substituted with a
// `trait_object_dummy_self`, so check for that.
let references_self = match pred.skip_binder().term.unpack() {
ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
ty::TermKind::Const(c) => {
c.ty().walk().any(|arg| arg == dummy_self.into())
}
};
// If the projection output contains `Self`, force the user to
// elaborate it explicitly to avoid a lot of complexity.
//
// The "classically useful" case is the following:
// ```
// trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
// type MyOutput;
// }
// ```
//
// Here, the user could theoretically write `dyn MyTrait<Output = X>`,
// but actually supporting that would "expand" to an infinitely-long type
// `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
//
// Instead, we force the user to write
// `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
// the discussion in #56288 for alternatives.
if !references_self {
// Include projections defined on supertraits.
projection_bounds.push((pred, span));
}
}
_ => (),
}
}
}
// `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where <Self as Trait>::Assoc = Foo`.
// So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
// types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
// corresponding `Projection` clause
for def_ids in associated_types.values_mut() {
for (projection_bound, span) in &projection_bounds {
let def_id = projection_bound.projection_def_id();
def_ids.remove(&def_id);
if tcx.generics_require_sized_self(def_id) {
tcx.emit_spanned_lint(
UNUSED_ASSOCIATED_TYPE_BOUNDS,
hir_id,
*span,
crate::errors::UnusedAssociatedTypeBounds { span: *span },
);
}
}
// If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated
// type in the `dyn Trait`.
def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
}
self.complain_about_missing_associated_types(
associated_types,
potential_assoc_types,
hir_trait_bounds,
);
// De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
// `dyn Trait + Send`.
// We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
// the bounds
let mut duplicates = FxHashSet::default();
auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
debug!("regular_traits: {:?}", regular_traits);
debug!("auto_traits: {:?}", auto_traits);
// Erase the `dummy_self` (`trait_object_dummy_self`) used above.
let existential_trait_refs = regular_traits.iter().map(|i| {
i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
assert_eq!(trait_ref.self_ty(), dummy_self);
// Verify that `dummy_self` did not leak inside default type parameters. This
// could not be done at path creation, since we need to see through trait aliases.
let mut missing_type_params = vec![];
let mut references_self = false;
let generics = tcx.generics_of(trait_ref.def_id);
let substs: Vec<_> = trait_ref
.substs
.iter()
.enumerate()
.skip(1) // Remove `Self` for `ExistentialPredicate`.
.map(|(index, arg)| {
if arg == dummy_self.into() {
let param = &generics.params[index];
missing_type_params.push(param.name);
return tcx.ty_error_misc().into();
} else if arg.walk().any(|arg| arg == dummy_self.into()) {
references_self = true;
return tcx.ty_error_misc().into();
}
arg
})
.collect();
let substs = tcx.mk_substs(&substs);
let span = i.bottom().1;
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
&& hir_bound.span.contains(span)
});
self.complain_about_missing_type_params(
missing_type_params,
trait_ref.def_id,
span,
empty_generic_args,
);
if references_self {
let def_id = i.bottom().0.def_id();
let mut err = struct_span_err!(
tcx.sess,
i.bottom().1,
E0038,
"the {} `{}` cannot be made into an object",
tcx.def_descr(def_id),
tcx.item_name(def_id),
);
err.note(
rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
.error_msg(),
);
err.emit();
}
ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
})
});
let existential_projections = projection_bounds
.iter()
// We filter out traits that don't have `Self` as their self type above,
// we need to do the same for projections.
.filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
.map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_ty.self_ty(), dummy_self);
// Like for trait refs, verify that `dummy_self` did not leak inside default type
// parameters.
let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return true;
}
false
});
if references_self {
let guar = tcx.sess.delay_span_bug(
span,
"trait object projection bounds reference `Self`",
);
let substs: Vec<_> = b
.projection_ty
.substs
.iter()
.map(|arg| {
if arg.walk().any(|arg| arg == dummy_self.into()) {
return tcx.ty_error(guar).into();
}
arg
})
.collect();
b.projection_ty.substs = tcx.mk_substs(&substs);
}
ty::ExistentialProjection::erase_self_ty(tcx, b)
})
});
let regular_trait_predicates = existential_trait_refs
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
});
// N.b. principal, projections, auto traits
// FIXME: This is actually wrong with multiple principals in regards to symbol mangling
let mut v = regular_trait_predicates
.chain(
existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
)
.chain(auto_trait_predicates)
.collect::<SmallVec<[_; 8]>>();
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
v.dedup();
let existential_predicates = tcx.mk_poly_existential_predicates(&v);
// Use explicitly-specified region bound.
let region_bound = if !lifetime.is_elided() {
self.ast_region_to_region(lifetime, None)
} else {
self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
if tcx.named_bound_var(lifetime.hir_id).is_some() {
self.ast_region_to_region(lifetime, None)
} else {
self.re_infer(None, span).unwrap_or_else(|| {
let mut err = struct_span_err!(
tcx.sess,
span,
E0228,
"the lifetime bound for this object type cannot be deduced \
from context; please supply an explicit bound"
);
let e = if borrowed {
// We will have already emitted an error E0106 complaining about a
// missing named lifetime in `&dyn Trait`, so we elide this one.
err.delay_as_bug()
} else {
err.emit()
};
ty::Region::new_error(tcx, e)
})
}
})
};
debug!("region_bound: {:?}", region_bound);
let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation);
debug!("trait_object_type: {:?}", ty);
ty
}
}

View file

@ -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};
@ -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,
}

View file

@ -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,
@ -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`.

View file

@ -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! }

View file

@ -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;
}
@ -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 {
@ -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;
}
@ -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
};
}

View file

@ -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() {}

View file

@ -1,12 +0,0 @@
error[E0191]: the value of the associated type `Bar` (from trait `Foo`) must be specified
--> $DIR/assoc_type_bounds_sized.rs:7:16
|
LL | type Bar
| -------- `Bar` defined here
...
LL | fn foo(_: &dyn Foo) {}
| ^^^ help: specify the associated type: `Foo<Bar = Type>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0191`.

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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

View file

@ -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
}

View file

@ -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`.