Skip to content

Commit

Permalink
Rework trait expansion to happen once explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Dec 4, 2024
1 parent 5469673 commit 8e324b4
Show file tree
Hide file tree
Showing 25 changed files with 551 additions and 634 deletions.
191 changes: 79 additions & 112 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 = 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;
}
Expand All @@ -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");
// Check that there are no gross dyn-compatibility violations;
// most importantly, that the supertraits don't contain `Self`,
// to avoid ICEs.
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);
}
}
}

// 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()));
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);
}
// We don't support >1 principal
if regular_traits.len() > 1 {
let guar = self.report_trait_object_addition_traits_error(&regular_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 &regular_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);
}
}
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:?}`");

Expand Down Expand Up @@ -186,12 +160,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,
);
}
Expand All @@ -204,24 +178,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// 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 {
for &(projection_bound, span) in &projection_bounds {
let def_id = projection_bound.projection_def_id();
needed_associated_types.swap_remove(&def_id);
if tcx.generics_require_sized_self(def_id) {
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);
}
Expand All @@ -231,15 +205,20 @@ 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![];
Expand All @@ -249,7 +228,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.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];
Expand All @@ -268,8 +248,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)
});
Expand All @@ -280,26 +259,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,
))
})
});

Expand All @@ -322,21 +286,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()));
Expand Down
Loading

0 comments on commit 8e324b4

Please sign in to comment.