Skip to content

Commit

Permalink
Auto merge of rust-lang#136458 - compiler-errors:fix-3, r=<try>
Browse files Browse the repository at this point in the history
Do not deduplicate list of associated types provided by dyn principal

r? `@ghost`
  • Loading branch information
bors committed Feb 3, 2025
2 parents 613bdd4 + a55ea23 commit 654bc80
Show file tree
Hide file tree
Showing 16 changed files with 302 additions and 62 deletions.
124 changes: 96 additions & 28 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
Expand Down Expand Up @@ -59,9 +59,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

let (trait_bounds, mut projection_bounds) =
let (elaborated_trait_bounds, elaborated_projection_bounds) =
traits::expand_trait_aliases(tcx, user_written_bounds.clauses());
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
.into_iter()
.partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));

Expand Down Expand Up @@ -102,38 +102,87 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

// Map the projection bounds onto a key that makes it easy to remove redundant
// bounds that are constrained by supertraits of the principal def id.
//
// Also make sure we detect conflicting bounds from expanding a trait alias and
// also specifying it manually, like:
// ```
// type Alias = Trait<Assoc = i32>;
// let _: &dyn Alias<Assoc = u32> = /* ... */;
// ```
let mut projection_bounds = FxIndexMap::default();
for (proj, proj_span) in elaborated_projection_bounds {
let key = (
proj.skip_binder().projection_term.def_id,
tcx.anonymize_bound_vars(
proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
),
);
if let Some((old_proj, old_proj_span)) =
projection_bounds.insert(key, (proj, proj_span))
&& tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj)
{
let item = tcx.item_name(proj.item_def_id());
self.dcx()
.struct_span_err(
span,
format!(
"conflicting associated type bounds for `{item}` when \
expanding trait alias"
),
)
.with_span_label(
old_proj_span,
format!("`{item}` is specified to be `{}` here", old_proj.term()),
)
.with_span_label(
proj_span,
format!("`{item}` is specified to be `{}` here", proj.term()),
)
.emit();
}
}

let principal_trait = regular_traits.into_iter().next();

let mut needed_associated_types = FxIndexSet::default();
if let Some((principal_trait, spans)) = &principal_trait {
let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx);
for ClauseWithSupertraitSpan { pred, supertrait_span } in
let mut needed_associated_types = vec![];
if let Some((principal_trait, ref spans)) = principal_trait {
let principal_trait = principal_trait.map_bound(|trait_pred| {
assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
trait_pred.trait_ref
});

for ClauseWithSupertraitSpan { clause, supertrait_span } in
traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(
pred,
ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx),
*spans.last().unwrap(),
)])
.filter_only_self()
{
debug!("observing object predicate `{pred:?}`");
let clause = clause.instantiate_supertrait(tcx, principal_trait);
debug!("observing object predicate `{clause:?}`");

let bound_predicate = pred.kind();
let bound_predicate = clause.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
ty::ClauseKind::Trait(pred) => {
// FIXME(negative_bounds): Handle this correctly...
let trait_ref =
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
needed_associated_types.extend(
tcx.associated_items(trait_ref.def_id())
tcx.associated_items(pred.trait_ref.def_id)
.in_definition_order()
// We only care about associated types.
.filter(|item| item.kind == ty::AssocKind::Type)
// No RPITITs -- even with `async_fn_in_dyn_trait`, they are implicit.
.filter(|item| !item.is_impl_trait_in_trait())
// If the associated type has a `where Self: Sized` bound,
// we do not need to constrain the associated type.
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
.map(|item| (item.def_id, trait_ref)),
);
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
ty::ClauseKind::Projection(pred) => {
let pred = bound_predicate.rebind(pred);
// A `Self` within the original bound will be instantiated with a
// `trait_object_dummy_self`, so check for that.
Expand Down Expand Up @@ -161,8 +210,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// `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, supertrait_span));
let key = (
pred.skip_binder().projection_term.def_id,
tcx.anonymize_bound_vars(
pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
),
);
if !projection_bounds.contains_key(&key) {
projection_bounds.insert(key, (pred, supertrait_span));
}
}

self.check_elaborated_projection_mentions_input_lifetimes(
Expand All @@ -182,12 +238,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// 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 {
for &(projection_bound, span) in projection_bounds.values() {
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)),
);
needed_associated_types.swap_remove(&(def_id, trait_ref));
if tcx.generics_require_sized_self(def_id) {
tcx.emit_node_span_lint(
UNUSED_ASSOCIATED_TYPE_BOUNDS,
Expand All @@ -198,9 +250,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

let mut missing_assoc_types = FxIndexSet::default();
let projection_bounds: Vec<_> = needed_associated_types
.into_iter()
.filter_map(|key| {
if let Some(assoc) = projection_bounds.get(&key) {
Some(*assoc)
} else {
missing_assoc_types.insert(key);
None
}
})
.collect();

if let Err(guar) = self.check_for_required_assoc_tys(
principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
needed_associated_types,
missing_assoc_types,
potential_assoc_types,
hir_bounds,
) {
Expand Down Expand Up @@ -266,7 +331,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
});

let existential_projections = projection_bounds.iter().map(|(bound, _)| {
let existential_projections = projection_bounds.into_iter().map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_term.self_ty(), dummy_self);

Expand All @@ -291,12 +356,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
});

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);
let mut auto_trait_predicates: Vec<_> = 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()))
});
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id()))
})
.collect();
auto_trait_predicates.dedup();

// N.b. principal, projections, auto traits
// FIXME: This is actually wrong with multiple principals in regards to symbol mangling
Expand All @@ -306,7 +375,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.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, unless the bound is missing.
Expand Down
18 changes: 4 additions & 14 deletions compiler/rustc_middle/src/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,11 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
b: Self,
) -> RelateResult<'tcx, Self> {
let tcx = relation.cx();

// FIXME: this is wasteful, but want to do a perf run to see how slow it is.
// We need to perform this deduplication as we sometimes generate duplicate projections
// in `a`.
let mut a_v: Vec<_> = a.into_iter().collect();
let mut b_v: Vec<_> = b.into_iter().collect();
a_v.dedup();
b_v.dedup();
if a_v.len() != b_v.len() {
if a.len() != b.len() {
return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b)));
}

let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
match (ep_a.skip_binder(), ep_b.skip_binder()) {
let v =
iter::zip(a, b).map(|(ep_a, ep_b)| match (ep_a.skip_binder(), ep_b.skip_binder()) {
(ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
Expand All @@ -109,8 +100,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
ty::ExistentialPredicate::AutoTrait(b),
) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
_ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))),
}
});
});
tcx.mk_poly_existential_predicates_from_iter(v)
}
}
Expand Down
15 changes: 6 additions & 9 deletions compiler/rustc_type_ir/src/elaborate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,22 @@ pub trait Elaboratable<I: Interner> {
}

pub struct ClauseWithSupertraitSpan<I: Interner> {
pub pred: I::Predicate,
pub clause: I::Clause,
// Span of the supertrait predicatae that lead to this clause.
pub supertrait_span: I::Span,
}
impl<I: Interner> ClauseWithSupertraitSpan<I> {
pub fn new(pred: I::Predicate, span: I::Span) -> Self {
ClauseWithSupertraitSpan { pred, supertrait_span: span }
pub fn new(clause: I::Clause, span: I::Span) -> Self {
ClauseWithSupertraitSpan { clause, supertrait_span: span }
}
}
impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
fn predicate(&self) -> <I as Interner>::Predicate {
self.pred
self.clause.as_predicate()
}

fn child(&self, clause: <I as Interner>::Clause) -> Self {
ClauseWithSupertraitSpan {
pred: clause.as_predicate(),
supertrait_span: self.supertrait_span,
}
ClauseWithSupertraitSpan { clause, supertrait_span: self.supertrait_span }
}

fn child_with_derived_cause(
Expand All @@ -72,7 +69,7 @@ impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
_parent_trait_pred: crate::Binder<I, crate::TraitPredicate<I>>,
_index: usize,
) -> Self {
ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span }
ClauseWithSupertraitSpan { clause, supertrait_span }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ trait I32Iterator = Iterator<Item = i32>;

fn main() {
let _: &dyn I32Iterator<Item = u32> = &vec![42].into_iter();
//~^ ERROR expected `IntoIter<u32>` to be an iterator that yields `i32`, but it yields `u32`
//~^ ERROR conflicting associated type bounds
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
error[E0271]: expected `IntoIter<u32>` to be an iterator that yields `i32`, but it yields `u32`
--> $DIR/associated-types-overridden-binding-2.rs:6:43
error: conflicting associated type bounds for `Item` when expanding trait alias
--> $DIR/associated-types-overridden-binding-2.rs:6:13
|
LL | trait I32Iterator = Iterator<Item = i32>;
| ---------- `Item` is specified to be `i32` here
...
LL | let _: &dyn I32Iterator<Item = u32> = &vec![42].into_iter();
| ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
|
= note: required for the cast from `&std::vec::IntoIter<u32>` to `&dyn Iterator<Item = u32, Item = i32>`
| ^^^^^^^^^^^^^^^^----------^
| |
| `Item` is specified to be `u32` here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0271`.
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ trait U32Iterator = I32Iterator<Item = u32>; //~ ERROR type annotations needed

fn main() {
let _: &dyn I32Iterator<Item = u32>;
//~^ ERROR conflicting associated type bounds
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ note: required by a bound in `I32Iterator`
LL | trait I32Iterator = Iterator<Item = i32>;
| ^^^^^^^^^^ required by this bound in `I32Iterator`

error: aborting due to 2 previous errors
error: conflicting associated type bounds for `Item` when expanding trait alias
--> $DIR/associated-types-overridden-binding.rs:10:13
|
LL | trait I32Iterator = Iterator<Item = i32>;
| ---------- `Item` is specified to be `i32` here
...
LL | let _: &dyn I32Iterator<Item = u32>;
| ^^^^^^^^^^^^^^^^----------^
| |
| `Item` is specified to be `u32` here

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0284`.
15 changes: 15 additions & 0 deletions tests/ui/closures/deduce-from-object-supertrait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@ check-pass

trait Foo: Fn(Bar) {}
impl<T> Foo for T where T: Fn(Bar) {}

struct Bar;
impl Bar {
fn bar(&self) {}
}

fn main() {
let x: &dyn Foo = &|x| {
x.bar();
};
}
18 changes: 18 additions & 0 deletions tests/ui/dyn-compatibility/multiple-supers-should-work.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//@ check-pass

trait Sup<T> {
type Assoc;
}

impl<T> Sup<T> for () {
type Assoc = T;
}

trait Trait<A, B>: Sup<A, Assoc = A> + Sup<B, Assoc = B> {}

impl<T, U> Trait<T, U> for () {}

fn main() {
let x: &dyn Trait<(), _> = &();
let y: &dyn Trait<_, ()> = x;
}
Loading

0 comments on commit 654bc80

Please sign in to comment.