Auto merge of #133830 - compiler-errors:span-key, r=lcnr
Rework dyn trait lowering to stop being so intertwined with trait alias expansion This PR reworks the trait object lowering code to stop handling trait aliases so funky, and removes the `TraitAliasExpander` in favor of a much simpler design. This refactoring is important for making the code that I'm writing in https://github.com/rust-lang/rust/pull/133397 understandable and easy to maintain, so the diagnostics regressions are IMO inevitable. In the old trait object lowering code, we used to be a bit sloppy with the lists of traits in their unexpanded and expanded forms. This PR largely rewrites this logic to expand the trait aliases *once* and handle them more responsibly throughout afterwards. Please review this with whitespace disabled. r? lcnr
This commit is contained in:
commit
cd805f09ff
26 changed files with 576 additions and 657 deletions
|
@ -4,13 +4,12 @@ use rustc_errors::struct_span_code_err;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
use rustc_middle::ty::{
|
||||
self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
|
||||
TypeVisitableExt, Upcast,
|
||||
};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
|
||||
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
|
||||
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
|
||||
|
@ -30,16 +29,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
&self,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
hir_trait_bounds: &[hir::PolyTraitRef<'tcx>],
|
||||
hir_bounds: &[hir::PolyTraitRef<'tcx>],
|
||||
lifetime: &hir::Lifetime,
|
||||
representation: DynKind,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx();
|
||||
let dummy_self = tcx.types.trait_object_dummy_self;
|
||||
|
||||
let mut bounds = Bounds::default();
|
||||
let mut user_written_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() {
|
||||
for trait_bound in hir_bounds.iter() {
|
||||
if let hir::BoundPolarity::Maybe(_) = trait_bound.modifiers.polarity {
|
||||
continue;
|
||||
}
|
||||
|
@ -53,92 +52,67 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
hir::BoundConstness::Never,
|
||||
hir::BoundPolarity::Positive,
|
||||
dummy_self,
|
||||
&mut bounds,
|
||||
&mut user_written_bounds,
|
||||
PredicateFilter::SelfOnly,
|
||||
) {
|
||||
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::PredicatePolarity::Positive);
|
||||
trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span));
|
||||
}
|
||||
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(_)
|
||||
| ty::ClauseKind::HostEffect(..) => {
|
||||
span_bug!(span, "did not expect {pred} clause in object bounds");
|
||||
}
|
||||
}
|
||||
let (trait_bounds, mut projection_bounds) =
|
||||
traits::expand_trait_aliases(tcx, user_written_bounds.clauses());
|
||||
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds
|
||||
.into_iter()
|
||||
.partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
||||
|
||||
// We don't support empty trait objects.
|
||||
if regular_traits.is_empty() && auto_traits.is_empty() {
|
||||
let guar =
|
||||
self.report_trait_object_with_no_traits_error(span, user_written_bounds.clauses());
|
||||
return Ty::new_error(tcx, guar);
|
||||
}
|
||||
|
||||
// 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.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
|
||||
|
||||
// We don't support >1 principal
|
||||
if regular_traits.len() > 1 {
|
||||
let guar = self.report_trait_object_addition_traits_error(®ular_traits);
|
||||
return Ty::new_error(tcx, guar);
|
||||
}
|
||||
// We don't support empty trait objects.
|
||||
if regular_traits.is_empty() && auto_traits.is_empty() {
|
||||
let guar = self.report_trait_object_with_no_traits_error(span, &trait_bounds);
|
||||
return Ty::new_error(tcx, guar);
|
||||
}
|
||||
// Don't create a dyn trait if we have errors in the principal.
|
||||
if let Err(guar) = trait_bounds.error_reported() {
|
||||
if let Err(guar) = regular_traits.error_reported() {
|
||||
return Ty::new_error(tcx, guar);
|
||||
}
|
||||
|
||||
// Check that there are no gross dyn-compatibility violations;
|
||||
// most importantly, that the supertraits don't contain `Self`,
|
||||
// to avoid ICEs.
|
||||
for item in ®ular_traits {
|
||||
let violations =
|
||||
hir_ty_lowering_dyn_compatibility_violations(tcx, item.trait_ref().def_id());
|
||||
if !violations.is_empty() {
|
||||
let reported = report_dyn_incompatibility(
|
||||
tcx,
|
||||
span,
|
||||
Some(hir_id),
|
||||
item.trait_ref().def_id(),
|
||||
&violations,
|
||||
)
|
||||
.emit();
|
||||
return Ty::new_error(tcx, reported);
|
||||
for (clause, span) in user_written_bounds.clauses() {
|
||||
if let Some(trait_pred) = clause.as_trait_clause() {
|
||||
let violations =
|
||||
hir_ty_lowering_dyn_compatibility_violations(tcx, trait_pred.def_id());
|
||||
if !violations.is_empty() {
|
||||
let reported = report_dyn_incompatibility(
|
||||
tcx,
|
||||
span,
|
||||
Some(hir_id),
|
||||
trait_pred.def_id(),
|
||||
&violations,
|
||||
)
|
||||
.emit();
|
||||
return Ty::new_error(tcx, reported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let principal_trait = regular_traits.into_iter().next();
|
||||
|
||||
let mut needed_associated_types = FxIndexSet::default();
|
||||
|
||||
let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1);
|
||||
let regular_traits_refs_spans = trait_bounds
|
||||
.into_iter()
|
||||
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
||||
|
||||
for (base_trait_ref, original_span) in regular_traits_refs_spans {
|
||||
let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx);
|
||||
if let Some((principal_trait, spans)) = &principal_trait {
|
||||
let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx);
|
||||
for ClauseWithSupertraitSpan { pred, supertrait_span } in
|
||||
traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)])
|
||||
.filter_only_self()
|
||||
traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(
|
||||
pred,
|
||||
*spans.last().unwrap(),
|
||||
)])
|
||||
.filter_only_self()
|
||||
{
|
||||
debug!("observing object predicate `{pred:?}`");
|
||||
|
||||
|
@ -179,7 +153,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// }
|
||||
// ```
|
||||
//
|
||||
// Here, the user could theoretically write `dyn MyTrait<Output = X>`,
|
||||
// Here, the user could theoretically write `dyn MyTrait<MyOutput = X>`,
|
||||
// but actually supporting that would "expand" to an infinitely-long type
|
||||
// `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
|
||||
//
|
||||
|
@ -188,12 +162,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// the discussion in #56288 for alternatives.
|
||||
if !references_self {
|
||||
// Include projections defined on supertraits.
|
||||
projection_bounds.push((pred, original_span));
|
||||
projection_bounds.push((pred, supertrait_span));
|
||||
}
|
||||
|
||||
self.check_elaborated_projection_mentions_input_lifetimes(
|
||||
pred,
|
||||
original_span,
|
||||
*spans.first().unwrap(),
|
||||
supertrait_span,
|
||||
);
|
||||
}
|
||||
|
@ -202,11 +176,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
}
|
||||
}
|
||||
|
||||
// `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, span) in &projection_bounds {
|
||||
// `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. `needed_associated_types` contains all associated
|
||||
// types that we expect to be provided by the user, so the following loop
|
||||
// removes all the associated types that have a corresponding `Projection`
|
||||
// clause, either from expanding trait aliases or written by the user.
|
||||
for &(projection_bound, span) in &projection_bounds {
|
||||
let def_id = projection_bound.item_def_id();
|
||||
let trait_ref = tcx.anonymize_bound_vars(
|
||||
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
|
||||
|
@ -216,17 +192,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
tcx.emit_node_span_lint(
|
||||
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
||||
hir_id,
|
||||
*span,
|
||||
crate::errors::UnusedAssociatedTypeBounds { span: *span },
|
||||
span,
|
||||
crate::errors::UnusedAssociatedTypeBounds { span },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(guar) = self.check_for_required_assoc_tys(
|
||||
principal_span,
|
||||
principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
|
||||
needed_associated_types,
|
||||
potential_assoc_types,
|
||||
hir_trait_bounds,
|
||||
hir_bounds,
|
||||
) {
|
||||
return Ty::new_error(tcx, guar);
|
||||
}
|
||||
|
@ -236,32 +212,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
// 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);
|
||||
auto_traits.retain(|(trait_pred, _)| duplicates.insert(trait_pred.def_id()));
|
||||
|
||||
debug!(?principal_trait);
|
||||
debug!(?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>| {
|
||||
let principal_trait_ref = principal_trait.map(|(trait_pred, spans)| {
|
||||
trait_pred.map_bound(|trait_pred| {
|
||||
let trait_ref = trait_pred.trait_ref;
|
||||
assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
|
||||
assert_eq!(trait_ref.self_ty(), dummy_self);
|
||||
|
||||
let span = *spans.first().unwrap();
|
||||
|
||||
// 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 args: Vec<_> = trait_ref
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(1) // Remove `Self` for `ExistentialPredicate`.
|
||||
// Skip `Self`
|
||||
.skip(1)
|
||||
.map(|(index, arg)| {
|
||||
if arg == dummy_self.into() {
|
||||
let param = &generics.own_params[index];
|
||||
missing_type_params.push(param.name);
|
||||
Ty::new_misc_error(tcx).into()
|
||||
} else if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
references_self = true;
|
||||
let guar = self.dcx().span_delayed_bug(
|
||||
span,
|
||||
"trait object trait bounds reference `Self`",
|
||||
|
@ -273,8 +253,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let span = i.bottom().1;
|
||||
let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
|
||||
let empty_generic_args = hir_bounds.iter().any(|hir_bound| {
|
||||
hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
|
||||
&& hir_bound.span.contains(span)
|
||||
});
|
||||
|
@ -285,26 +264,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
empty_generic_args,
|
||||
);
|
||||
|
||||
if references_self {
|
||||
let def_id = i.bottom().0.def_id();
|
||||
struct_span_code_err!(
|
||||
self.dcx(),
|
||||
i.bottom().1,
|
||||
E0038,
|
||||
"the {} `{}` cannot be made into an object",
|
||||
tcx.def_descr(def_id),
|
||||
tcx.item_name(def_id),
|
||||
)
|
||||
.with_note(
|
||||
rustc_middle::traits::DynCompatibilityViolation::SupertraitSelf(
|
||||
smallvec![],
|
||||
)
|
||||
.error_msg(),
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
ty::ExistentialTraitRef::new(tcx, trait_ref.def_id, args)
|
||||
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new(
|
||||
tcx,
|
||||
trait_ref.def_id,
|
||||
args,
|
||||
))
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -327,21 +291,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
b.projection_term = replace_dummy_self_with_error(tcx, b.projection_term, guar);
|
||||
}
|
||||
|
||||
ty::ExistentialProjection::erase_self_ty(tcx, b)
|
||||
ty::ExistentialPredicate::Projection(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()))
|
||||
let auto_trait_predicates = auto_traits.into_iter().map(|(trait_pred, _)| {
|
||||
assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive);
|
||||
assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self);
|
||||
|
||||
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.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)),
|
||||
)
|
||||
let mut v = principal_trait_ref
|
||||
.into_iter()
|
||||
.chain(existential_projections)
|
||||
.chain(auto_trait_predicates)
|
||||
.collect::<SmallVec<[_; 8]>>();
|
||||
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
|
||||
use rustc_middle::ty::{
|
||||
self, AdtDef, Binder, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeVisitableExt,
|
||||
self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt,
|
||||
suggest_constraining_type_param,
|
||||
};
|
||||
use rustc_session::parse::feature_err;
|
||||
|
@ -19,8 +19,9 @@ use rustc_span::edit_distance::find_best_match_for_name;
|
|||
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym};
|
||||
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
|
||||
use rustc_trait_selection::traits::{
|
||||
FulfillmentError, TraitAliasExpansionInfo, dyn_compatibility_violations_for_assoc_item,
|
||||
FulfillmentError, dyn_compatibility_violations_for_assoc_item,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::errors::{
|
||||
self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
|
||||
|
@ -720,7 +721,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
/// emit a generic note suggesting using a `where` clause to constraint instead.
|
||||
pub(crate) fn check_for_required_assoc_tys(
|
||||
&self,
|
||||
principal_span: Span,
|
||||
spans: SmallVec<[Span; 1]>,
|
||||
missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>,
|
||||
potential_assoc_types: Vec<usize>,
|
||||
trait_bounds: &[hir::PolyTraitRef<'_>],
|
||||
|
@ -729,6 +730,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let principal_span = *spans.first().unwrap();
|
||||
|
||||
let tcx = self.tcx();
|
||||
// FIXME: This logic needs some more care w.r.t handling of conflicts
|
||||
let missing_assoc_types: Vec<_> = missing_assoc_types
|
||||
|
@ -1124,29 +1127,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
|
||||
pub fn report_trait_object_addition_traits_error(
|
||||
&self,
|
||||
regular_traits: &Vec<TraitAliasExpansionInfo<'_>>,
|
||||
regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>,
|
||||
) -> ErrorGuaranteed {
|
||||
let first_trait = ®ular_traits[0];
|
||||
let additional_trait = ®ular_traits[1];
|
||||
// we use the last span to point at the traits themselves,
|
||||
// and all other preceding spans are trait alias expansions.
|
||||
let (&first_span, first_alias_spans) = regular_traits[0].1.split_last().unwrap();
|
||||
let (&second_span, second_alias_spans) = regular_traits[1].1.split_last().unwrap();
|
||||
let mut err = struct_span_code_err!(
|
||||
self.dcx(),
|
||||
additional_trait.bottom().1,
|
||||
*regular_traits[1].1.first().unwrap(),
|
||||
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.span_label(first_span, "first non-auto trait");
|
||||
for &alias_span in first_alias_spans {
|
||||
err.span_label(alias_span, "first non-auto trait comes from this alias");
|
||||
}
|
||||
err.span_label(second_span, "additional non-auto trait");
|
||||
for &alias_span in second_alias_spans {
|
||||
err.span_label(alias_span, "second non-auto trait comes from this alias");
|
||||
}
|
||||
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()
|
||||
// FIXME: This should `print_sugared`, but also needs to integrate projection bounds...
|
||||
.map(|t| t.trait_ref().print_only_trait_path().to_string())
|
||||
.map(|(pred, _)| pred
|
||||
.map_bound(|pred| pred.trait_ref)
|
||||
.print_only_trait_path()
|
||||
.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" + "),
|
||||
));
|
||||
|
@ -1161,14 +1171,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
|||
pub fn report_trait_object_with_no_traits_error(
|
||||
&self,
|
||||
span: Span,
|
||||
trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>,
|
||||
user_written_clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
|
||||
) -> ErrorGuaranteed {
|
||||
let tcx = self.tcx();
|
||||
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 trait_alias_span = user_written_clauses
|
||||
.into_iter()
|
||||
.filter_map(|(clause, _)| clause.as_trait_clause())
|
||||
.find(|trait_ref| tcx.is_trait_alias(trait_ref.def_id()))
|
||||
.map(|trait_ref| tcx.def_span(trait_ref.def_id()));
|
||||
|
||||
self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span })
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue